# 👉 Webpack 基础扫盲
# webpack 常规配置
# entry 和 output
entry 配置文件入口、output 配置文件出口
// 应用入口起点
// 单页应用(SPA):一个入口起点,多页应用(MPA):多个入口起点。
entry: {
main: './src/index.js'
},
// 文件出口
output: {
// 对应于 entry 里面的输入文件名称,配置输出文件的名称
// 如果只有一个输出文件可写成静态不变'bundle.js'
// 多个输出文件时,可借助模版/变量,[name] [chunkhash] [hash]
filename: 'js/[name].js',
// 一般指未被列在 entry 中,却又需要被打包出来的 chunk 文件的名称
// 若未指定,默认会把filename的 [name] 替换为 chunk 文件的 id 号
// 常见的会在运行时生成 Chunk 场景有在使用 CommonChunkPlugin、使用 import('path/to/module') 动态加载等时。
chunkFilename: 'js/[name].chunk.js',
// 配置输出文件存放目录,必须是 string 类型的绝对路径。通常通过 Node.js 的 path 模块去获取绝对路径
path: path.resolve(__dirname, 'dist_[hash]'),
// 构建出的资源需要异步加载,加载这些异步资源需要对应的 URL 地址。比如需要把构建出的资源文件上传到 CDN 服务上,以利于加快页面的打开速度
// filename:'[name]_[chunkhash:8].js'
// publicPath: 'https://cdn.example.com/assets/'
// 打包后的引入的路径:<script src='https://cdn.example.com/assets/a_12345678.js'></script>
publicPath: 'https://cdn.example.com/assets/"',
},
# output.fileName、output.chunckName
output.filename 是指在入口文件 entry 中引入生成出来的文件名,而 output.chunkname 是指那些未被在入口文件 entry 引入,但又通过按需加载(异步)模块的时候引入的文件。
# output.path、output.publicPath
output.path 是输出目录对应的绝对路径,output.publicPath 则是指打包出来的资源的 URL 前缀(这个配置项在生产模式和开发模式中都很重要,开发模式就是打包在内存中),即在浏览器中访问的路径的前缀。可以填写相对路径或者绝对路径。
# 文件指纹 hash、chunckhash、contenthash 区别
hash
hash 是基于整个 module identifier 序列计算得到的,没有任何改变的情况下,每次编译出来的 hash 都是一样的,但当你改变了任何一点东西,它的 hash 就会发生改变。简单理解,你改了任何东西,hash 就会和上次不一样了。chunkhash
和 Webpack 打包的 chunk 有关,不同的 entry 会生出不同的 chunkhash。chunkhash 根据具体每一个模块文件自己的的内容,包括它的依赖计算所得的 hash。contenthash
它的出现主要用于使 css 文件不受 js 文件的影响。比如 foo.css 被 foo.js 引用了,所以它们共用相同的 chunkhash 值。但这样子是有问题的,CSS 样式单独抽离生成文件(使用了 MiniCssExtractPlugin 插件),如果 foo.js 修改了代码,css 文件就算内容没有任何改变,由于是该模块的 hash 发生了改变,其 css 文件的 hash 也会随之改变。这个时候我们就可以使用 contenthash 了,保证即使 css 文件所处的模块里有任何内容的改变,只要 css 文件内容不变,那么它的 hash 就不会发生变化。
contenthash 是根据文件内容和 moduleId 来生成 hash 值。
moduleId 又是怎么来的?因为 module 是全局的,为了不同的入口文件引用同一个 module 时可以使用缓存而不必重复打包,因此采用模块排序方法进行计算,即按照解析顺序进行增量打包。eg:
module.id = require.resolve('./file.js')
。
# module、chunk、 bundle 分别是什么
module
对于同一份逻辑的代码,import/require 引入的资源(图片、代码脚本)就是 module,比如一个 index.js 一个 module,module 是 webpack 资源处理的基本单位。chunk
当我们写的 module 源文件传到 webpack 进行打包时,webpack 会根据模块间的引用关系生成 chunk 文件。每个 chunk 包可含多个 module。bundle
webpack 处理好 chunk 文件后,最后会输出 bundle 文件,这个 bundle 文件包含了经过加载和编译的最终源文件,所以它可以直接在浏览器中运行。
参考:
# loader
webpack 只认识 javaScript,对其他类型的文件不知道如何处理,比如图片,字体图标的模块,loader 相当于翻译官,对其他类型的资源进行转译的预处理。
loader 本质是一个函数,在该函数中对接收到的内容进行转译,然后返回转换后的结果。
webpack 支持 loader 的链式调用,即一个文件可以经多个 loader 处理。当一个文件使用多个 loader 处理时,他的处理顺序是倒序的,即传入 loader 数组的从右到左执行。
loader 在 module.rules 中配置,作为模块的解析规则,类型为数组。每一项都是一个 Object,内部包含了 test(匹配对应类型文件)、use(loader、options (参数))等属性。
const path = require('path');
module.exports = {
mode: 'production',
entry: {
main: './src/index.js'
},
output: {
filename: 'js/[name].js',
path: path.resolve(__dirname, 'dist'),
}
module: {
rules: [
{
test: /\.scss|\.css/,
use: [
// 把整合的css部分挂载到head标签中
'style-loader',
// 负责解析css代码, 处理css中的依赖,将多个css文件整合到一起
// 对 @import 和 url() 进行处理,就像 js 解析 import/require() 一样。
{
loader: 'css-loader',
// 解决多次引入问题
options: {
// 作用域当前的css环境中,样式不会全局引入
modules : true,
// 使用多少个后面的 loader 去处理 @import 进来的资源
// 0的时候没有,1的时候使用 sass-loader,2的时候使用 postcss-loader、sass-loader
importLoaders: 2,
},
},
// 将sass文件翻译成css文件
'sass-loader',
// 加上各浏览器厂商前缀
'postcss-loader',
],
},
]
},
}
# 常见 loader
- file-loader
- url-loader
- style-loader / css-loader / less-loader / sass-loader / postcss-loader
- eslint-loader
- vue-loader
- babel-loader
- cache-loade
# plugins
plugins 直译是插件,相当于扩展器,通过监听 webapck 运行生命周期中广播出来的事件,在适当的时机通过 webpack 提供的 api 去改变或者拓展输出的结果。
plugin 通过 pulgins 中单独进行配置,类型为数组,每一项是一个 plugin 的实例,参数都通过构造函数传入。
const path = require('path');
const htmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
filename: 'js/[name].js',
path: path.resolve(__dirname, 'dist'),
}
module: {
rules: [
{
test: /\.scss|\.css/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules : true,
importLoaders: 2,
},
},
'sass-loader',
'postcss-loader',
],
},
]
},
plugins: [
// 清除上一次打包好的文件
new CleanWebpackPlugin(),
// 生成一个HTML文件,然后将打包好的js文件自动引入到这个html文件中。
new htmlWebpackPlugin({
// 以具体哪个目录下的index.html为模板文件去打包
template: '../src/index.html',
// 输出名称
filename: 'index.html'
});
]
}
# 常用的 plugin
clean-webpack-plugin
:清除上一次打包好的文件html-webpack-plugin
:创建 html 模版来服务资源mini-css-extract-plugin
: 提取 css 到分离的文件,需要 webpack4
参考:
# webpack 构建流程
Webpack 的运行流程是一个串行的过程,从 webpack 开始构建到输出会依次执行以下流程:
(1)
初始化参数
:
从 webpack 配置文件以及 shell 命令中获取和合并配置参数,得出最终的参数结果;(2)
开始编译
:
用上一步的最终参数初始化 Complier 对象;并且加载所有配置的插件(方便让插件监听 webpack 生命周期广播出来的各事件节点并作出回应),执行 compiler 对象的 run 方法开始执行编译;
(3)
从入口出发编译模块
:
根据 entry 确定入口找出所有的入口文件,并从入口文件开始解析生成 AST 树,找出每个文件所依赖的文件,递归下去。在解析文件的递归过程中,会根据文件类型调用对应配置的 loader 进行文件转换翻译。递归本步骤,直到所有入口文件都经过处理找出依赖的模块。
(4)
输出资源
:
根据步骤 (3),每个模块都被翻译成最终内容以及它们之间的依赖关系。根据 entry 配置组装成一个个包含多个模块的代码块 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表。
根据 output 配置确定输出的路径和文件名,把文件内容写入到文件系统。
注意:在构建生命周期中有一系列插件在合适的时机做了合适的事情,比如 UglifyJsPlugin 会在 loader 转换递归完后对结果再使用 UglifyJs 压缩覆盖之前的结果。
参考: