分析工具
- webpackbar:在打包时实时显示进度
- speed-measure-webpack-plugin:看到每个 loader 和 plugin 的耗时
- webpack-bundle-analyzer:可以看到打包的 bundle 中到底包含哪些模块,以及每个模块体积包的大小
加快构建速度
最快捷的方式就是更新 webpack 版本到 5
webpack 5 较于 webpack 4 ,新增了 持久化缓存、改进缓存算法等优化
cache
通过配置 webpack 持久缓存 cache: filesystem | memory
, 来缓存生成的 webpack 模块和 chunk,改善构建速度,当构建突然中断,二次进行构建时,可以直接从缓存中拉取,可以提速 90%左右
module.exports = {
cache: {
type: 'filesystem' // 使用文件缓存
}
}
通过 持久缓存,再也不用配置dll
和cache-loader
了。
减少 loader、plugins
每个 loader、plugin 都有启动时间。尽量少地使用工具,将非必要地 loader、plugins 删除
指定 include
为 loader 指定 include,减少 loader 应用范围,仅应用于最少数量地必要模块
rule.exclude 可以排除模块范围
资源管理
使用 asset module
代替旧的 assets loader(如 file-loader、url-loader、raw-loader)
优化 resolve 配置
- 减少
resolve.modules
,resolve.extensions
,resolve.mainFiles
,resolve.descriptionFiles
中条目数量,因为他们会增加文件系统调用的次数
extensions 表示需要解析的文件类型列表
modules 表示 webpack 解析模块时需要解析的目录
- 如果项目中不适用 symlinks (例如
npm link
或者yarn link
),可以设置resolve.symlinks: false
,减少解析工作量 如果使用自定的 resolve plugin 规则,并且没有指定 context 上下文,可以设置
resolve.cacheWithContext: false
多进程
通过
thread-loader
将耗时的 laoder 放在一个独立的 worker 池中运行,加快 loader 构建速度。
比如,对于 sass: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),
},
],
}
node-sass 中有个来自 Node.js 线程池阻塞线程池的 bug,当使用 thread-loader 时,需要设置
workerParallelJobs: 2
不要使用太多的 worker,因为 nodejs 的 runtime 和 loader 都有启动开销。最小化 worker 和 主进程之间的模块传输。 进程间通信(IPC)是非常消耗资源的。
happypack 也是用来设置多线程的,但是不推荐了devtool
eval 的性能最好,但是不利于调试。大多数情况下,最佳选择是
eval-cheap-module-source-map
启动HMR(热模块替换)
使用 oneOf
根据文件类型加载对应的 loader、只要能匹配一个即可退出
最小化 entry chunk
通过配置
optimization.runtimeChunk = true
,为运行时代码创建一个额外的 chunk ,减少 entry chunk 体积,提高性能。module.exports = {
optimization: {
runtimeChunk: true,
},
}
减少打包体积
代码压缩
JS 压缩
使用TerserWebpackPlugin
来压缩 JavaScript,但是 webpack5 已经自带了。terser-webpack-plugin
默认开启了parallel: true
,并发运行的默认数量:os.cups().length - 1
。使用多进程并发运行压缩以提高构建速度。(使用了这个就不用再用 ParallelUglifyPlugin 插件了)CSS 压缩
使用CssMinimizerWebpackPlugin
压缩 CSS 文件。将在 webpack 构建期间搜索 css 文件,优化、压缩 css
和optimize-css-assets-webpack-plugin
相比,css-minimizer-webpack-plugin
在 souce maps 和 assets 中使用查询字符串会更准确,而且支持缓存和并发模式下运行。module.exports = {
optimization: {
minimizer: {
new CssMinimizerPlugin([
parallel: 4
])
}
}
}
代码分离
代码分离能够把代码分离到不同的 bundle 中,然后可以按需加载或并行加载这些文件,代码分离可以获取更小的 bundle,以及控制资源加载优先级,可以缩短页面加载时间。
代码分割 SplitChunksPlugin
- CSS 文件分离:
mini-css-extract-plugin
, 将 css 提起到单独的文件中,为每个包含 CSS 的 JS 文件创建一个 CSS 文件,并且支持 CSS 和 SourceMaps 的按需加载 ```javascript const MiniCssExtractPlugin = require(‘mini-css-extract-plugin’)
module.exports = { plugins: [new MiniCssExtractPlugin()], module: { rules: [ { test: /.module.(scss|sass)$/, include: paths.appSrc, use: [ ‘style-loader’, isEnvProduction && MiniCssExtractPlugin.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), }, ], }, }
> **MiniCssExtractPlugin.loader 要放在 style-loader 后面**
<a name="vOgX0"></a>
#### Tree Shaking
> 必要条件:CommonJs、AMD、CMD 等是高度动态的,难以预测的。ESM 从规范层面避免了这一行为,要求所有的导入导出模块必须在最顶层,且导入导出的模块必须是字符串常量。所以 ESM 下模块之间的依赖关系是高度确定的,与运行状态无关,编译工具只需要对 ESM 模块做静态分析,就可以从代码字面量中推断出哪些模块值未曾被其他模块使用。**这就是 Tree Shaking 的必要条件**
1. JS<br />我们可以设置 `package.json`的`sideEffects`属性作为标记,向 compiler 提供提示,表明项目中哪些文件是 'pure' ,由此可以安全的删除文件中未使用的部分。<br />Dead Code 一般具有一下几个特征:
- 代码不会执行,不可到达
- 代码执行的结果不会用到
- 代码只会影响死变量(只写不读)
```json
{
"name": "your-project",
"sideEffects": false
}
// 当代码有副作用时,需要将sideEffects 写成数组的形式
- CSS
使用purgecss-webpack-plugin
进行 css tree shaking。配合mini-css-extract-plugin
```javascript const glob = require(‘glob’) const MiniCssExtractPlugin = require(‘mini-css-extract-plugin’) const PurgeCssPlugin = require(‘purgecss-webpack-plugin’) const paths = require(‘pahts’)
module.exports = {
plugins: [
new MiniCssExtractPlugin({
filename: ‘[name].css’,
}),
new PurgeCssPlugin({
paths: glob.sync(${paths.appSrc}/**/*
, { nodir: true }),
}),
],
}
<a name="Jn9OP"></a>
#### CDN
这里引入 CDN 的首要目的时为了减少打包体积,因此仅仅是将一大部分**静态资源手动上传至 CDN**,并修改本地引入路径
- 字体:压缩并上传 CDN
- 图片:压缩并上传至 CDN
<a name="Ql8ai"></a>
## 加快加载速度
<a name="mk1y0"></a>
#### 按需加载
<a name="gmqz3"></a>
#### 浏览器缓存
webpack 支持根据资源内容,**创建 hash id**,当资源内容发生变化时,创建新的 hash id
```javascript
module.exports = {
output: {
// 仅在生产环境添加hash
filename: ctx.isEnvProduction ? '[name].[contenthash].bundle.js' : '[name].bundle.js',
},
}
配置 CSS bundle hash
module.exports = {
plugins: [
new MiniCssExtractPlugin({
filename: '[hash].[name].css',
}),
],
}
配置 optimization.moduleIds, 让公共包 splitChunks 的 hash 不因为新依赖而改变,减少非必要的 hash 变动。
module.exports = {
optimization: {
moduleIds: 'deterministic',
},
}
CDN
将所有静态资源,上传至 CDN,通过 CDN 加速来提升加载速度。
module.exports = {
output: {
publicPath: ctx.isEnvProduction ? 'https:xxx.com' : '', // cdn域名
},
}