提高 webpack 的打包速度 happypack 和 dll 打包

发布于 2022-03-03 14:30:10 字数 6788 浏览 1115 评论 0

本人一直在用 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,我们可以在后续的时候再挖掘这些优化点。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

JSmiles

生命进入颠沛而奔忙的本质状态,并将以不断告别和相遇的陈旧方式继续下去。

0 文章
0 评论
84961 人气
更多

推荐作者

qq_aHcEbj

文章 0 评论 0

寄与心

文章 0 评论 0

13545243122

文章 0 评论 0

流星番茄

文章 0 评论 0

春庭雪

文章 0 评论 0

潮男不是我

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文