Webpack 在 build 包的时候,有时候会遇到打包时间很长的问题,这里提供了一个解决方案,让打包如丝般顺滑。

1. 介绍

在用 Webpack 打包的时候,对于一些不经常更新的第三方库,比如 reactlodashvue 我们希望能和自己的代码分离开,Webpack 社区有两种方案:

  • CommonsChunkPlugin
  • DLLPlugin

对于 CommonsChunkPlugin,Webpack 每次打包实际还是需要去处理这些第三方库,只是打包完之后,能把第三方库和我们自己的代码分开。而 DLLPlugin 则是能把第三方代码完全分离开,即每次只打包项目自身的代码。Dll 这个概念是借鉴了 Windows 系统的 dll。一个 dll 包,就是一个纯纯的依赖库,它本身不能运行,是用来给你的app引用的。

2. 模板 webpack-simple 用法

要使用 DLLPlugin,需要额外新建一个配置文件。所以对于用这种方式打包的项目,一般会有下面两个配置文件:

  • webpack.config.js
  • webpack.dll.config.js

在项目根目录新建一个文件 webpack.dll.config.js

const path    = require('path');
const webpack = require('webpack');
module.exports = {
  entry: {
      vendor: ['vue-router','vuex','vue/dist/vue.common.js','vue/dist/vue.js','vue-loader/lib/component-normalizer.js','vue']
  },
  output: {
    path: path.resolve('./dist'),
    filename: '[name].dll.js',
    library: '[name]_library'
  },
  plugins: [
    new webpack.DllPlugin({
      path: path.resolve('./dist', '[name]-manifest.json'),
      name: '[name]_library'
    })
  ]
};

这是把用到的第三方插件添加到 vendor 中。然后在 webpack.config.js 中添加代码

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

再在入口 html 文件中引入 vendor.dll.js
<script type="text/javascript" src="./../vendor.dll.js"></script>

然后在 package.json 文件中添加快捷命令(build:dll)。

"scripts": {
    "dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot",
    "build": "cross-env NODE_ENV=production webpack --progress --hide-modules",
    "build:dll": "webpack --config webpack.dll.config.js"
  },

最后打包的时候首先执行 npm run build:dll 命令会在打包目录下生成 vendor-manifest.json 文件与 vendor.dll.js 文件。打包 dll 的时候,Webpack 会将所有包含的库做一个索引,写在一个 manifest 文件中,而引用 dll 的代码(dll user)在打包的时候,只需要读取这个 manifest 文件,就可以了。

再执行npm run build
发现现在的 Webpack 打包速度为 2,3 秒左右,与之前的 20 秒左右快了很多。

3. 模板webpack 用法

build 下创建 webpack.dll.config.js

内容:

const path = require('path')
const webpack = require('webpack')
module.exports = {
  entry: {
    vendor: [
      'vue-router',
      'vuex',
      'vue/dist/vue.common.js',
      'vue/dist/vue.js',
      'vue-loader/lib/component-normalizer.js',
      'vue',
      'axios',
      'echarts'
    ]
  },
  output: {
    path: path.resolve('./dist'),
    filename: '[name].dll.js',
    library: '[name]_library'
  },
  plugins: [
    new webpack.DllPlugin({
      path: path.resolve('./dist', '[name]-manifest.json'),
      name: '[name]_library'
    }),
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        warnings: false
      }
    })
  ]
}

建议加上代码压缩插件,否则 dll 包会比较大。

在 webpack.prod.conf.js 的 plugin 后面加入配置

new webpack.DllReferencePlugin({
    manifest: require('../dist/vendor-manifest.json')
})

根目录下的入口 index.html 加入引用
<script type="text/javascript" src="./vendor.dll.js"></script>

package.json script 里加入快捷命令
"build:dll": "webpack --config build/webpack.dll.config.js"

要生成 dll 时运行npm run build:dll,即生成 dist 目录下两个文件 vender-manifest.jsonvender.dll.js
然后正式生成 prod npm run build:prod,即生成除webpack.dll.config.js中指定包之外的其他打包文件。

在尝试在 vue-element-admin 中引入 DllPlugin 时,加入 20 个打包项,测试结果:
原来的打包时间——

引入 DllPlugin 后的打包时间——

可以看到大幅缩短了打包时间。

4. 另一种方法 externals 选项

也可以使用 externals 让 Webpack 不打包某部分,然后在其他地方引入 cdn 上的 js 文件,利用缓存下载 cdn 文件达到减少打包时间的目的。
配置 externals 选项

// webpack.prod.config.js
// 多余代码省略
module.exports = {
    externals: {
        'vue': 'window.Vue',
        'vuex': 'window.Vuex',
        'vue-router': 'window.VueRouter'
        ...
    }
}

// 配置externals之后,webpack不会把配置项中的代码打包进去,别忘了需要在外部引入cdn上的js文件
// html
<body>
    <script src="XXX/cdn/vue.min.js"></script>
    ......
</body>