提高 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.jseditor.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,我们可以在后续的时候再挖掘这些优化点。

以上是 提高 webpack 的打包速度 happypack 和 dll 打包 的全部内容, 来源链接: utcz.com/z/264664.html

回到顶部