减少模块解析
什么叫做模块解析
模块解析包括:抽象语法树分析、依赖分析、模块语法替换
不做模块解析会怎样
如果某个模块不做解析,该模块经过 loader 处理后的代码就是最终代码。
如果没有 loader 对该模块进行处理,该模块的源码就是最终打包结果的代码。
如果不对某个模块进行解析,可以缩短构建时间。
哪些模块不需要解析
模块中无其他依赖:一些已经打包好的第三方库,比如 jquery。
如何让某个模块不要解析
配置 module.noParse
,它是一个正则,被正则匹配到的模块不会解析。
优化 loader 性能
进一步限制 loader 的应用范围
对于某些库,不使用 loader,例如:babel-loader 可以转换 ES6 或更高版本的语法,可是有些库本身就是用 ES5 语法书写的,不需要转换,使用 babel-loader 反而会浪费构建时间。
lodash 就是这样的一个库,lodash 是在 ES5 之前出现的库,使用的是 ES3 语法。
通过 module.rule.exclude
或 module.rule.include
,排除或仅包含需要应用 loader 的场景
module.exports = {
module: {
rules: [
{
test: /\.js$/,
exclude: /lodash/,
use: "babel-loader"
}
]
}
}
如果暴力一点,甚至可以排除掉 node_modules
目录中的模块,或仅转换 src
目录的模块
module.exports = {
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
//或
// include: /src/,
use: "babel-loader"
}
]
}
}
这种做法是对 loader 的范围进行进一步的限制,和 noParse 不冲突,想想看,为什么不冲突
缓存 loader 的结果
我们可以基于一种假设:如果某个文件内容不变,经过相同的 loader 解析后,解析后的结果也不变,于是,可以将 loader 的解析结果保存下来,让后续的解析直接使用保存的结果。
cache-loader
可以实现这样的功能。
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: ['cache-loader', ...loaders]
},
],
},
};
有趣的是,cache-loader
放到最前面,却能够决定后续的 loader 是否运行,实际上,loader 的运行过程中,还包含一个过程,即 pitch
。
cache-loader
还可以实现各自自定义的配置,具体方式见文档。
在 webpack5 中自带缓存功能。
为 loader 的运行开启多线程
thread-loader
会开启一个线程池,线程池中包含适量的线程,它会把后续的loader放到线程池的线程中运行,以提高构建效率,由于后续的 loader 会放到新的线程中,所以,后续的 loader 不能:
- 使用 webpack api 生成文件
- 无法使用自定义的 plugin api
- 无法访问 webpack options
在实际的开发中,可以进行测试,来决定
thread-loader
放到什么位置
特别注意,开启和管理线程需要消耗时间,在小型项目中使用thread-loader
反而会增加构建时间。
热替换 HMR
热替换并不能降低构建时间(可能还会稍微增加),但可以降低代码改动到效果呈现的时间
当使用 webpack-dev-server
时,考虑代码改动到效果呈现的过程。
而使用了热替换后,流程发生了变化。
启用 HMR
此功能可以很大程度提高生产效率。我们要做的就是更新 webpack-dev-server
配置, 然后使用 webpack 内置的 HMR 插件。
从 webpack-dev-server
v4.0.0 开始,模块热替换是默认开启的。
webpack.config.js
const path = require("path")
const HtmlWebpackPlugin = require("html-webpack-plugin")
module.exports = {
context: path.resolve(__dirname, "src"),
entry: {
index: "./index",
},
devtool: "source-map",
devServer: {
hot: true,
},
plugins: [
new HtmlWebpackPlugin({
template: "index.html",
title: "webpack",
}),
],
}
默认情况下,webpack-dev-server
不管是否开启了热更新,更改代码,重新打包后,都会调用 location.reload
刷新页面。
index.js
if (module.hot) {
module.hot.accept() // 接受热更新
}
当开启了热更新后,webpack-dev-server
会向打包结果中注入 module.hot
属性,如果运行了 module.hot.accept()
,将改变自动刷新页面的行为。更多关于 HMR API 的信息,请查看文档。
module.hot.accept()
的作用是让 webpack-dev-server
通过 socket
管道,把服务器更新的内容发送到浏览器
然后,将结果交给插件 HotModuleReplacementPlugin
注入的代码执行,插件 HotModuleReplacementPlugin
会根据覆盖原始代码,然后让代码重新执行。
所以,热替换发生在代码运行期。
样式热替换
对于样式也是可以使用热替换的,但需要使用 style-loader
,因为热替换发生时,HotModuleReplacementPlugin
只会简单的重新运行模块代码,因此 style-loader
的代码一运行,就会重新设置 style
元素中的样式。
而 mini-css-extract-plugin
,由于它生成文件是在构建期间,运行期间并会也无法改动文件,因此它对于热替换是无效的。
热替换发生时,HotModuleReplacementPlugin
只会简单的重新运行模块代码,因此 style-loader
的代码一运行,就会重新设置 style
元素中的样式。
而 mini-css-extract-plugin
,由于它生成文件是在构建期间,运行期间并会也无法改动文件,因此它对于热替换是无效的。