webpack踩坑记——DllPlugin和DllReferencePlugin

场景描述:

使用vue-cli官方提供的webpack项目,进行钉钉移动端的开发,项目中期遇到文件限制大小的问题,钉钉要求一个js不能超过5MB,而npm run dev的时候,并不会把app.js进行拆分,所以所有的依赖库都在app.js里,太大了。
于是就有了这个需求————在dev环境里,也把依赖库拆出来。

方案一:

npm run build里的方法拷贝过来,其实就是CommonsChunkPlugin

原配置里的写法是把node_modules里的库都打包到vendor.js里,这个方法里可以自己配置,返回个布尔值就可以了。
拷贝过来以后,发现app.js是变小了,但是vendor.js巨大,5.2mb,还是超了,而且,理论上这些库(vue全家桶、mint、g2)加起来也不应该这么大呀。

方案二:

把依赖的库,先整体都不作处理地打包出来,剩余自己的app.js还是该怎么样怎么样,webpack给我们提供了这么个插件DllPlugin

新建一个配置文件,比如build/webpack.dll.conf.js

const path = require('path')
const webpack = require('webpack')
module.exports = {
  entry: {
    vendor: ['vue','vue-router']
  },
  output: {
    path: path.join(__dirname, '../static'),
    filename: 'dll.[name].js',
    library: '[name]'
  },
  plugins: [
    new webpack.DllPlugin({
      path: path.join(__dirname, '../', '[name]-manifest.json'),
      name: '[name]'
    })
  ]
}

entry里就是先打包出来的库,output就是输出地址和名字,输出到static,因为原配置已经会把static里的内容直接复制到dist里,就不去折腾改其他地方了。
Dllplugin里的path,会输出一个vendor-manifest.json,这是用来做关联id的,打包的时候不会打包进去,所以不用放到static里
然后运行一下webpack -p --progress --config build/webpack.dll.conf.js
成功以后,static下会有dll.vendor.js,根目录下会有vendor.manifest.json
各自打开看一下,就会看到依赖库的源码和匹配id

ok,到这里,抽离依赖库的事情就完成了,那么接下来问题就是怎么引用呢,怎么在dev和build跑呢?

这里补了一点dll和commonsChunk概念上的区别,commonsChunk之所以慢和大,是因为每次run的时候,都会去做一次打包,而实际上我们不会一直去更新我们引用的依赖库,所以dll的做法就等于是,事先先打包好依赖库,然后只对每次都修改的js做打包。

继续刚才的步骤

修改build/webpack.base.conf.js,添加DllReferencePlugin的配置

const manifest = require('../vendor-manifest.json')
。。。。
plugins: [
    new webpack.DllReferencePlugin({
      manifest
    })
  ]

然后我们直接打开index.html,在底部加上<script src="./static/dll.vendor.js"></script>
是的,就是这么简单粗暴。
运行一下npm run dev,打开f12看看网络监控,一切顺利的话,这样就ok了

接下来是打包,只需要把原来的commonsChunkPlugin的东西删掉就可以了。
npm run build --report
可以感受到速度比原来快了不是一点点

继续优化

  1. 依赖从package.json读取,而不是手写;
  2. 带上hash,以免线上缓存问题;
  3. 带上hash后,自动修改index.html里的引用;
  4. 命令太长了,最好yarn run一下就好了
const path = require('path')
const webpack = require('webpack')
const package = require('../package.json')
const AssetsPlugin = require('assets-webpack-plugin')
//读取package.json里的依赖,normalize.css除外,打包会报错
const package = require('../package.json')
let dependencies = Object.keys(package.dependencies) || []
//如果使用了chrome的vue-devtool,那打包的时候把vue也排除掉,因为压缩过的vue是不能使用vue-devtool的
dependencies = dependencies.length > 0 ? dependencies.filter(item => item !== 'vue') : []

module.exports = {
  entry: {
    vendor: dependencies
  },
  output: {
    path: path.join(__dirname, '../static'),
    filename: 'dll.[name]_[hash:6].js',
    library: '[name]_[hash:6]'
  },
  plugins: [
    new webpack.DllPlugin({
      path: path.join(__dirname, '../', '[name]-manifest.json'),
      name: '[name]_[hash:6]'
    }),
    new AssetsPlugin({
      filename: 'bundle-config.json',
      path: './'
    })
  ]
}

在build文件夹新建一个dll.js,读一下配置,跑一下webpack,输出点日志。

var ora = require('ora')
var chalk = require('chalk')
var webpack = require('webpack')
var webpackConfig = require('./webpack.dll.conf')

var spinner = ora('building for dependencies...')
spinner.start()

webpack(webpackConfig, function (err, stats) {
  spinner.stop()
  if (err) throw err
  process.stdout.write(stats.toString({
    colors: true,
    modules: false,
    children: false,
    chunks: false,
    chunkModules: false
  }) + '\n\n')

  console.log(chalk.cyan('  Build complete.\n'))
})

保存以后,到package.json里添加命令,"dll": "node build/dll.js"
运行 yarn dll 完成~

代码不在这里赘述,可以看我的 github

总结

没有最好的配置,只有最适合的,遇到问题,思考问题,解决问题

参考

  1. https://doc.webpack-china.org...
  2. http://engineering.invisionap...
  3. https://segmentfault.com/a/11...
阅读 23k

推荐阅读