《webpack实战-入门、进阶和调优》读后总结
webpack打包原理
(function(modules) {
// 模块缓存
var installedModules = {};
// 实现require
function __webpack_require__(moduleId) {
...
}
// 执行入口模块的加载
return __webpack_require__(__webpack_require__.s = 0);
})({
// modules: 以key-value的形式储存所有被打包的模块
0: function(module, exports, __webpack_require__) {
// 打包入口
module.exports = __webpack_require__("3qiv")
},
"3qiv": function(module, exports, __webpack_require__) {
// index.js 内容
}
})
- 最外层立即执行匿名函数。包裹bundle,构成自身作用域
-
installedModules
对象。每个模块只在第一次被加载的时候执行,导出值存储到这个对象里面,当再次被加载的时候就从中取值,不会重新执行。 -
__webpack_require__
函数。对模块加载的实现。 -
modules
对象。工程中所有产生了依赖关系的模块都会以key-value形式放在这里。 - bundle运行时,先加载入口模块,再依次执行模块代码,解析模块依赖。
entry
webpack通过context和entry两个配置项共同决定入口文件路径。
enrty的配置支持字符串、数组、对象、函数形式。
- 数组类型入口:将多个资源预先合并,数组的最后一个元素是实际的入口路径。
- 对象类型入口:key是chunk name,value是入口路径。
- 函数类型入口:支持返回Promise对象来进行异步操作
此处可以应用优化
optimization.splitChunks
,提取公共模块。可以利用客户端缓存,加快页面渲染速度。
output
- filename
输出资源文件名,支持相对路径,支持模板语法动态生成文件名。
变量名称 | 描述 |
---|---|
[name] | 指代chunk name |
[hash] | 指代webpack此次打包所有资源生成的hash |
[chunkhash] | 指代当前chunk内容的hash |
[id] | 指代当前chunk的id |
[query] | 指代filename配置项中的query |
- path
指定资源输出的位置,必须为绝对路径。 - publicPath
指代资源的请求位置,请求url相对路径的前缀。
loader
webpack一切皆模块,loader帮助webpack识别js、json之外的文件,从而维护模块之间的关系。
output = loader(input)
loader 配置
- 引入loader
module.exports = {
// ...
module: {
rules: [{
test: /\.css$/,
use: ['css-loader'],
}]
}
}
test 接受正则表达式/正则表达式数组,只有匹配的模块才使用该规则;
use 接受一个数组,将从后向前的调用数组中定义的loader。
- exclude/include
exclude优先级高于include
此处配置
exclude: /node_modules/
可以提高不必要的loader执行,提高编译效率
- enforce
接受pre
post
两种字符串类型值,强制指定loader的执行顺序
自定义loader
- 启用缓存:使用
this.cacheable
控制 - 获取options:引入
const loaderUtils = require("loader-utils");
,通过loaderUtils.getOptions(this)
获取配置对象
代码分片
代码分片和公共模块提取
通过入口划分代码,适合于接口绑定在全局对象上的库,和多页面应用。
SplitChunksPlugin
将多个chunk中公共的部分提取出来
资源异步加载
模块数量过多,资源体积过大时,可以吧一些暂时使用不到的模块延迟加载。
原理:import().then()
require.ensure
配置:使用特有注释对chunk命名
import(/* webpackChunkName: "bar" */ './bar.js').then(({add}) => {
// ...
})
生产环境配置
开启production模式、环境变量配置
module.exports = {
mode: 'production',
plugins: [
new webpack.DefinePlugin({
ENV: 'production'
})
]
}
source map
source map指的是将编译、打包、压缩后的代码映射回源代码的过程。帮助追查线上问题。
devtool: 'source-map'
安全问题
source map会暴露源码,但是没有map文件无法调试代码,解决方案如下:
hidden-source-map: 编译产出完整map文件,但是不会引用。利用第三方工具(Sentry),手动上传map文件;
nisources-source-map:隐藏文件具体内容,但是可以查看错误栈和日志的准确行数;
nginx对白名单开放.map文件访问权限。
资源压缩
配置项: config.optimization.minimize,开启了mode: production
后不需要人为设置。
自定义压缩插件 terser-webpack-plugin
module.exports = {
optimization: {
// 覆盖默认的 minimizer
minimizer: [
new TerserPlugin({
test: /\.js(\?.*)?$/i, // 作用范围
exclude: /\/exculdes/,
cache: true, // 是否开启缓存,传入字符串可以指定缓存目录
parallel: 2, // 允许多进程压缩
sourceMap: true // 生成 source map,需要同事配置 devtool
})
]
}
}
缓存
output.filename: 'bundle@[chunkhash].js'
通常使用chunkhash作为文件版本号,使用 html-webpack-plugin
输出动态HTML,自动更新最新的资源名到html中。
打包优化
缩小打包作用域
动态链接库和 DllPlugin
DllPlugin和Code Splitting有点类似,都可以用来提取公共模块。Code Splitting思路是设置一些特定的规则并在打包过程中根据这些规则提取模块,Dlllpugin则是将vendor完全拆出,独立打包,实际工程构建是就不用对它再做处理,直接取用。