Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

提高webpack的打包速度:happypack和dll打包 #21

Open
p2227 opened this issue Oct 1, 2017 · 0 comments
Open

提高webpack的打包速度:happypack和dll打包 #21

p2227 opened this issue Oct 1, 2017 · 0 comments

Comments

@p2227
Copy link
Owner

p2227 commented Oct 1, 2017

提高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打包,需要两个插件 DllPluginDllReferencePlugin 。前者是用来打包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,我们可以在后续的时候再挖掘这些优化点。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant