您现在的位置是:首页 > 文章详情

webpack 从入门到放弃(一)

日期:2018-07-10点击:324


什么是 webpack,为什么要使用 webpack

什么是 webpack

官网给出的概念是:

本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。 根据官网最直观最出名的那个图我们可以知道,webpack 可以打包/脚本/图片/样式/表 从图中我们可以看出左边有依赖关系的模块(MODULES WITH DEPENDENCIES)通过 webpack 打包成了各种静态资源(STATIC ASSETS)

为什么要使用 webpack

通过上面的概念,你是不是已经大概知道了 webpack 是干什么的,那么问题来了,为什么要使用 webpack 呢? 这就说来话长了,那就长话短说,emmmm

为什么使用 webpack,这应该是和前端的发展有关系的,我认为,webpack 是前端发展到一定阶段的必然产物(貌似是一句废话)。 因为计算机网络的飞速发展,导致 web 前端也在迅猛发展。最初的实践方案已经不能满足我们的需求,于是,越来越多的新技术新思想新框架孕育而生,比如:

前端模块化

随着前端项目的复杂度越来越高,相互之前的依赖越来越多,以及为了更好的复用代码,前端也需要用模块化的思想来组织代码。

首先我们要明白模块化解决了前端的哪些痛点:

  • 命名冲突
  • 文件依赖(js 加载顺序)
  • 代码复用

我们这里说的模块和 Java 的 package 的概念是类似的。逻辑上相关的代码放在一个包中,每一个包都是相互独立的,不用担心命名冲突的问题,如果其他人想要用这部分功能,直接 import 导入包就好

所以前端代码模块化的实现,会帮我们解决命名冲突和代码复用的问题,那么文件依赖要怎么处理呢?这就用到了我们的 webpack,稍后再做介绍。

所以有了模块,我们就可以方便的复用他人的代码,那么问题来了,无规矩不成方圆,我们在使用他人代码的时候肯定是要遵循某种规范,所以就出现了 CommonJS、AMD 和 终极模块化方案 —— ES6 模块,这些都是前端模块化的规范。

我们来简单了解一下:

CommonJS

node.js 采用的就是 CommonJS 规范,使用 require 的方法同步加载依赖,一个文件就是一个模块,导入导出格式如下:



// 导入 const moduleA = require('./moduleA'); // 导出 module.exports = moduleA.someFunc; 

缺点是加载的模块是同步的,只有加载完才能执行后面的操作。因为 node.js 的模块文件一般存在于本地硬盘,所以一般不会出现这个问题,但是在浏览器环境该规范就不那么适用了。

AMD

原因如上,因为 CommonJS 不适用于浏览器环境,所以出现了 AMD 规范。该规范异步加载依赖,可以再声明的时候指定需要加载的依赖,并且需要当做参数传入,对于依赖的模块提前执行,依赖前置。

写法如下:



define("module", ["dep1", "dep2"], function(d1, d2) { return someExportedValue; }); require(["module", "../file"], function(module, file) { /* ... */ }); 

ES6 模块化

ES6 直接在语言层面上实现了模块化。我们现在用的最多的就是 ES6 模块化的实践方式。

写法如下:


// 导入 import { readFile } from 'fs'; import React from 'react'; // 导出 export function hello() {}; export default { // ... }; 

样式模块化

现在越来越多人也开始使用模块化的思想写样式。比如现在大部分的 CSS 预编译器都支持 @import 的写法。将一些公用样式放在一个文件中,在其他文件中导入。






//全局安装 npm install -g webpack //安装到你的项目目录 npm install --save-dev webpack 




npm init 

初始化之后我们还要创建几个文件来存放我们的项目文件。

创建一个 app 文件夹来存放我们打包之前的源文件

创建一个 public 文件夹来存放一个入口文件 index.hrml 和通过 webpack 打包之后浏览器可直接运行的 js 文件

比如我们在 app 文件夹下创建一个 Greeter.js 文件,里面有一个方法可以再页面显示文字信息 Hi there and greetings!



module.exports = function() { var greet = document.createElement('div'); greet.textContent = "Hi there and greetings!"; return greet; }; 

然后我们创建一个 main.js 来引入 Greeter.js 这个文件,

浏览器的入口 index.html 文件内容如下(其中 bundle.js 是打包之后的文件):


<html lang="en"> <head> <meta charset="utf-8"> <title>Webpack Sample Project</title> </head> <body> <div id='root'> </div> <script src="bundle.js"></script> </body> </html> 


此时的文件路径如下

. ├── app │ ├── Greeter.js │ └── main.js ├── package.json ├── public │ ├── bundle.js │ └── index.html 

安装 webpack

初始化项目之后,要安装 webpack



npm install --save-dev webpack


在安装完 webpack 之后,可以再 package.json 文件中看到增加了 webpack 的依赖。

配置 webpack

安装完 webpack 之后,我们就要配置 webpack 了,首先创建配置文件 webpack.config.js 文件内容如下:



module.exports = { entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件 output: { path: __dirname + "/public",//打包后的文件存放的地方 filename: "bundle.js"//打包后输出文件的文件名 } } 

然后我们在该项目的终端输入

webpack 

就可以看到如下信息:


Hash: 4e6a6b5eb88a83b29e02 Version: webpack 4.12.0 Time: 551ms Built at: 2018-06-24 14:53:39 Asset Size Chunks Chunk Names bundle.js 6.82 KiB 0 [emitted] main [3] ./node_modules/css-loader!./app/main.css 190 bytes {0} [built] [4] ./app/main.css 1.04 KiB {0} [built] [5] ./app/Greeter.js 143 bytes {0} [built] [6] ./app/main.js 119 bytes {0} [built] + 3 hidden modules WARNING in configuration The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment. You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/concepts/mode/ 


看到这样的信息,那么恭喜你,你的第一个 webpack 项目就完成了!

打开 public 文件夹下面的 index.html,你就可以再浏览器上看到如下的效果。

Loader

loader 用于对模块的源代码进行转换。loader 可以使你在 import 或"加载"模块时预处理文件。因此,loader 类似于其他构建工具中“任务(task)”,并提供了处理前端构建步骤的强大方法。loader 可以将文件从不同的语言(如 TypeScript)转换为 JavaScript,或将内联图像转换为 data URL。loader 甚至允许你直接在 JavaScript 模块中 import CSS文件!

因为 webpack 本身只能处理 JavaScript,如果要处理其他类型的文件,就需要使用 loader 进行转换,loader 本身就是一个函数,接受源文件为参数,返回转换的结果。

举个 —— css-loader

例如,我们想在刚刚的页面增加样式,使文字居中显示,那么我在 app 文件夹下面新建一个 main.css 文件。在 webpack 中,所有的文件都是模块,所以要使用这个 css 文件,就必须要先引入。

引入 css 文件

所以我就在 app 文件夹下面的 main.js 中引入该 css 文件


const greeter = require('./Greeter.js'); require ('./main.css') document.querySelector("#root").appendChild(greeter()); 

重新打包

然后我重新打包一遍,蓝后,发现居然报错了!



Hash: 179c18498fac6de89a96 Version: webpack 4.12.0 Time: 533ms Built at: 2018-06-24 15:00:24 1 asset [0] ./app/Greeter.js 143 bytes {0} [built] [1] ./app/main.js 119 bytes {0} [built] WARNING in configuration The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment. You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/concepts/mode/ ERROR in ./app/main.js Module not found: Error: Can't resolve 'style-loader' in '/Users/cherry/Workspace/webpack-demo' @ ./app/main.js 2:0-22 


根据报错信息,我们很明显能发现是提示我们项目缺少 style-loader,这是因为 webpack 原生只支持解析 js 文件,要支持非 js 类型的文件,就需要使用 loader

安装 loader

所以我们要安装 style-loadercss-loader



npm i -D style-loader css-loader 


修改配置文件

然后修改 webpack 的配置文件 webpack.config.js


module.exports = { entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件 output: { path: __dirname + "/public",//打包后的文件存放的地方 filename: "bundle.js"//打包后输出文件的文件名 }, module: { rules: [ { test: /\.css$/, use: ['style-loader', 'css-loader'] } ] } } 

配置文件增加了 module.rules 数组,该数组是一些配置规则,告诉 webpack 符合 test 的文件需要使用 use 后面的 loader 处理。所以该规则就是对所有 .css 结尾的文件使用 style-loadercss-loader 进行处理。

loader 特性

我们来看一下 loader 有哪些特性:

  • loader 支持链式传递。能够对资源使用流水线(pipeline)。一组链式的 loader 将按照相反的顺序执行。loader 链中的第一个 loader 返回值给下一个 loader。在最后一个 loader,返回 webpack 所预期的 JavaScript。
  • loader 可以是同步的,也可以是异步的。
  • loader 运行在 Node.js 中,并且能够执行任何可能的操作。
  • loader 接收查询参数。用于对 loader 传递配置。
  • loader 也能够使用 options 对象进行配置。
  • 除了使用 package.json 常见的 main 属性,还可以将普通的 npm 模块导出为 loader,做法是在 package.json 里定义一个 loader 字段。
  • 插件(plugin)可以为 loader 带来更多特性。
  • loader 能够产生额外的任意文件。

使用 webpack 的三种姿势

webpack 中使用 loader 有三种姿势

通过 CLI

命令行中运行



webpack --module-bind jade --module-bind 'css=style!css' 

jade,style,css后面可省略-loader,他们分别对.jade使用jade-loader,对.css使用style-loader和css-loader

通过require

可以直接在源码中指定使用什么 loader 去处理文件。



require('style-loader!css-loader?minimize!./main.css') 

这样就是对 ./main.css 文件先使用 css-loader 再使用 style-loader 进行转换

使用配置文件 webpack.config.js

最常用的方式就是使用本文所使用的配置文件的方式

常见的 loader

常见的 loader

b57ff7f3702639e9afe31c502e2c2559926ffb46

将图片拷贝到相应的路径,再根据我们的配置,修改打包后文件引用路径,使之指向正确的文件。 gzip-loader | 可以加载 gzip 压缩之后的资源 html-loader | 将 html 输出为字符串,也可以根据配置进行压缩 imports-loader | imports-loader 允许使用依赖于特定全局变量的模块,这对于依赖于像 $ 这样的全局变量的第三方模块非常有用 jshint-loader | 为加载的模块使用 jshint json-loader | 由于 webpack >= v2.0.0 默认支持导入 JSON 文件。如果你使用自定义文件扩展名,你可能仍然需要使用此 loader json5-loader | 将 json5 文件解析成 js 对象 less-loader | 将 less 转化为 css null-loader | 返回一个空模块 postcss-loader | 将 postcss 转化为 css raw-loader | 加载文件原始内容(utf-8格式) sass-loader | 将 SASS/SCSS 转换为 css source-map-loader | 从现有源文件(源代码源URL)中提取源映射,方便调试 style-loader | 将 CSS 放在 <style> 标签中注入到 DOM 中 script-loader | 在全局上下文中执行一次 JavaScript 文件(如在 script 标签),不需要解析 svg-inline-loader | 将 SVG 作为模块嵌入 url-loader | 将文件加载为 Base64 编码的 URL

row 2 col 1 | row 2 col 2

Plugin

插件(plugin)是 webpack 的支柱功能。webpack 自身也是构建于,你在 webpack 配置中用到的相同的插件系统之上! 插件目的在于解决 loader 无法实现的其他事

Plugin 是用来扩展 Webpack 功能的,通过在构建流程里注入钩子实现,它给 Webpack 带来了很大的灵活性。

通过plugin(插件)webpack可以实 loader 所不能完成的复杂功能,使用 plugin 丰富的自定义 API 以及生命周期事件,可以控制 webpack 打包流程的每个环节,实现对 webpack 的自定义功能扩展。

举个 —— ExtractTextPlugin

webpack4 已经不支持用extract-text-webpack-plugin来优化 css, 需要改成optimize-css-assets-webpack-plugin和mini-css-extract-plugin

在刚刚的例子中,我们查看打包之后的 index.html 文件可以看到我们刚刚写的 css 代码放在了 head 中的 style 标签中,这是 style-loader 帮我们处理的

但是,如果你希望打包之后 css 在单独的文件中,那么你就需要 ExtractTextPlugin 这个 plugin 了。

安装 ExtractTextPlugin

npm i -D ExtractTextPlugin

修改配置文件

我们需要修改配置文件:



const path = require('path') const ExtractTextPlugin = require('extract-text-webpack-plugin') module.exports = { entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件 output: { path: __dirname + "/public",//打包后的文件存放的地方 filename: "bundle.js",//打包后输出文件的文件名 }, module: { rules: [ { test: /\.css$/, // 转换 .css 需要的 loader loaders: ExtractTextPlugin.extract({ use: ['css-loader'], }) } ] }, plugins: [ new ExtractTextPlugin({ filename: '[name]-[contenthash:8].css' }) ] } 

然后我们再重新打包,就可以发现在 public 文件夹下面多了一个 main-493a2c3c.css 文件,下面我们要在 index.html 中自动引入这个 css 文件

html-webpack-plugin

html-webpack-plugin 可以根据你设置的模板,在每次运行后生成对应的模板文件,同时所依赖的 CSS/JS 也都会被引入,如果 CSS/JS 中含有 hash 值,则 html-webpack-plugin 生成的模板文件也会引入正确版本的 CSS/JS 文件。

安装 html-webpack-plugin

安装方式我们已经很熟悉了


npm i -D html-webpack-plugin 

修改配置文件


const path = require('path') const ExtractTextPlugin = require('extract-text-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件 output: { path: __dirname + "/public",//打包后的文件存放的地方 filename: "bundle.js",//打包后输出文件的文件名 }, module: { rules: [ { test: /\.css$/, loaders: ExtractTextPlugin.extract({ use: ['css-loader'], }) } ] }, plugins: [ new ExtractTextPlugin({ filename: '[name]-[contenthash:8].css' }), new HtmlWebpackPlugin(), ] } 

蓝后我们删除之前我们在 punlic 文件夹下面 index.html 的内容,在打包一次,就会生成一个 html 模板
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Webpack App</title> <link href="main-493a2c3c.css" rel="stylesheet"></head> <body> <script type="text/javascript" src="bundle.js"></script></body> </html> 


new HtmlWebpackPlugin 的时候,我们可以进行一系列的配置


 new HtmlWebpackPlugin({ // 生成的HTML模板的title,如果模板中有设置title的名字,则会忽略这里的设置 title: "This is the webpack config", // 生成的模板文件的名字 filename: "/index.html", // 模板来源文件 template: "index.html", // 引入模块的注入位置;取值有true/false/body/head // true 默认值,script标签位于html文件的 body 底部 // body script标签位于html文件的 body 底部 // head script标签位于html文件的 head中 // false 不插入生成的js文件,这个几乎不会用到的 inject: "body", // 指定页面图标; favicon: "", // 是html-webpack-plugin中集成的 html-minifier ,生成模板文件压缩配置 minify: { caseSensitive: false, //是否大小写敏感 collapseBooleanAttributes: true, // 省略布尔属性的值 collapseWhitespace: true //删除空格 }, // 是否生成hash添加在引入文件地址的末尾,类似于我们常用的时间戳,避免缓存带来的麻烦 hash: true, // 是否需要缓存,如果填写true,则文件只有在改变时才会重新生成 cache: true, // 是否将错误信息写在页面里,默认true,出现错误信息则会包裹在一个pre标签内添加到页面上 showErrors: true, // 引入的模块,这里指定的是entry中设置多个js时,在这里指定引入的js,如果不设置则默认全部引入 chunks: "", // 引入模块的排序方式 // 默认四个选项: none auto dependency {function} // 'dependency' 文件的依赖关系 // 'auto' 默认值,插件的内置的排序方式 // 'none' 无序 // {function} 自定义 chunksSortMode: "auto", // 排除的模块 excludeChunks: "", // 生成的模板文档中标签是否自动关闭,针对xhtml的语法,会要求标签都关闭,默认false xhtml: false }), 

常见的 plugin

摘自:http://www.css88.com/doc/webpack/plugins/


29f0f64993d98c74ba22452778b6ccda526f2039
d86c806b02ec1fbc3f2fb865098c214acec5e3d7


原文发布时间为:2018年06月25日
原文作者: sunshine小小倩
本文来源: 掘金 如需转载请联系原作者


原文链接:https://yq.aliyun.com/articles/609849
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章