webpack4.x迁移-生产环境篇

560 阅读4分钟

 之前做过开发环境的webpack4.x迁移,但是由于一些原因后续生产的配置改造搁置了...近期正在着手处理这块,本文是对迁移过程的记录分享,包括一些新版本的处理方式和实际改写存在的问题。

  想看之前开发环境配置记录的可以走该传送门

  下面开始正文...

踩坑

mode

  上次的分享中有讨论过在新版本的webpack内,有约定大于配置一说,但是当时我配置的部分内容其实是有问题的(主要是没考虑production,逃)。process.env.NODE_ENV在默认情况下将会得到development,在生产的config文件内,我们可以通过直接设置mode属性进行merge,它会被关联到process.env.NODE_ENV上。

let prodConfig = merge(baseConfig, {
    mode: 'production',
    // ...
});

  老版本可以像下面这样设置:

new webpack.DefinePlugin({
  'process.env.NODE_ENV': JSON.stringify('production')
})

url-loader

  在配置生产环境打包文件的过程中,我发现之前处理文件输出的loader写法也存在问题。因为最终我们的静态资源都要放在一个dist目录下,并且通常是不会改变的(请结合自身实际业务场景)。那我需要的就是将url()引到的文件,原封不动地输出到dist中。之前的写法是怎么样的呢?

{
    loader: 'url-loader',
    options: {
        limit: 1024,
        outputPath: 'images'
    }
}

  这样处理会有两个问题:

  1. 文件打包时输出路径并不在dist下。我们调整时,需关注output中的path设置。
  2. 没有配置name。该属性会指定文件输出时的名称,缺省状态下会生成一串哈希值(不包含原文件名)。

  改写:

{
    loader: 'url-loader',
    options: {
        limit: 1024,
        name: '[name].[ext]', // 原文件名.后缀  等价于输出原文件名
        outputPath: './'  // 结合path 定位输出目录为dist
    }
}

DllReferencePlugin

  这是一个归属于webpack下的插件,通过如下方式配置,会检视manifest.json中的映射关系略过已被处理的模块。与开发环境配置一文中的处理相同。

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

CleanWebpackPlugin

  用于清理文件目录的插件,通常我们会在重新打包编译前清空你存放部署文件的目录,比如我们的dist。引入方式务必注意,在webpack4.x版本中,我们须要通过const { CleanWebpackPlugin } = require('clean-webpack-plugin')的方式引用。

  前文我们有讨论会先打一个dll出来,而配置dll时,我们已经清理了一次目录,在build时,我们同样需要再清理一次之前可能打包过的旧内容,但像vendor.dll.jsvendor.manifest.json之类的dll生成内容需要保留,我们可以结合该插件提供的生命周期属性cleanOnceBeforeBuildPatterns介入:

new CleanWebpackPlugin({
    cleanOnceBeforeBuildPatterns: ['**/*', '!vendor.dll.js', '!vendor-manifest.json', '!vendors~pdfjsWorker.dll.js'], // 数组格式,通过!保留你要的内容,第一个参数表明当前目录
})

CopyWebpackPlugin

  当然,我们可能有些静态资源不是通过url引用的,须要我们手动输出到dist下,可以通过CopyWebpackPlugin插件拷贝过去:

new CopyWebpackPlugin([
    {
        from: './public/',
        to: './'
    }
])

HtmlWebpackPlugin

  这个跟开发环境的配置也类似,不过我们部署时须写入文件,并且要适当减小体积,操作如下:

new HtmlWebpackPlugin({
    template: path.resolve(__dirname, './src/index.ejs'),
    filename: 'index.html',
    alwaysWriteToDisk: true,  // 写入磁盘
    chunks: ['vendor', 'index'],  // 配置取决于你的分块内容,有分块加vendor
    minify: {
        removeComments: true,   // 移注释
        collapseWhitespace: true,   // 移空格
        removeAttributeQuotes: true // 移引号
    }
})

optimization压缩优化

  过去版本的webpack常采用UglifyJsPlugin进行代码压缩。在webpack4.x中则改为通过在optimization下配置minimizer的方案实现:它接收一个数组,里面是我们使用的插件。通常都是TerserPlugin

  optimization: {
      minimize: true,
      minimizer: [
          new TerserPlugin({
              terserOptions: {
                  output: {
                      comments: false, // 配合下面的extractComments,移除代码中注释
                      compress: {}, // 默认 设置false可以跳过压缩环节 想自己定制 见 https://github.com/terser/terser#compress-options
                  },
              },
              // sourceMap: true,
              extractComments: false, // 不单独提取/^\**!|@preserve|@license|@cc_on/i规则的注释,并生成xx.js.LICENSE
              cache: true,  // 开启缓存
              parallel: true, // 是否并发 设置为true 并发数为 os.cpus().length - 1 即你的内核数 - 1 也可以手动指定数字
          }),
      ]
  }

stats

  这个属性是用来控制命令行输出统计内容的,默认情况下我们会看到一堆输出内容,非常冗余。事实上,我们只想看到最后输出了哪些文件以及报错时是什么问题,下面是我的配置:

stats: {
    all: false, // 不输出全部信息
    assets: true, // 输出最后的打包文件
    errors: true, // 遇到错误时,输出内容
    warnings: false,  // 静默warning
    moduleTrace: true,  // 遇到错误时,定位文件
    errorDetails: true, // 输出具体错误
}

比较

  以上大致就是本人迁移中遇到的一些问题,下面贴一下操作前后的比较图...

  原本脚手架自带的打包:

  这里可能有同学会问这个File sizes after gzip是什么,打出来的体积就是gzip压缩体积吗?其实不是的,它只是在react-dev-utils库中的一些方法帮助下计算了文件处理后的体积,并非是真实进行了处理。是否开启gzip需要服务器的处理。

  迁移魔改后:

  1. dll耗时:

  1. 生产build

  60s -> 20s ?

  溜了溜了.