提高 webpack 的打包速度 happypack 和 dll 打包
本人一直在用 React 开发一个后台管理系统,那天不小心加了个富文本编辑器(https://github.com/jpuri/react-draft-wysiwyg)之后,webpack 打包的速度就直线下降,觉得是时候要开始一波优化了。
当前的配置是 8G内存,i5CPU的mac,node v8.2.1 , webpack 3.5.5
,在这个配置下打包一次的数据是
Compiled successfully in 46.5s.
File sizes after gzip:
296.77 KB dist/js/common.js
149.49 KB (-1 B) dist/js/atadd.997ff5.js
34.06 KB dist/css/index.css
15.32 KB (-8 B) dist/js/cpadd.5c7f29.js
4.45 KB dist/js/cpsupple.9fe38b.js
3.3 KB (+13 B) dist/js/cplist.3a5f02.js
3.08 KB dist/js/cpview.e68fe9.js
2.91 KB dist/js/atview.2a5aac.js
2.89 KB dist/js/atlist.6d166c.js
2.24 KB dist/js/atuser.30b019.js
295 B dist/js/activity.4e286a.js
291 B dist/js/coupon.3dd30f.js
happypack 多线程打包
一般情况下,js 是单线程执行的,但 node 不是。利用 node 提供的多线程环境,happypack 是可以多线程打包的。基本上打开官网看了一下 readme 就可以配置了,特别是我只针对 js 的编译进行优化,配置还是比较简单的。
{
test:/\.js$/,
loader:'happypack/loader',
}
//...上面修改了js的loader,下面也相应增加一个即可
new HappyPack({
loaders:....,//原来的babel相关配置
})
这里 有一篇 happypack 原理的解释文章,就不赘言了。应用了 happypack 后的打包一次的数据如下:
Compiled successfully in 45.7s.
File sizes after gzip:
296.82 KB dist/js/common.js
150.06 KB (+144.13 KB) dist/js/atadd.5afe2c.js
34.06 KB dist/css/index.css
15.32 KB dist/js/cpadd.5c7f29.js
4.45 KB dist/js/cpsupple.9fe38b.js
3.3 KB dist/js/cplist.3a5f02.js
3.08 KB dist/js/cpview.e68fe9.js
2.91 KB dist/js/atview.2a5aac.js
2.89 KB dist/js/atlist.6d166c.js
2.24 KB dist/js/atuser.30b019.js
295 B dist/js/activity.4e286a.js
291 B dist/js/coupon.3dd30f.js
好像并没有多大区别。
dll 打包
回归到原始的问题,在加富文本编辑器后特别的慢,所以我们应该要把这个富文本编辑器特别提取出来,不要每次打包都分析它。webpack 本身提供这种功能,叫 dll 打包,需要两个插件 DllPlugin 和 DllReferencePlugin 。前者是用来打包 dll.js,后者是用在打包主流程时引用刚才的dll.js的。具体配置可以参考官网。
我应用 dll 打包了两个文件,一个是 vendor
文件,一个是 editor
文件,相关配置如下
//webpack.config.dll.js
const path = require('path');
const webpack=require('webpack');
const env = 'production';
const vendors=[
'react',
'react-dom',
'react-router',
'history',
'redux',
'dva',
'axios',
'qs',
'moment',
'styled-components'
];
const editor = [
'react-draft-wysiwyg',
'draftjs-to-html',
'html-to-draftjs',
'draft-js'
]
const libname = '[name]_lib'
module.exports={
entry:{
'vendor':vendors,
'editor':editor,
},
output:{
path:path.join(__dirname, 'dll'),
filename:'[name].[hash:4].dll.js',
library:libname,
},
plugins:[
new webpack.HashedModuleIdsPlugin(), //保持其他包的hash不会变
new webpack.optimize.CommonsChunkPlugin({
names:['vendor'],
minChunks:2, //把编辑器里面关于react那部分代码提取到vendor
}),
new webpack.DllPlugin({
path: path.resolve(__dirname, 'dll', 'manifest-[name].json'),
name: libname,
context:process.cwd() //是解析包路径的上下文,这个要和 webpack.config.js 一致。
}),
...uglify, //uglifyjs及其他在生产环境应该要用的插件配置
]
}
这样再运行 webpack -p --config ./config/webpack.config.dll.js
即可生成 vendor.dll.js
和 editor.dll.js
其中 vendor.dll.js
是基础文件,一进入页面就要引入的,所以直接在html里面加入即可
//webpack.config.js
const dir = fs.readdirSync(path.resolve(__dirname,'dll')); //因为文件名带hash,所以要用程序读出来
new webpack.DllReferencePlugin({
context: process.cwd(), //此目录要跟之前build dll时一样
manifest: require('./dll/manifest-vendor.json')
}),
new HtmlWebpackPlugin({
...otherConfig,
scripts: env === 'production' ? dir.map(file=>{
if(/vendor\..{4}\.dll\.js$/.test(file)){
return `${publicPath}js/${file}` //publicPath是全局配置的publicPath
}
}): [xxx],
动态载入 dll 脚本
还有一个 editor.dll.js
文件,gzip 后有 160 多k,是需要富文本编辑器的页面才需要用到,我并不想在页面一载入的时候就用,于是就想了个黑方法,在页面第一次加载编辑器时,异步引入该dll文件再进行渲染。关键代码如下:
import Async from 'react-code-splitting';
let editorScriptloaded = __DEV__; //编辑器的dll脚本载入完成,在开发状态下永远为真
let editorScriptStartload = false; //编辑器的dll脚本开始载入
export default class AsyncEditor extends React.Component {
render(){
return <Async load={new Promise((res,rej)=>{
if(typeof window !== 'undefined'){
if(editorScriptloaded){
resolveEditor(res, rej);
}else if(!editorScriptStartload){
editorScriptStartload = true;
var doc = window.document;
var s = doc.createElement('script');
s.src=__EDITOR_URL__; //这是一个用DefinePlugin定义的变量
s.onload = function(){
editorScriptloaded = true;
resolveEditor(res, rej);
};
doc.body.appendChild(s);
}
}else{
res(null)
}
})} componentProps={this.props}/>
}
}
function resolveEditor(res, rej){
@res
class RichEditor extends React.Component {
//......
}
这样只需要引入AsyncEditor,像正常的React组件那样使用,就会自动把依赖的dll文件动态加载进来,而且只会加载一次。
优化结果
不计两个 dll 文件的打包,时间一下子就减少很多了,结果如下
Compiled successfully in 25.3s.
File sizes after gzip:
172.04 KB dist/js/common.js
160.28 KB dist/js/editor.7517.dll.js
130.92 KB dist/js/vendor.7517.dll.js
34.06 KB dist/css/index.css
15.32 KB dist/js/cpadd.be002d.js
5.93 KB (-144.13 KB) dist/js/atadd.18beb0.js
4.45 KB dist/js/cpsupple.9fe38b.js
3.3 KB dist/js/cplist.3a5f02.js
3.08 KB dist/js/cpview.e68fe9.js
2.91 KB dist/js/atview.2a5aac.js
2.89 KB dist/js/atlist.6d166c.js
2.24 KB dist/js/atuser.30b019.js
295 B dist/js/activity.4e286a.js
291 B dist/js/coupon.3dd30f.js
效率提高了 46%!
细心的同学会发现后来的打包的文件会有细微的增大,但是相比起打包的时间,这点还是可以接受的。如果想要减少体积,react-draft-wysiwyg
中有很多国际化的文件,还有 draft-js
也不支持 tree shaking
,我们可以在后续的时候再挖掘这些优化点。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
上一篇: PWA 下一代的 Web 模型
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论