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

1. 介绍

在用 Webpack 打包的时候,对于一些不经常更新的第三方库,比如 react,lodash,vue 我们希望能和自己的代码分离开,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。

image-58

内容:

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.json 与 vender.dll.js。
然后正式生成 prod npm run build:prod,即生成除webpack.dll.config.js中指定包之外的其他打包文件。

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

image-59

引入 DllPlugin 后的打包时间——

image-60

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

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>