Webpack优化 | 快一点,再快一点

Posted 点融黑帮

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Webpack优化 | 快一点,再快一点相关的知识,希望对你有一定的参考价值。


最近项目的开发环境升级,

引入了 js 构建工具 webpack,生产力大大提高。

开心了两个月后,随着项目代码逐日增多,

我们遇到了一个问题:构建太慢了。


慢到什么程度呢,

开发时第一次启动要40秒左右,

生产构建则需要1分多钟甚至2分钟。


看着似乎不是特别慢,

你可以试着出了 bug 后和 pm, 测试,设计小姐姐注视2分钟…



李梦南 1分钟前

人生苦短, 我用 python,一刻千金。

大好时光怎么可以把时间浪费在构建上呢?

so,快一点,再快一点!



☞分析


我们是多页面应用,拥有很多入口,构建工具需要去遍历指定文件夹内的指定 js 文件并生成对应的 html 文件出来,然后我们有 rename 的需求,即生成的 html 的文件名可以和 js 文件名不一致。


于是我们在 js 里提供了一个特定语句:

// card/index.js 
@Entry({ 
  filename: 'card_list.html' 
}) 

然后在编译时通过 babel parse 出 ast 后拿到 filename 的值,再交给 webpack 。


这样的好处是,开发时能够直观的设置文件名称,后续维护的时候也很直观的知道最终输出的文件名称。时间上的消耗在项目初期代码不是很多,js 文件不是很大的时候也还是可以接受的。但当 js 文件越来越多,越来越大,就有点让人头疼了。而且为了满足就近原则,我们会把公用的业务逻辑抽出来放在同文件夹内,就会浪费很多时间在这些非 entry 文件上。


☞结论


ast 好用,但是有点花时间。


☞加速


秉着谁拖后腿就干掉谁的原则,首先 babel parse to ast 这个我们就别要了,找个其他能满足我们需求的办法,有没有呢?当然有,而且就近在眼前。用过 gulp 的同学肯定都见过这行代码:gulp.src('client/templates/*.jade') gulp api,这里的匹配规则使用的node-glob。glob 在匹配特定文件时非常方便,且支持ignore,用来匹配我们的 entry 实在太合适不过了。下面是获取 entries 的例子:

function getEntries(dir = 'src/pages') { 
  const entries = {} 
  const root = resolve(dir) + '/' 
  const files = glob.sync(root + '**/*.js', { 
    ignore: [ 
      '**/view.js', 
      '**/_*/**',  
      '_*.js' 
    ] 
  }) 

  files.forEach((file) => { 
    entries[ 
      file 
        .replace(root, '') 
        .replace('/index.js', '') 
        .replace(/\//g, '_') 
        .replace('.js', '') 
    ] = file 
  }) 
 
  return entries 
} 

这里我们通过特定的类似 sass 中的做法约定非 entry 文件使用_前缀以忽略。并根据文件的路径生成 {dir_filename: absolutePath}  这样的 entries 给 webpack。同时,为了满足 rename 的需求,允许提供一个 router.js 文件来定义需要 rename 的 entry,类似:

// router.js 
module.exports = { 
  rename: require.resolve(filePath) 
} 

然后通过下面的方法对原始 entry 和 router 进行 merge:

function mergeEntries(entry, router) { 
  const reducer = (prev, [key, val]) => { 
    prev[val] = key 
    return prev 
  } 
  const reverse = source => Object 
    .entries(source) 
    .reduce(reducer, {}) 
 
  return reverse(reverse(Object.assign({}, entry, router))) 
} 

然后把最终的 entries 交给 webpack 就 ok 了。


再快一点


原来的 webpack 使用的是 v1.x 版本,最近 webpack 已经发布了3.x 的版本,更加的 powerful。于是我便将依赖也升级到最新的 v3.5.5,然后傻眼了。在多 entry 的情况下,webpack3 与最新的 html-webpack-plugin 配合起来并不默契。会导致构建时间几何翻倍,详细看可以看这个issue(https://github.com/jantimon/html-webpack-plugin/issues/724)。

在这个 issue 里我发现了个有意思的东西,就是 html-webpack-plugin 的 v1.7.0 版本简直不要太快,降级到 v1.7.0 后构建速度果然飞起来了,比起 2.x 都要快很多。但是它不支持chunksSortMode: dependency ,意味者当你在生产构建时使用了CommonsChunkPlugin后将无法正确的排列 js 的引入顺序。不过好在它支持 function 参数,且我们的 chunks 是固定的,便可以通过下面这样的方式使用:

Object.keys(config.entry).map((file) => { 
    const chunks = ['manifest', 'vendor', 'commons', file] 
    return new HtmlWebpackPlugin({ 
      chunks, 
      filename: file + '.html', 
      template: resolve('index.html'), 
      inject: 'head', 
      minify: { 
        removeComments: true, 
        collapseWhitespace: true, 
        removeAttributeQuotes: true 
      }, 
      chunksSortMode: (a, b) => chunks.indexOf(a.names[0]) - chunks.indexOf(b.names[0]) 
    }) 
  }) 

到这里,其实速度已经可以了,但不容易满足的我岂会到这里就结束。

之前曾在淘宝 FED 看过一篇讲happypack的文章,happypack 是 webpack 的一个插件,目的是通过多进程模型,来加速代码构建,具体原理可以看这里。

happpack 的使用方法很简单:

// webpack config 
const HappyPack = require('happypack') 
const os = require('os') 
 
const config = { 
  module: { 
    rules: [ 
      { 
        test: /\.js$/, 
        loader: 'happypack/loader?id=js' 
      } 
    ] 
  }, 
  plugins: [ 
    new HappyPack({ 
      id: 'js', 
      // 创建一个线程池共享给所有的 HappyPack 实例 
      threadPool: HappyPack.ThreadPool({ size: os.cpus().length }), 
      loaders: [{ 
        path: 'babel-loader', 
        query: { 
          cacheDirectory: true 
        } 
      }] 
    }) 
  ] 
} 

至此为止,本次对构建速度的优化就结束了,效果如何呢?开发时 1st 构建可以控制在 10s 左右,生产构建在 uglify + gzip 的情况下 1st 构建:


Webpack优化 | 快一点,再快一点


有 cache 的情况下改动单个文件:


Webpack优化 | 快一点,再快一点


可以说是开上飞机啦!



结语


其实 webpack 还有一些优化空间

比如 dllPlugin


不过目前的速度我已经很满足了

等到遇到下一个瓶颈时再去研究吧


如果你有更好的优化方法

欢迎分享评论

Webpack优化 | 快一点,再快一点


点击回顾往期精彩内容













以上是关于Webpack优化 | 快一点,再快一点的主要内容,如果未能解决你的问题,请参考以下文章

走快点,再快一点

netty系列之:让TCP连接快一点,再快一点

简单12招让Hive运行快一点,再快一点

多线程预加载,让网站打开速度再快一点!

怎么使eclipse中Java代码运行快一点

6.15项目心得体会