优化打包速度
通过 speed-measure-webpack-plugin 测量你的 webpack 构建期间各个阶段花费的时间。
1. 模块按需编译
参考链接:zhuanlan.zhihu.com/p/137120584
2. 缩小文件的搜索范围(配置include exclude alias noParse extensions)
- resolve.alias: 但我们代码中出现
import * from 'vue'时,webpack会采用向上递归搜索的方式去node_modules目录下找。为了减少搜索范围我们可以直接告诉webpack去哪个路径下查找。也就是别名(alias)的配置。 - include exclude:可以减少webpack loader的搜索转换时间。
- noParse:当我们代码中使用到了
import $ from jquery时,webpack会去解析jquery这个库是否有依赖其他的包。但是我们对类似jquery这类依赖包,一般会认为不会引用其他的包(特殊除外,自行判断)。增加noParse属性,告诉webpack不必解析,以此增加打包的速度。 - resolve.extensions:webpack会根据extensions定义的后缀查找文件(频率较高的文件类型优先写在前面)。
3. 合理配置mode参数与devtool参数
mode可以设置development production两个参数
如果没有设置,webpack4会将mode的默认值设置为production
production模式下会进行tree shaking(去除无用代码)和uglifyjs(代码压缩混淆)
4. 缓存编译结果
我们每次执行苟安都会把所有的文件都重复一遍,这样的重复工作是否可以被缓存下来呢?答案是可以的,目前大部分的loader都提供了cache配置项。比如在babel-loader中,可以通过设置cacheDirectory来开启缓存,babel-loader?cacheDirectory=true就会将每次的编译结果写进硬盘文件(默认是在项目根目录下的node_modules/.cache/babel-loader目录中,当然你也可以自定义)
但如果loader不支持缓存呢?我们也有办法,我们可以通过cache-loader,他所做的事件很简单,就是babel-loader开启cache后做的事情,将loader的编译结果写入硬盘缓存,再次构建会先比较一下,如果文件较之前的没有发生变化则会直接使用缓存。
5. 抽离第三方模块
DllPlugin和DllReferencePlugin用某种方法实现了拆分bundles,同时还大幅度提升了构建的速度。
6. 优化解析时间——开启多线程打包
运行在Node.js之上的webpack是单线程模式的,也就是说,webpack打包只能串行处理文件,当webpack需要打包大量文件时,打包时间就会比较漫长。
- thread-loader (webpack4官方推荐) 把这个loader放置在其他loader之前,放置在这个loader之后的loader就会在一个单独的worker池里运行,一个worker就是一个nodejs进程, 每个单独进程的处理时间上线为600ms, 各个进程的数据交换也会限制在这个时间内。
module.exports = {
// ...
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
// 创建一个 js worker 池
use: [
'thread-loader',
'babel-loader'
]
},
{
test: /\.s?css$/,
exclude: /node_modules/,
// 创建一个 css worker 池
use: [
'style-loader',
'thread-loader',
{
loader: 'css-loader',
options: {
modules: true,
localIdentName: '[name]__[local]--[hash:base64:5]',
importLoaders: 1
}
},
'postcss-loader'
]
}
// ...
]
// ...
}
// ...
}
注意:thread-loader 放在了 style-loader 之后,这是因为 thread-loader 后的 loader 没法存取文件也没法获取 webpack 的选项设置。
官方上说每个 worker 大概都要花费 600ms ,所以官方为了防止启动 worker 时的高延迟,提供了对 worker 池的优化:预热
// ...
const threadLoader = require('thread-loader');
const jsWorkerPool = {
// options
// 产生的 worker 的数量,默认是 (cpu 核心数 - 1)
// 当 require('os').cpus() 是 undefined 时,则为 1
workers: 2,
// 闲置时定时删除 worker 进程
// 默认为 500ms
// 可以设置为无穷大, 这样在监视模式(--watch)下可以保持 worker 持续存在
poolTimeout: 2000
};
const cssWorkerPool = {
// 一个 worker 进程中并行执行工作的数量
// 默认为 20
workerParallelJobs: 2,
poolTimeout: 2000
};
threadLoader.warmup(jsWorkerPool, ['babel-loader']);
threadLoader.warmup(cssWorkerPool, ['css-loader', 'postcss-loader']);
module.exports = {
// ...
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: [
{
loader: 'thread-loader',
options: jsWorkerPool
},
'babel-loader'
]
},
{
test: /\.s?css$/,
exclude: /node_modules/,
use: [
'style-loader',
{
loader: 'thread-loader',
options: cssWorkerPool
},
{
loader: 'css-loader',
options: {
modules: true,
localIdentName: '[name]__[local]--[hash:base64:5]',
importLoaders: 1
}
},
'postcss-loader'
]
}
// ...
]
// ...
}
// ...
}
- HappyPack已不再继续维护,不推荐使用
7. 优化压缩时间-开启多进程压缩
webpack4中webpack.optimize.UglifyJsPlugin已被废弃
也不推荐使用ParallelUglifyPlugin,项目基本处于没人维护的阶段
webpack4默认内置使用terser-webpack-plugin插件压缩优化代码,而该插件使用terser来缩小JavaScript。
terser启动多进程
使用多进程并行运行来提高构建速度,并发运行的默认数量为os.cpus.length - 1。
module.exports = {
optimization: {
minimizer: [
new TerserPlugin({
parallel: true,
}),
],
},
};
- 使用高本版的wepack、loader、plugin
打包体积优化
代码分离
把代码分离到不同的bundle中,然后可以按需加载或并行加载这些文件。代码分离可以用于获取更小的hundle,以及控制资源加载优先级,如果使用合理,会极大影响加载时间。 常用的代码分离方法有三种:
- 入口起点:使用entry配置手动的分离代码
- 防止重复:使用SplitChunksPlugin去重和分类chunk
- 动态导入:通过模块的内联函数调用来分离代码