kylin

醉里论道,醒时折花。

kylin

webpack打包慢的解决方案

前言

在开发某基于electron、vue等技术栈的桌面程序的过程中发现,模块越多,webpack打包的速度越慢,居然慢到了令人发指的50min!之后我对整个项目进程了排查,最后发现打包慢的罪魁祸首来自gojs这个模块。由于我对gojs的源代码进行过修改,在.vue组件中引入的是未压缩的gojs/release/go-nw.js,该文件巨大无比多达数万行。起初我以为是Uglify的过程占用了太多时间,然而发现,假如不使用压缩混淆,当只有一个文件引入该模块时,打包时间减少到了两分钟,但若有5、6个文件都引用了该模块,打包速度并没有明显的提升,此时我想到了将该文件直接在index页面中引入的方式来避免将该文件打包,由于种种原因,在这个项目中并不能将变量go作为属性赋值给window,这里我放弃了使用最常见的externals方法,而是采用了另一种方式。

解决方案

DLLPlugin方法能把第三方代码完全分离开,即每次只打包项目自身的代码。

gojs修改后的版本进行混淆压缩

这里可以使用任何能达到效果的方式,我使用的是全局模块uglyfy-js

修改gojs模块指向

先将gojs模块的package.json中的main字段指向想要引入的文件

1
2
3
4
5
{
// ...
"main": "release/go-nw.min.js",
// ...
}

配置文件

  • 准备一个新的配置文件 dll.config.js 用来打包gojs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const webpack = require('webpack');
const path = require('path');
const vendors = [
'gojs'
//如果有别的第三方文件也可以加在这里,如vue、vue-router等
];
module.exports = {
output: {
filename: '[name].js',
path: path.join(__dirname, '../static/lib'),
library: '[name]'
},
entry: {
"lib": vendors,
},
plugins: [
new webpack.DllPlugin({
//path决定了生成的manifest.json文件的路径
path: path.join(__dirname, '../.electron-vue/manifest.json'),
name: '[name]',
context: __dirname,
})
]
};

运行命令

1
webpack --config .electron-vue/dll.config.js

命令生成两个文件——位于/static/liblib.js和位于.electron-vuemanifest.json

  • 修改原来的打包配置文件webpack.prod.config.js
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    //...
    plugins:[
    // ...
    //添加plugin
    new webpack.DllReferencePlugin({
    context: __dirname,
    //路径要注意
    manifest: require('./manifest.json'),
    })
    // ...
    new HtmlWebpackPlugin({
    filename: 'index.html',
    template: path.resolve(__dirname, '../src/index.ejs'),
    minify: {
    collapseWhitespace: true,
    removeAttributeQuotes: true,
    removeComments: true
    },
    //...HtmlWebpackPlugin.options中添加字段dll,值为之前生成的lib文件的路径,注意不能使用/User/xxx/...这样的绝对路径
    //如果有多个文件则可以使用数组
    dll: 'static/lib/lib.js'
    })
    ]

修改index.ejs

1
2
3
4
5
6
7
8
9
<!-- ... -->
<meta charset="utf-8">
<title>cx-metadata</title>
<% if (htmlWebpackPlugin.options.dll) { %>
<!-- 如果有多个可以使用for循环插入script -->
<script src="<%= htmlWebpackPlugin.options.dll %>"></script>
<% } %>
<!-- ... -->

修改打包命令

修改package.json文件中的打包命令

1
2
3
4
5
6
7
8
9
10
{
// ...
"scripts": {
"build": "npm run pack:dll && node .electron-vue/build.js && electron-builder",
// ...
"pack:dll":"webpack --config .electron-vue/dll.config.js",
// ...
},
// ...
}

结果

最后将该项目的打包时间控制在了40s以内,若是还嫌弃慢,可以将vuevue-router等库文件做同样的处理,还可减少几十秒的时间。