I recently decided to write this article because I felt I needed to share my experiences with the process of upgrading flecle-CLI to webpackage 4. Don’t know fle- CLI? See here)

Webpack4 is a big trend and upgrading is inevitable, so why upgrade now?

The reasons are as follows:

  • The newly released version is unstable and potentially risky, but the current version has been updated to 4.8.3, which is basically stable.
  • The WebPack community tools are not fully up to speed, and many tools have to be built by themselves.
  • Webpack itself and the community tools have more or less problems that have not settled over time and are expensive to maintain.

Now, however, I think it’s time for a wave of upgrades.

preface

This article will not cover every detail of the WebPack configuration, as these official documents are available. The author will select some difficult new concepts, problems that may be encountered, as well as the optimization scheme summarized by the author to share, hoping to bring some help to you.

configuration

mode

Mode is a new parameter in Webpack4. It has three values: development, production, and None. It can help us load some default configurations. The default configurations are listed below for your reference to avoid repeated configurations.

development

Focus on improving code build speed and development experience

module.exports = {
  cache: true.devtools: "eval".plugins: [
    new webpack.NamedModulesPlugin(),
    new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("development")]}})Copy the code

prodution

Provides code optimizations such as compression, scoping, etc

var UglifyJsPlugin = require('uglifyjs-webpack-plugin')

module.exports = {
  plugins: [
    new UglifyJsPlugin(/ *... * /),
    new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("production")}),new webpack.optimize.ModuleConcatenationPlugin(),
    new webpack.NoEmitOnErrorsPlugin()
  ]
}
Copy the code

optimization

This option is also new to webpack4, mainly to customize some optimized packaging policies.

minimizer

In Production mode, this configuration will default to compress the obfuscated code, but this clearly does not satisfy our desire to optimize the code. Below, the author shares a set of configuration and explanation summarized from his own practice:


var UglifyJsPlugin = require('uglifyjs-webpack-plugin')
var OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')

module.exports = {
  optimization: {
    minimizer: [
      // A custom js optimization configuration will override the default configuration
      new UglifyJsPlugin({
        exclude: /\.min\.js$/.// Filter out files that end with ".min.js". We think that the suffix itself is already compressed code, so there is no need for secondary compression
        cache: true.parallel: true.// Enable parallel compression to make full use of CPU
        sourceMap: false.extractComments: false.// Remove comments
        uglifyOptions: {
          compress: {
            unused: true.warnings: false.drop_debugger: true
          },
          output: {
            comments: false}}}),// Used to optimize CSS files
      new OptimizeCssAssetsPlugin({
        assetNameRegExp: /\.css$/g.cssProcessorOptions: {
          safe: true.autoprefixer: { disable: true }, // There is a big pit, which will be mentioned later
          mergeLonghand: false.discardComments: {
            removeAll: true // Remove comments}},canPrint: true}}})]Copy the code

UglifyJsPlugin is a plugin that I believe is often used, and I will not say more here. The highlight here is to filter out js files that are already compressed, which can improve our compilation efficiency and avoid unknown bugs caused by secondary confusion compression.

OptimizeCssAssetsPlugin this plugin is used to optimize the output of the CSS file, use the default cssnano, its optimization strategy mainly includes: reject repeat style definition, cut off excess parameters in style rules and removes unwanted browser prefix, more optimization rules here. We mentioned that there was a big hole, and I’m sure you’ve noticed that, yes, this is the one that prefixes us with Autoprefixer. Autoprefixer: {disable: true} disables cssnano’s processing of browser prefixes.

runtimeChunk

The benefit of separating the runtime code from WebPack, which we previously called the manifest code block, is that it makes it easier to do persistent caching of files. It can set multiple types of values, as you can see here, where single packs all chunk’s running code into a file, and multiple packs each chunk’s running code into a file.

We can cooperate InlineManifestWebpackPlugin plug-in will run code inserted into HTML file directly, because this code is very few, this can avoid the cost of a request, but the new plug-in configuration and some are not quite same, before the next explain in detail how to configure.

var HtmlWebpackPlugin = require('html-webpack-plugin')
var InlineManifestWebpackPlugin = require('inline-manifest-webpack-plugin')

module.exports = {
  entry: {
    app: 'src/index.js'
  },
  optimization: {
    runtimeChunk: 'single'
    / / equivalent to the
    // runtimeChunk: {
    // name: 'runtime'
    // }
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: 'fle-cli'.filename: 'index.html'.template: 'xxx'.inject: true.chunks: ['runtime'.'app'].// Insert runtime into HTML
      chunksSortMode: 'dependency'.minify: {/ * * /}}),new InlineManifestWebpackPlugin('runtime')]}Copy the code

This configuration will produce a called the runtime code block, and the old version, we don’t need to added in the HTML template < % = htmlWebpackPlugin. Files. WebpackManifest % >, only need to add the runtime chunks. There is a point to note that InlineManifestWebpackPlugin plug-in order must after HtmlWebpackPlugin, otherwise it will cause the failure of compilation.

splitChunks

Finally, the big story, which I personally find the most difficult to understand. Webpack4 removes CommonsChunkPlugin and replaces it with splitChunks.

Let’s take a look at the default configuration:

splitChunks: {
  chunks: "async",
  minSize: 30000,
  minChunks: 1,
  maxAsyncRequests: 5,
  maxInitialRequests: 3,
  automaticNameDelimiter: '~',
  name: true,
  cacheGroups: {
    vendors: {
      test: /[\\/]node_modules[\\/]/,
      priority: -10
    },
    default: {
      minChunks: 2,
      priority: -20,
      reuseExistingChunk: true}}}Copy the code

The default configuration only applies to asynchronously loaded blocks of code, which limits the minimum size of the split file to 30KB (note that this size is before compression), and then it has two groups: belonging to the node_modules module, or referenced by at least two entry files before it is packaged as a separate file.

Why limit the minimum volume? Webpack thinks it’s too expensive to separate a block of code smaller than 30KB and have to spend an extra request to load it. Of course, this value is not arbitrary, but is based on a lot of practice, which I personally think is a good strategy.

MaxAsyncRequests (the maximum number of asynchronous requests) and maxInitialRequests (the maximum number of initial requests) are used to limit how detailed blocks of code can become, resulting in large numbers of file requests.

However, separating asynchronous blocks alone is obviously not enough for our needs, so I share a relatively elegant separation packaging configuration:

splitChunks: {
  cacheGroups: {
    vendors: {
      test: /[\\/]node_modules[\\/]/,
      name: 'vendors',
      minSize: 30000,
      minChunks: 1,
      chunks: 'initial', priority: 1 // This configuration item sets the priority of the processing. The higher the value is, the higher the priority is}, Commons: {test: /[\\/]src[\\/]common[\\/]/,
      name: 'commons',
      minSize: 30000,
      minChunks: 3,
      chunks: 'initial',
      priority: -1,
      reuseExistingChunk: true// This configuration allows us to use existing code blocks}}}Copy the code

The first is to separate out the node_modules module, which I won’t bother to cover. Asynchronously loaded modules will inherit the default configuration, so we don’t need a second configuration here.

The second point is to separate the shared modules. I believe that for an elegant project structure, the common code (or reusable code) should be placed in the same root directory, so we can extract the common code in SRC /common.

Another option is to extract files with the.js suffix that have been used more than 3 times, but I don’t recommend this because it is not conducive to persistent caching, and adding or deleting files may affect the number of times used and cause the original public file to become invalid.

At the end of the article

Originally also want to talk about the CSS plug-in part of the configuration, limited to space, this article will not explain the explanation, interested in small brother small sister can look at the source code here: Webpack4-test.

By the way, we recommend a good global general purpose scaffolding fle CLI: it aims to help us free from the complicated compilation and configuration, wholeheartedly into the business development, improve the development efficiency; It is also a true global scaffold, unlike other global scaffolders on the market. It does not generate a variety of compile configuration files in your project, nor does it install a series of compile dependencies for you, which means your project can be very clean and pure.