WebPack构建速度以及体积优化策略
# 前言
在日常开发中,大型项目的构建速度太慢以及构建产物的体积太大会极大的影响我们的工作效率和舒适度。下面将介绍几种优化策略,来让你的项目更加丝滑一些
在进行优化之前,先让我们安装两个插件来更加直观的分析一下到底是哪个地方影响了构建速度以及占用了较多的体积
# 速度分析
一般构建速度可以用到speed-measure-webpack-plugin
这个插件
npm i speed-measure-webpack-plugin
安装好之后在项目中引入进来,用法也很简单,创建一个插件实例然后用实例的wrap方法把原来的配置包裹一下就可以
const speedWebPackPlugin = require('speed-measure-webpack-plugin')
const swp = new speedWebPackPlugin()
module.exports = swp.wrap({
...这里书写你的webpack config
})
2
3
4
5
6
运行构建就可以看到上述图片中的时间分析,这样我们就可以针对性的去对构建速度慢的地方优化
# 体积分析
体积分析可以用到webpack-bundle-analyzer
这个插件
npm i webpack-bundle-analyzer
使用方法
const BundleAnalyzer = require('webpack-bundle-analyzer').BundleAnalyzerPlugin //查看打包后的体积分布图
module.exports ={
plugins: [
new BundleAnalyzer(),
],
}
2
3
4
5
6
7
运行构建之后会默认打开一个窗口,里面可以看到构架体积的图
# 速度优化
下面介绍一下构建速度方面的优化
# 使用高版本webpack和nodejs
这一点没什么好说的,新版本肯定都是对旧版本进行了优化的,所以我们尽量选择最新版本的工具来构建我们的项目
# 多进程/多实例构建
webpack默认是单进程构建,一旦我们的项目太过庞大,单进程的速度会很明显的慢下来,所以我们可以通过开启多进程构建的方式来提高我们的构建速度
早在webpack3的时候社区有一个HappyPack
的插件,用于多进程构建,但是在webpack4中已经内置了一个loader:thread-loader
来处理多进程构建了,这里主要介绍一下thread-loader
,对happypack感兴趣的同学可以自行学习一下
thread-loader
的使用非常的简单,需将此 loader 放置在其他 loader 之前。放置在此 loader 之后的 loader 会在一个独立的 worker 池中运行
在 worker 池中运行的 loader 是受到限制的。例如:
- 这些 loader 不能生成新的文件。
- 这些 loader 不能使用自定义的 loader API(也就是说,不能通过插件来自定义)。
- 这些 loader 无法获取 webpack 的配置。
开启多线程也是有成本的,所以此loader必须放在耗时的loader前面,比如babel-loader
这种,随意使用的话可能会适得其反
use:[
{
loader:"thread-loader",
options:{
workers:3,//表示开启多少个线程
}
},
"babel-loader"
]
2
3
4
5
6
7
8
9
# 多进程/多实例压缩
关于这一方面的优化建议使用TerserWebpackPlugin
,webpack5已经内置了,具体使用方法直接查看文档即可
TerserWebpackPlugin (opens new window)
# 预编译资源模块
所谓预编译资源模块指的就是提前把一些不怎么会变的资源编译好,这样就不需要在每次构建的时候都去重新编译他。 在webpack中有提供externals选项允许我们将使用到的外部库通过cdn方式引入,从而让webpack不去处理他。但是这种方案需要我们自己准备cdn啥的非常的麻烦。另外除了这种方案外,我们的splitchunks选项也可以做到将一些外部库提取出来打包成一个单独的文件,这样一来虽然不能提升构建速度,但是在浏览器端可以充分利用缓存。不过此方案最好还是使用在提取公共代码上,用在提取外部资源库还是不妥。下面就来介绍一下真正的利器
dll
dll
是webpack内置的一个插件,dllplugin (opens new window)
他是通过在一开始就将一些不常改变的js剥离出来,构建好,然后在打包的时候直接通过json文件的映射关系去引入已经构建好的文件,极大的加快了我们的构建速度。不过他的缺点也很明显,牺牲空间换时间。在使用他的过程中我们需要额外创建一个配置文件,并且提前运行这个配置文件,去构建这些库。下面就一起来看看他的用法吧
新建一个webpack.dll.js文件
const path = require('path')
const webpack = require('webpack')
// dllplugin 分包策略
module.exports = {
entry: {
library:[
'vue',
'vue-router'
]
},
output: {
filename: "[name]_[chunkhash].dll.js",
path:path.join(__dirname,'build/library'),
library: '[name]'
},
plugins: [
new webpack.DllPlugin({
name:"[name]_[hash]",
path: path.join(__dirname,'build/library/[name].json')
})
]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
通过webpack运行他
"dll": "npx webpack --config webpack.dll.js",
运行完之后就可以看到生成了一个build文件夹,里面的library.js就是抽离出来的库,json文件就是描述文件
接下来在我们的webpack.config里配置一下引入这个描述文件
plugins: [
//plugin的配置
new webpack.DllReferencePlugin({
manifest: require('./build/library/library.json'),
}),
// new BundleAnalyzer(),
]
2
3
4
5
6
7
执行构建,你就会发现速度有了一个质的提升。
# 充分利用缓存
- babel-loader的缓存
babel-loader提供了一个配置项
cacheDirectory
,开启缓存机制
{
loader: "babel-loader",
options: {
cacheDirectory:true //开启babel-loader的缓存
}
}
2
3
4
5
6
- hard-source-webpack-plugin hard-source-webpack-plugin插件可以为模块提供中间缓存步骤,也是在第二次运行构建之后速度会有显著的提升
// webpack.config.js
var HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
module.exports = {
context: // ...
entry: // ...
output: // ...
plugins: [
new HardSourceWebpackPlugin()
]
}
2
3
4
5
6
7
8
9
10
11
注意
HardSourceWebpackPlugin 插件仅支持webpack5一下的版本,webpack5可以采用cache属性来配置构建缓存
cache: {
type:'filesystem',
allowCollectingMemory:true,
},
2
3
4
# 缩小构建目标
- 1.配置babel-loader,缩小babel-loader的转译目标
module: {
rules: [
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,//此处配置不转译node_modules|bower_components文件夹内的东西
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
- 2.配置reslove,减少模块搜索层级 在webpack中查找文件的规则和nodejs的require类似,比如我们引入了一个外部的库,webpack会现在目录内查找, 没找到就回去node——modlues里去查找,如果还没有就去父级目录的依赖里查找,以此类推,而我们日常开发中基本上依赖包都是安装在根目录的依赖文件夹下,所以我们可以通过配置搜索范围来达到优化构建速度的目的
resolve: {
alias: {
'@':path.resolve(__dirname,'./src')
},
modules: [path.resolve(__dirname,'node_modules')],//指定查找目录,优化查找速度
},
2
3
4
5
6
# 使用Tree Sharking 擦除无用的js和css
webpack5已经内置了Tree Sharking 擦除js ,下面就介绍一下如何擦除无用的css
- 1.PurifyCSS: 遍历代码,识别已经用到得css
- 2.uncss: HTML需要通过jsdom加载,所有的样式通过PostCSS解析,通过document.querySelector来识别在html文件里面不存在的选择器
在webpack中进行无用css的擦除
purgecss-webpack-plugin
的使用
此插件在webpack4中需要配合mini-css-extract-plugin
一起使用,用法如下
const PATHS = {
src: path.join(__dirname, 'src')
}
new PurgeCSSPlugin({
paths: glob.sync(`${PATHS.src}/**/*`, { nodir: true }),
})
2
3
4
5
6
# 图片压缩
大图片是导致网站加载慢的主要元凶,为了避免这种情况,我们一般会手动的去压缩图片,这种方式繁琐且不适合程序员,在webpack中,当然也是提供了这一类功能的插件,下面就让我们一起来学习一下
image-webpack-loader
是一个 imagemin
的图片压缩工具,他可以支持多种格式的图,如PNG, JPEG, GIF, SVG and WEBP
.
注意这个插件在使用的过程中需要用cnpm安装,用npm安装可能会出现漏下载了包的情况,这一点有点蛋疼
{
loader: 'image-webpack-loader',
options: {
mozjpeg: {
progressive: true,
},
optipng: {
enabled: false,
},
pngquant: {
quality: [0.65, 0.90],
speed: 4
},
gifsicle: {
interlaced: false,
},
webp: {
quality: 75
}
}
},
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
注意
生产环境还是不建议使用这个插件,因为安装依赖的时间太长,而且npm安装有可能丢包,这样会影响到发布体验。最好还是约定一个规范,在使用图片前自己去压缩好再使用,这样虽然开发阶段有点麻烦,但是对后续的流程可以避免不必要的问题出现
# 总结
最后让我们来总结一下文中提到的优化方案
Scope Hoisting
将多个模块合并成一个函数,减少性能问题。
Tree-shaking
擦除无用的js和css
- 公共资源的分离,如
splitchunk
提取公共资源,dll
预编译分离公共资源,external
避免打包外部资源,通过cdn引入资源包
- 公共资源的分离,如
- 简化资源搜索路径
reslove
相关的内容
- 简化资源搜索路径
- 图片资源的压缩
image-webpack-loader
- 图片资源的压缩
- 充分利用缓存,如
babel-loader
缓存,webpack5 cache
缓存等
- 充分利用缓存,如