基于 vue-cli 的 webpack 打包之第三方库优化

由于加入了数个较为庞大的库,导致vendor.js 达到了恐怖的8M, 为此参考了网上的不少优化资料,并查阅官方文档进行一定的修正,得出一个较为满意的方案。
首先优化方案有两种 CDN和 DLL。

CDN 方案

该非常直接,粘贴到index.html模板中, 在build/webpack.base.conf.js 加上externals: { 'jquery': 'Jquery' }, 使用时和正常安装的没有区别, webpack打包时并不会引入该包。
CDN方案具体就不多介绍,因为这个方案在深思熟虑后被放弃了,因为别人的 CND无法放心,可能会极小可能访问出现问题,而且包过多时请求数暴涨会引起页面加载的缓慢。

DLL 方案

此方案属于对vendor.js文件打包的再次优化,有目标的合并较小的包,较大的包单独存在。
执行此方案前需要知道哪些包占了vendor.js文件较大的位置,可以独立出来,此时可以在package.json加上新的运行命令(使用了vue-cli内置的webpack-bundle-analyzer)

"build:sit-preview": "NODE_ENV=production env_config=sit npm_config_preview=true npm_config_report=true node build/build.js"

通过webpack-bundle-analyzer我们可以观察到包的组成情况dll的配置
 
接下来开始优化,在config/index.js文件中新增一个dll配置对象

   dll: {
    entry: {
      vue: [
        'vue/dist/vue.esm.js',
        'vue-router',
        'vuex'
      ],
      ui: [
        'element-ui'
      ],
      echarts: [
        'echarts'
      ],
      utils: [
        'axios',
        'lodash',
        'jquery',
        'moment'
      ],
      other: [
        'perfect-scrollbar'
      ]
    },
    outputPath: path.resolve(__dirname, '../dist/dll'),
    publicPath: '/dll/'
  }

 
新增一个build/webpack.dll.conf.js文件, 需要先安装webpack-manifest-plugin库支持,该库为dll文件生成了一个文件列表

const path = require('path')
const webpack = require('webpack')
const ManifestPlugin = require('webpack-manifest-plugin')
const config = require('../config').dll
const webpackConfig = {
  entry: config.entry,
  output: {
    path: config.outputPath,
    filename: '[name].dll.[chunkhash].js',
    library: '[name]_library',
    publicPath: config.publicPath
  },
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      },
      {
        test: /\.js$/,
        loader: 'babel-loader',
        exclude: /node_modules/
      }
    ]
  },
  plugins: [
    new webpack.optimize.ModuleConcatenationPlugin(),
    new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /zh-cn|en-gb/),
    new webpack.DllPlugin({
      path: path.join(config.outputPath, '[name]-manifest.json'),
      libraryTarget: 'commonjs2',
      name: '[name]_library'
    }),
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        warnings: false
      }
    }),
    new ManifestPlugin({
      fileName: path.join(config.outputPath, 'manifest.dll.json')
    })
  ]
}
module.exports = webpackConfig

 
新增一个build/build-dll.js文件,其作用是运行前面配置好的打包dll文件

'use strict'
process.env.NODE_ENV = 'production'
const ora = require('ora')
const rm = require('rimraf')
const path = require('path')
const chalk = require('chalk')
const webpack = require('webpack')
const config = require('../config').dll
const webpackConfig = require('./webpack.dll.conf')
const spinner = ora('开始构建 dll...')
spinner.start()
rm(path.resolve(__dirname, config.outputPath), err => {
  if (err) throw err
  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')
    if (stats.hasErrors()) {
      console.log(chalk.red('  构建发生了错误.\n'))
      process.exit(1)
    }
    console.log(chalk.cyan('  构建完成.\n'))
    console.log(chalk.yellow(
      '  提示:当第三方库升级或变动时,请重新构建dll .\n'
    ))
  })
})

 
此时只需要在package.json文件中增加一个script方法

 "build:dll": "node build/build-dll.js"

运行yarn run build:dll可以在dist/dll文件夹中看到打包的dll文件
接下来让build项目时引入dll文件,让其打包时不引入dll已经打包的库
build/utils.js中增加一个辅助方法, 用于快速获得dll文件的路径

exports.dllPath = function (_path) {
  const outputPath = config.dll.outputPath
  return path.posix.join(outputPath, _path)
}

 
接下来修改build/webpack.prod.conf.js文件

// 获得dll文件的列表
const manifestDll = require(utils.dllPath('manifest.dll.json'))

HtmlWebpackPlugin插件中加一行

dll: manifestDll,

DllReferencePlugin插件注入到webpack,由于有多个 dll 文件,采用了自执行的匿名循环新建插件实例,运用扩展运算符写入到webpackConfig

  ...(function () {
      let res = []
      let keys = Object.keys(config.dll.entry)
      keys.forEach(key => {
        res.push(new webpack.DllReferencePlugin({
          context: path.resolve(__dirname, '../'),
          manifest: require(utils.dllPath(`${key}-manifest.json`))
        }))
      });
      return res
    })()

 
最后只需要在index.html模板中添加dll打包文件

 <!-- dll files will be auto injected -->
 <% for (var name in htmlWebpackPlugin.options.dll) { %>
   <script type="text/javascript" src="<%= htmlWebpackPlugin.options.dll[name] %>"></script>
 <% } %>

 

在升级第三方库的情况下,dll文件可以以一直使用,不需要打包。
现在运行build速度飞快,webpack不用每次都去重复打包第三方库了

发表评论