webpack 5 优化
编译速度分析
通过 speed-measure-webpack-plugin 插件进行构建速度分析,可以看到各个 loader、plugin 的构建时长,后续可针对耗时 loader、plugin 进行优化。
安装:
npm i -D speed-measure-webpack-pluginwebpack.dev.js 配置方式如下:
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')
const smp = new SpeedMeasurePlugin()
module.exports = smp.wrap({
// ...webpack config...
})打包体积分析
使用 webpack-bundle-analyzer 查看打包后生成的 bundle 体积分析,将 bundle 内容展示为一个便捷的、交互式、可缩放的树状图形式。帮助我们分析输出结果来检查 模块在何处结束。
安装:
npm i -D webpack-bundle-analyzerwebpack.prod.js 配置方式如下:
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
module.exports = {
plugins: [
// 打包体积分析
new BundleAnalyzerPlugin(),
],
}加快构建速度
- 通过配置 webpack 持久化缓存
cache: filesystem,来缓存生成的 webpack 模块和 chunk,改善构建速度。
简单来说,通过 cache: filesystem 可以将构建过程的 webpack 模板进行缓存,大幅提升二次构建速度、打包速度,当构建突然中断 ,二次进行构建时,可以直接从缓存中拉取,可提速 90% 左右。
webpack.common.js 配置方式如下:
module.exports = {
cache: {
type: 'filesystem', // 使用文件缓存
},
}- 减少 loader、plugins
每个的 loader、plugin 都有其启动时间。尽量少地使用工具,将非必须的 loader、plugins 删除。
- 指定 include
为 loader 指定 include,减少 loader 应用范围,仅应用于最少数量的必要模块。
webpack.common.js 配置方式如下:
module.exports = {
rules: [
{
test: /\.(js|ts|jsx|tsx)$/,
include: paths.appSrc,
use: [
{
loader: 'esbuild-loader',
options: {
loader: 'tsx',
target: 'es2015',
},
},
],
},
],
}- 使用 webpack 资源模块 (asset module) 代替旧的 assets loader(如
file-loader/url-loader/raw-loader等),减少 loader 配置数量。
配置方式如下:
module.exports = {
rules: [
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
include: [paths.appSrc],
type: 'asset/resource',
},
],
}- alias
alias 可以创建 import 或 require 的别名,用来简化模块引入。
webpack.common.js 配置方式如下:
module.exports = {
resolve: {
alias: {
'@': paths.appSrc, // @ 代表 src 路径
},
},
}- extensions
extensions 表示需要解析的文件类型列表。
根据项目中的文件类型,定义 extensions,以覆盖 webpack 默认的 extensions,加快解析速度。
由于 webpack 的解析顺序是从左到右,因此要将使用频率高的文件类型放在左侧,如下我将 tsx 放在最左侧。
webpack.common.js 配置方式如下:
module.exports = {
resolve: {
extensions: ['.tsx', '.js'], // 因为我的项目只有这两种类型的文件,如果有其他类型,需要添加进去。
},
}- modules
modules 表示 webpack 解析模块时需要解析的目录。
指定目录可缩小 webpack 解析范围,加快构建速度。
webpack.common.js 配置方式如下:
module.exports = {
modules: ['node_modules', paths.appSrc],
}- thread-loader
通过 thread-loader 将耗时的 loader 放在一个独立的 worker 池中运行,加快 loader 构建速度。
安装:
npm i -D thread-loaderwebpack.common.js 配置方式如下:
module.exports = {
rules: [
{
test: /\.module\.(scss|sass)$/,
include: paths.appSrc,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: true,
importLoaders: 2,
},
},
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [['postcss-preset-env']],
},
},
},
{
loader: 'thread-loader',
options: {
workerParallelJobs: 2,
},
},
'sass-loader',
].filter(Boolean),
},
],
}减小打包体积
1. 代码压缩
体积优化第一步是压缩代码,通过 webpack 插件,将 JS、CSS 等文件进行压缩。
1.1 JS 压缩
使用 TerserWebpackPlugin 来压缩 JavaScript。
webpack5 自带最新的 terser-webpack-plugin,无需手动安装。
terser-webpack-plugin 默认开启了 parallel: true 配置,并发运行的默认数量: os.cpus().length - 1 ,本文配置的 parallel 数量为 4,使用多进程并发运行压缩以提高构建速度。
webpack.prod.js 配置方式如下:
const TerserPlugin = require('terser-webpack-plugin')
module.exports = {
optimization: {
minimizer: [
new TerserPlugin({
parallel: 4,
terserOptions: {
parse: {
ecma: 8,
},
compress: {
ecma: 5,
warnings: false,
comparisons: false,
inline: 2,
},
mangle: {
safari10: true,
},
output: {
ecma: 5,
comments: false,
ascii_only: true,
},
},
}),
],
},
}1.2 CSS 压缩
使用 CssMinimizerWebpackPlugin 压缩 CSS 文件。
和 optimize-css-assets-webpack-plugin 相比 ,css-minimizer-webpack-plugin 在 source maps 和 assets 中使用查询字符串会更加准确,而且支持缓存和并发模式下运行。
CssMinimizerWebpackPlugin 将在 Webpack 构建期间搜索 CSS 文件,优化、压缩 CSS。
安装:
npm install -D css-minimizer-webpack-pluginwebpack.prod.js 配置方式如下:
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
module.exports = {
optimization: {
minimizer: [
new CssMinimizerPlugin({
parallel: 4,
}),
],
},
}由于 CSS 默认是放在 JS 文件中,因此本示例是基于下章节将 CSS 代码分离后的效果。
2. 代码分离
代码分离能够把代码分离到不同的 bundle 中,然后可以按需加载或并行加载这些文件。代码分离可以用于获取更小的 bundle,以及控 制资源加载优先级,可以缩短页面加载时间。
2.1 抽离重复代码
SplitChunksPlugin 插件开箱即用,可以将公共的依赖模块提取到已有的入口 chunk 中,或者提取到一个新生成的 chunk。
webpack 将根据以下条件自动拆分 chunks:
- 新的 chunk 可以被共享,或者模块来自于
node_modules文件夹; - 新的 chunk 体积大于 20kb(在进行 min+gz 之前的体积);
- 当按需加载 chunks 时,并行请求的最大数量小于或等于 30;
- 当加载初始化页面时,并发请求的最大数量小于或等于 30; 通过 splitChunks 把 react 等公共库抽离出来,不重复引入占用体积。
注意:切记不要为 cacheGroups 定义固定的 name,因为 cacheGroups.name 指定字符串或始终返回相同字符串的函数时,会将所有常 见模块和 vendor 合并为一个 chunk。这会导致更大的初始下载量并减慢页面加载速度。
webpack.prod.js 配置方式如下:
module.exports = {
splitChunks: {
// include all types of chunks
chunks: 'all',
// 重复打包问题
cacheGroups: {
vendors: {
// node_modules里的代码
test: /[\\/]node_modules[\\/]/,
chunks: 'all',
// name: 'vendors', 一定不要定义固定的name
priority: 10, // 优先级
enforce: true,
},
},
},
}