webpack性能优化篇
webpack性能优化篇
webpack性能优化篇
随着业务代码不断增加,使用webpack进行构建的时长也会随之不断增加。下面我们来通过一些配置来减少构建时长。
speed-measure-webpack-plugin
该插件能够测量出在你的构建过程中,每一个 Loader 和 Plugin 的执行时长。
这样,你就能根据耗时较长的loader或者plugin来进行代码的优化。
使用:
只需要在你导出 Webpack 配置时,为你的原始配置包一层 smp.wrap 就可以了,接下来执行构建
const speedMeasurePlugin = require('speed-measure-webpack-plugin')
const config = {
entry: "./src/index.js",
output: {
filename: '[name].[hash].js',
clean: true
}
}
const smp = new speedMeasurePlugin();
module.exports = smp.wrap(config);
优化
虽然通过speed-measure-webpack-plugin我们可以找到耗时较长的loader或者plugin,但是如果我们稍微对AST有点概念的话,都会知道其实最耗时的是会是在编译js跟css的loader上(转化 AST -> 遍历树 -> 转化回代码)。
对此,我们大致可以将优化分成4个方向:
- 缓存
- 多核
- 抽离
- 拆分
缓存
在每次启动webpack的时候,都默认会将所有的文件都编译一次,那么我们试想一下,是否可以将一些没有变更的文件给缓存下来呢?
大部分 Loader 都提供了 cache 配置项,比如在 babel-loader 中,可以通过设置 cacheDirectory 来开启缓存。这样,babel-loader 就会将每次的编译结果写进硬盘文件(默认是在项目根目录下的node_modules/.cache/babel-loader目录内,当然你也可以自定义)。
const speedMeasurePlugin = require('speed-measure-webpack-plugin')
const config = {
mode: 'development',
entry: "./src/index.js",
output: {
filename: '[name].[hash].js',
clean: true
},
module: {
rules: [
{
test: /\.js$/,
include: [
path.resolve(__dirname, 'src')
],
use: [
{
loader: 'babel-loader',
options: {
cacheDirectory: 'babel-loader'
}
}
]
}
]
}
}
const smp = new speedMeasurePlugin();
module.exports = smp.wrap(config);
对于其他不支持缓存的loader,可以使用cache-loader。
注意: cache-loader必须放在第一个
const speedMeasurePlugin = require('speed-measure-webpack-plugin')
const config = {
mode: 'development',
entry: "./src/index.js",
output: {
filename: '[name].[hash].js',
clean: true
},
module: {
rules: [
{
test: /\.js$/,
include: [
path.resolve(__dirname, 'src')
],
use: [
{
loader: 'cache-loader',
options: {
cacheDirectory: 'cache-loader'
}
}
...otherLoaders
]
}
]
}
}
const smp = new speedMeasurePlugin();
module.exports = smp.wrap(config);
cache-loader默认将缓存存放的路径是项目根目录下的.cache-loader目录内,我们习惯将它配置到项目根目录下的node_modules/.cache目录下,与babel-loader等其他Plugin或者Loader缓存存放在一块。
多核
多核就是使用多个进程去编译处理。在webpack中最常用的就是happypack。
happypack原理: 将webpack中最耗时的loader文件转换操作任务,分解到多个进程中并行处理,从而减少构建时间。
const speedMeasurePlugin = require('speed-measure-webpack-plugin')
const HappyPack = require('happypack');
const path = require('path');
const threadPool = 5;
const config = {
mode: 'development',
entry: "./src/index.js",
output: {
filename: '[name].[hash].js',
clean: true
},
module: {
rules: [
{
test: /\.js$/,
// 只处理src目录下的文件
include: [
path.resolve(__dirname, 'src')
],
use: 'happypack/loader?id=babel'
}
]
},
plugins: [
new HappyPack({
id: 'babel',
loaders: [
{
loader: 'cache-loader',
options: {
cacheDirectory: 'cache-loader'
}
},
{
loader: 'babel-loader',
}
],
threads: threadPool
})
]
}
const smp = new speedMeasurePlugin();
module.exports = smp.wrap(config);
用 happypack 中的 Loaders 做一层包装就好了,向外暴露一个id ,而在 module.rules 里,就不需要写loader,直接引用这个 id 即可,
在speed-measure-webpack-plugin中耗时较久的loaders使用happypack做一层多核编译的包装。
抽离
对于一些不常变更的静态依赖,比如axios、lodash等,我们不希望这些依赖被集成进每一次构建逻辑中,因为它们真的太少时候会被变更了,所以每次的构建的输入输出都应该是相同的。因此,我们会设法将这些静态依赖从每一次的构建逻辑中抽离出去,以提升我们每次构建的构建效率。
常见方案:
- 使用
DllPlugin Externals方式
1. 使用DllPlugin
在使用webpack进行打包时候,对于依赖的第三方库,如axios等这些不会修改的依赖,可以让它和业务代码分开打包;
只要不升级依赖库版本,之后webpack就只需要打包项目业务代码,遇到需要导入的模块在某个动态链接库中时,就直接去其中获取;而不用再去编译第三方库,这样第三方库就只需要打包一次。
webpack中已经内置该插件(webpack.DllPlugin用于打包出一个个单独的动态链接库文件)。我们看看怎么使用:
新建一个文件,webpack.dll.config.js
const webpack = require('webpack');
const path = require('path');
module.exports = {
mode: 'development',
entry: {
utils: ['axios'],
// 将vue相关模块放在同一个动态链接库下
vue: ['vue', 'vue-router']
},
output: {
filename: '[name].dll.js',
path: path.resolve(__dirname, 'dll'),
// 存放动态链接库的全局变量名,加上_dll_防止全局变量冲突
library: '_dll_[name]'
},
plugins: [
new webpack.DllPlugin({
name: '_dll_[name]',
// 动态链接库的全局变量名称,需要可output.library中保持一致,也是输出的manifest.json文件中name的字段值
// 如axios.manifest.json字段中存在"name":"_dll_axios"
path: path.join(__dirname, 'dll', '[name].manifest.json')
})
]
}
在命令行中输入npx webpack --config webpack.dll.config.js.
可以看到生成了一个dll目录
然后在webpack.config.js中对生成的dll进行配置。
使用webpack.DllReferencePlugin引入DllPlugin插件打包好的动态链接库文件。
plugins: [
// 告诉webpack使用了哪些动态链接库
new webpack.DllReferencePlugin({
manifest: require('./dll/utils.manifest.json')
}),
new webpack.DllReferencePlugin({
manifest: require('./dll/vue.manifest.json')
}),
]
注意:在webpack.dll.config.js文件中,webpack.DllPlugin中的name参数必须和output.library中的一致;因为DllPlugin的name参数影响输出的manifest.json的name;而webpack.config.js中的webpack.DllReferencePlugin会读取manifest.json的name,将值作为从全局变量中获取动态链接库内容时的全局变量名
。
2. Externals方式
作为webpack内置的一个参数,其使用非常简单。
- 在
HTML中引入第三方库的cdn
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
- 在
webpack中配置externals
externals: {
'axios': 'axios'
}
- 在
js中引用
import axios from 'axios';
axios.post()
拆分
这个概念就很简单了,虽然说在大前端时代下,SPA 已经成为主流,但我们不免还是会有一些项目需要做成 MPA(多页应用),得益于 webpack 的多 entry 支持,因此我们可以把多页都放在一个 repo 下进行管理和维护。
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐

所有评论(0)