Webpack 是一个打包模块化 JavaScript 的工具,在 Webpack 里一切文件皆模块,通过 Loader 转换文件,通过 Plugin 注入钩子,最后输出由多个模块组合成的文件。Webpack 专注于构建模块化项目。
Webpack的优点是:
专注于处理模块化的项目,能做到开箱即用一步到位;
通过 Plugin 扩展,完整好用又不失灵活;
使用场景不仅限于 Web 开发;
社区庞大活跃,经常引入紧跟时代发展的新特性,能为大多数场景找到已有的开源扩展;
良好的开发体验。
Webpack的缺点是只能用于采用模块化开发的项目。
Webpack 是一个打包模块化 JavaScript 的工具,它会从 main.js 出发,识别出源码中的模块化导入语句, 递归的寻找出入口文件的所有依赖,把入口和其所有依赖打包到一个单独的文件中。 从 Webpack2 开始,已经内置了对 ES6、CommonJS、AMD 模块化语句的支持。
1.核心概念
Webpack 有以下几个核心概念。
Entry:入口,Webpack 执行构建的第一步将从 Entry 开始,可抽象成输入。
Module:模块,在 Webpack 里一切皆模块,一个模块对应着一个文件。Webpack 会从配置的 Entry 开始递归找出所有依赖的模块。
Chunk:代码块,一个 Chunk 由多个模块组合而成,用于代码合并与分割。
Loader:模块转换器,用于把模块原内容按照需求转换成新内容。
Plugin:扩展插件,在 Webpack 构建流程中的特定时机注入扩展逻辑来改变构建结果或做你想要的事情。
Output:输出结果,在 Webpack 经过一系列处理并得出最终想要的代码后输出结果。
Webpack 启动后会从 Entry 里配置的 Module 开始递归解析 Entry 依赖的所有 Module。 每找到一个 Module, 就会根据配置的 Loader 去找出对应的转换规则,对 Module 进行转换后,再解析出当前 Module 依赖的 Module。 这些模块会以 Entry 为单位进行分组,一个 Entry 和其所有依赖的 Module 被分到一个组也就是一个 Chunk。最后 Webpack 会把所有 Chunk 转换成文件输出。 在整个流程中 Webpack 会在恰当的时机执行 Plugin 里定义的逻辑。
1-1 Entry
entry是配置模块的入口,可抽象成输入,Webpack 执行构建的第一步将从入口开始搜寻及递归解析出所有入口依赖的模块。
entry 配置是必填的,若不填则将导致 Webpack 报错退出。
Entry 类型
Entry 类型可以是以下三种中的一种或者相互组合:
string './app/entry' 入口模块的文件路径,可以是相对路径。
array ['./app/entry1', './app/entry2'] 入口模块的文件路径,可以是相对路径。
object { a: './app/entry-a', b: ['./app/entry-b1', './app/entry-b2']} 配置多个入口,每个入口生成一个 Chunk
如果是 array 类型,则搭配 output.library 配置项使用时,只有数组里的最后一个入口文件的模块会被导出。
1-2 Module
在 webpack 里一切皆模块,一个模块对应着一个文件,module 配置如何处理模块。
配置 Loader
rules 配置模块的读取和解析规则,通常用来配置 Loader。其类型是一个数组,数组里每一项都描述了如何去处理部分文件。 配置一项 rules 时大致通过以下方式:
条件匹配:通过 test 、 include 、 exclude 三个配置项来命中 Loader 要应用规则的文件。
应用规则:对选中后的文件通过 use 配置项来应用 Loader,可以只应用一个 Loader 或者按照从后往前的顺序应用一组 Loader,同时还可以分别给 Loader 传入参数。
重置顺序:一组 Loader 的执行顺序默认是从右到左执行,通过 enforce 选项可以让其中一个 Loader 的执行顺序放到最前或者最后。
noParse
noParse 配置项可以让 Webpack 忽略对部分没采用模块化的文件的递归解析和处理,这样做的好处是能提高构建性能。 原因是一些库例如 jQuery 、ChartJS 它们庞大又没有采用模块化标准,让 Webpack 去解析这些文件耗时又没有意义。
noParse 是可选配置项,类型需要是 RegExp、[RegExp]、function 其中一个。
例如想要忽略掉 jQuery 、ChartJS,可以使用如下代码:
// 使用正则表达式 noParse: /jquery|chartjs/
// 使用函数,从 Webpack 3.0.0 开始支持
noParse: (content)=> {
// content 代表一个模块的文件路径
// 返回 true or false
return /jquery|chartjs/.test(content);
}
注意被忽略掉的文件里不应该包含 import 、 require 、 define 等模块化语句,不然会导致构建出的代码中包含无法在浏览器环境下执行的模块化语句。
parser
因为 Webpack 是以模块化的 JavaScript 文件为入口,所以内置了对模块化 JavaScript 的解析功能,支持 AMD、CommonJS、SystemJS、ES6。 parser 属性可以更细粒度的配置哪些模块语法要解析哪些不解析,和 noParse 配置项的区别在于 parser 可以精确到语法层面, 而 noParse 只能控制哪些文件不被解析。 parser 使用如下:
module: {
rules: [
{
test:/\.js$/,
use: ['babel-loader'],
parser: {
amd:false,// 禁用
requireJs:false,// 禁用 requirejs
}
},
]
}
1-3 Chunk
一个 Chunk 由多个模块组合而成,用于代码合并与分割。我们知道Webpack的打包是从一个入口文件开始,也可以说是入口模块,入口模块引用着其他模块,模块再引用模块。Webpack通过引用关系逐个打包模块,这些模块就组合成了一个Chunk。
如果我们有多个入口文件,可能会产出多条打包路径,一条路径就会形成一个Chunk。
一个Chunk是一些模块的封装单元。Chunk在构建完成就呈现为bundle。Chunk是过程中的代码块,Bundle是结果的代码块。
1-4 Loader
Webpack 把一切文件看作模块,CSS 文件也不例外,但是Webpack 不原生支持解析 CSS 文件。要支持非 JavaScript 类型的文件,需要使用 Webpack 的 Loader 机制。
Loader 可以看作具有文件转换功能的翻译员,配置里的 module.rules 数组配置了一组规则,告诉 Webpack 在遇到哪些文件时使用哪些 Loader 去加载和转换。 如上配置告诉 Webpack 在遇到以 .css 结尾的文件时先使用 css-loader 读取 CSS 文件,再交给 style-loader 把 CSS 内容注入到 JavaScript 里。
module: {
rules: [ {
// 用正则去匹配要用该 loader 转换的 CSS 文件
test:/\.css$/,
use: ['style-loader','css-loader?minimize'],
} ]
}
use 属性的值需要是一个由 Loader 名称组成的数组,Loader 的执行顺序是由后到前的;
每一个 Loader 都可以通过 URL querystring 的方式传入参数,例如 css-loader?minimize 中的 minimize 告诉 css-loader 要开启 CSS 压缩。
给 Loader 传入属性的方式除了有 querystring 外,还可以通过 Object 传入:
use: ['style-loader', { loader:'css-loader', options:{ minimize:true, } }]
1-5 Plugin
Plugin 用于扩展 Webpack 功能,通过在构建流程里注入钩子实现,它给 Webpack 带来了很大的灵活性,各种各样的 Plugin 几乎让 Webpack 可以做任何构建相关的事情。
配置 Plugin
Plugin 的配置很简单,plugins 配置项接受一个数组,数组里每一项都是一个要使用的 Plugin 的实例,Plugin 需要的参数通过构造函数传入。
const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
module.exports = {
plugins: [
// 所有页面都会用到的公共代码提取到 common 代码块中
newCommonsChunkPlugin({
name:'common',
chunks: ['a','b']
}),
]
};
使用 Plugin 的难点在于掌握 Plugin 本身提供的配置项,而不是如何在 Webpack 中接入 Plugin。
几乎所有 Webpack 无法直接实现的功能都能在社区找到开源的 Plugin 去解决,你需要善于使用搜索引擎去寻找解决问题的方法。
1-6 Output
output 配置如何输出最终想要的代码。output 是一个 object,里面包含一系列配置项,下面分别介绍它们。
filename
output.filename 配置输出文件的名称,为string 类型。 如果只有一个输出文件,则可以把它写成静态不变的:
filename:'bundle.js'
但是在有多个 Chunk 要输出时,就需要借助模版和变量了。前面说到 Webpack 会为每个 Chunk取一个名称,可以根据 Chunk 的名称来区分输出的文件名:
filename:'[name].js'
path
output.path 配置输出文件存放在本地的目录,必须是 string 类型的绝对路径。通常通过 Node.js 的 path 模块去获取绝对路径:
path: path.resolve(__dirname,'dist_[hash]')
publicPath
在复杂的项目里可能会有一些构建出的资源需要异步加载,加载这些异步资源需要对应的 URL 地址。
output.publicPath 配置发布到线上资源的 URL 前缀,为string 类型。 默认值是空字符串 '',即使用相对路径。
这样说可能有点抽象,举个例子,需要把构建出的资源文件上传到 CDN 服务上,以利于加快页面的打开速度。配置代码如下:
filename:'[name]_[chunkhash:8].js'
publicPath:'https://cdn.example.com/assets/'
这时发布到线上的 HTML 在引入 JavaScript 文件时就需要:
output.path 和 output.publicPath 都支持字符串模版,内置变量只有一个:hash 代表一次编译操作的 Hash 值。