打包优化
刀刀
4/7/2025
0 字
0 分钟
项目的构建速度由两个方面决定:
- 处理的内容数量
- 进行的操作
优化操作一般有以下几点:
减少处理内容数量
指定
include
和exclude
排除掉一些不必要的处理; DLL 优化处理提高处理效率
多线程操作;缓存
sourcemap
减少操作的数量
去掉一些没意义的操作
减少处理内容数量
排除不必要的处理
先来看看一段 vue.config.js
代码:
const path = require('path')
function resolve(dir) {
return path.join(__dirname, dir)
}
module.exports = {
publicPath: process.env.NODE_ENV === 'production' ? './' : '/',
chainWebpack: (config) => {
config.resolve.alias.set('@', resolve('src'))
},
productionSourceMap: false,
devServer: {
open: false,
port: 5003,
overlay: {
errors:true,
warmimgs: true,
},
proxy: {
'/api': {
target: 'xxx',
changeOrigin: true
}
}
}
}
打包这个项目,打包的时间为 6.4s,接下来测试一下通过 exclude
排除后能减少多少时间。
需要注意的是,不能什么都能排除, js
文件 webpack
本来就认识,但是 ts
、vue
、html
等文件不能排除,需要处理,通过 babel-loader-es6-es5
转编译。
开发环境中排除掉 js
中的 node_modules
和 src
,代码如下:
const path = require('path')
function resolve(dir) {
return path.join(__dirname, dir)
}
module.exports = {
publicPath: process.env.NODE_ENV === 'production' ? './' : '/',
chainWebpack: (config) => {
if(config.module.rule('js').exclude
.add(resolve('/node_modules'))
.add(resolve('/src'))
)
config.resolve.alias.set('@', resolve('src'))
},
// ...
}
重新打包,打包时间缩短为 6.19s,这个项目用到的 js
不多,如果 js
文件多的话能减少更多的时间。
DLL处理
接下来做 DLL 处理,步骤为先把一些第三方库打包,后续打包时候不去打包这些第三方库,而是直接复用。
根目录下新建一个 webpack.dll.js
文件,代码如下:
const path = require('path')
const webpack = require('webpack')
// dll 文件存放的目录
const dllPath = 'public/vendor'
module.exports = {
mode: 'production',
entry: {
// 需要提取的库文件
vendor: ['vue', 'vuex', 'vue-router', 'element-ui', 'echarts']
},
// 定义出口
output: {
// 当前目录下的 public文件夹下的 vendor
path: path.join(__dirname, dllPath),
filename: '[name].dll.js',
// vendor.dll.js 中暴露出的全局变量名
// 保持与 webpack.DllPlugin 中名称一致
library: '[name]_[hash]'
},
plugins: [
// manifest.json 描述动态链接库包含了哪些内容
new webpack DllPlugin({
path: path.join(__dirname, dllPath, '[name]-manifest.json'),
// 保持与 output.library 中名称一致
name: '[name]_[hash]',
context:process.cwd()
})
]
}
接着去到 package.json
文件重写打包命令:
{
// ...
"scripts": {
"dll": "webpack --config webpack.dll.js",
// ...
}
}
通过新命令打包,会遇到一个坑,它会提示你是否要下载 wenpack-cli
,如果直接点 yes ,会有版本冲突问题,正确做法是先在全局下载 webpack-cli
:
npm i webpack-cli@3.3.12 --save-dev
然后再运行命令:
npm run dll
运行完毕, public
文件夹下已经有打包好的 vendor
文件夹,接下来回到 vue.config.js
文件,添加 webpack
配置,代码如下:
const path = require('path')
const webpack = require('webpack')
function resolve(dir) {
return path.join(__dirname, dir)
}
module.exports = {
publicPath: process.env.NODE_ENV === 'production' ? './' : '/',
configureWebpack: {
plugins: [
new webpack.DllReferencePlugin({
context: process.cwd(),
// 关键设置,引用的文件地址
manifest: require('./public/vendor/vendor-manifest.json')
})
]
},
// ...
}
改完配置文件后因为没有缓存了,所以第一次打包速度会比较慢,以后续打包时间为准。打包后发现时间已经缩短为 3.15s 。
不过现在还没结束,打包完毕后此时项目如果在浏览器上打开,页面一片空白。这是因为前面打包的时候把这些资源排除掉不打包了,解决方法为去到 index.html
文件中,把那些排除出去的 js
文件手动引入。
<script src="<%= BASE_URL %>vendor/vendor.dll.js"></script>
提高处理效率
多线程处理
多线程打包的收益在项目较小的时候其实不是很明显,因为开启多线程也需要额外的操作。
想要使用多线程需要下载一个 loader
:
npm i thread-loader --save
下载同时来写代码,在 vue.config.js
文件中引入使用:
const os = require('os')
const path = require('path')
function resolve(dir) {
return path.join(__dirname, dir)
}
module.exports = {
chainWebpack: (config) => {
if(config.module.rule('js').exclude
.add(resolve('/node_modules'))
.add(resolve('/src'))
config.module.rule('vue').use('thread-loader')
.loader('thread-loader').tap(() =>{
return {
// 根据当前cpu数量
worker: os.cpus().length
}
}).before('vue-loader') // 在vue-loader处理完后再处理
)
config.resolve.alias.set('@', resolve('src'))
},
// ...
}
但是这样可能不会生效,甚至会有反效果,这是因为优化之后也有可能会增多处理。
上方开多线程这个举动也是需要时间,多线程处理的时间比开启多线程的时间要少,因此最后打包时间也会增多,不太推荐使用该方法。
sourceMap
举个例子,我在 login.vue
页面 console.log()
输出一行代码,开启 sourceMap
后可以根据这个输出定位到是哪个文件哪行代码输出的。在生产模式时没必要打开。
Vue-cli 中配置的默认 eval
的 sourceMap
,可以关闭,代码如下:
module.exports = {
configureWebpack: {
devtool: 'none',
plugins: [
// ...
]
},
}
关闭之后打包时间确实有减少,但是打开项目之后查看控制台的打印,会定位到打包好的文件,不利于我们排错,因此不推荐关闭。
注意
CSS 的
sourceMap
默认是关闭的,可以打开,但是最后打包时间会增加,因为增加了要处理的内容。开启方式为在vue.config.js
文件设置以下代码:jsmodule.exports = { // ... css: { sourceMap: true } }
完整代码
vue.config.js
完整代码如下:
const os = require('os')
const path = require('path')
function resolve(dir) {
return path.join(__dirname, dir)
}
module.exports = {
publicPath: process.env.NODE_ENV === 'production' ? './' : '/',
chainWebpack: (config) => {
if(config.module.rule('js').exclude
.add(resolve('/node_modules'))
.add(resolve('/src'))
/**
* 多线程处理模块start,不太推荐使用
*/
config.module.rule('vue').use('thread-loader')
.loader('thread-loader').tap(() =>{
return {
// 根据当前cpu数量
worker: os.cpus().length
}
}).before('vue-loader') // 在vue-loader处理完后再处理
/**
* 多线程处理模块end
*/
)
config.resolve.alias.set('@', resolve('src'))
},
configureWebpack: {
plugins: [
new webpack.DllReferencePlugin({
context: process.cwd(),
// 关键设置,引用的文件地址
manifest: require('./public/vendor/vendor-manifest.json')
})
]
},
productionSourceMap: false,
devServer: {
open: false,
port: 5003,
overlay: {
errors:true,
warmimgs: true,
},
proxy: {
'/api': {
target: 'xxx',
changeOrigin: true
}
}
}
}