Webpack is a must-have skill for our front-end engineers, and our daily development has become dependent on this configuration. There have been a lot of articles about this, but I still want to record my learning process. Based on the webPack construction principle introduced in the previous article, this article will cover two directions that can be optimized in our actual engineering configuration.

  • Increasing build speed, which means reducing the time it takes to complete a package build,
  • Optimizing the build output means reducing the file size of our final build output.

1. Build faster

1.1 Which stages can speed up?

Let’s review the entire construction process first. First, module instances of all files are generated recursively from the entry files. Then, the dependency of all Module instances is analyzed and optimized, and the module instances are divided into one or more chunks to generate the final output of packaged files. So what are the stages where we can save time?

  • The RESOvle phase: Gets the absolute path where the file is and which loaders are compiling and converting the file
  • The run-loader phase: executes the corresponding loaders to compile and convert the file
  • The parse phase: resolves whether a file has dependencies and the corresponding dependent files.

In this process, we can save time in the direction of:

  • Resolve phase: Reduce the time to find the absolute path to a file
  • Run-loader phase: Reduces the number of files to be compiled and converted by the loader

1.2 How do I Configure it?

  • Resolve phase
Resolve: {// Create an alias for common modules in the SRC folderalias: {
      xyz$: path.resolve(__dirname, 'path/to/file.js')
    },
    modules: ['node_modules'], // Reduce the extensions of the search scope: ['.js'.'.json'], // import files are not suffixed, webpack will find mainFields according to the extensions defined in the extensions: ['loader'.'main'], // Reduce the search step for entry files, set the location of entry files for third-party modules},Copy the code
  • Make full use of aliases and configure paths for aliases to accurately match corresponding file paths, reducing file search time
  • Configure modules to reduce the scope of file lookup
  • The extensions should be fully used. Webpack will traverse this configuration item when there is no suffix information in the introduced file, and then add the suffix in the configuration item array in order to find the matching file. Therefore, it is better to add file suffixes in our daily development, so we can omit the step of adding the suffix search. Or we could put the high frequency file suffix in front of the array to save time by reducing the number of iterations
  • The run – loader stages
 module: {
    rules: [
      {
        test: /\.js$/, // Matching file use:'babel-loader'Exclude: file => (/node_modules/.test(file) &&!) // Exclude: file => (/node_modules/.test(file) &&! /\.vue\.js/.test(file) ) } ] }Copy the code

In this phase, make full use of the include and exclude configuration items to limit files that need to be converted by the Loader to a certain range, or filter out files that do not need to be executed by the Loader.

2. Optimize build output

The final output package volume can be reduced in the following directions:

  • tree-shaking
  • Code compression

2.1 the Tree – shaking

Tree-shaking means weeding out code we don’t use to reduce the total packaging volume. In webpack4.0, tree-shaking is enabled by default. Let’s see how tree-shaking works

// a.js
import {
  add
} from './b.js'

add(1, 2)
Copy the code
// b.js
export function add(n1, n2) {
  return n1 + n2
}

export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
Copy the code

We can see that the square and cube methods in B. js are not used. We can enable usedExports configuration in development mode.

 mode: 'development'.optimization: {
   usedExports: true
 }
Copy the code

The final packing results of B. Js are as follows:

/ * * * / "./src/chunk/b.js":
/ *! * * * * * * * * * * * * * * * * * * * * * * * *! * \! *** ./src/chunk/b.js ***! A \ * * * * * * * * * * * * * * * * * * * * * * * * /
/ *! exports provided: add, square, cube */
/ *! exports used: add */
/ * * * / (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a".function() { return add; });
/* unused harmony export square */
/* unused harmony export cube */
// import('./d.js').then(mod => mod(100, 11))

function add(n1, n2) {
  return n1 + n2
}

function square(x) {
  return x * x;
}

function cube(x) {
  return x * x * x;
}
Copy the code

The square and cube functions are marked /**unused Harmony export **/. Webpackage 4.0 is tree-shaking minification, which is enabled by default in production mode with this tag set.

Of course, he has a few caveats, otherwise many times we might find treeshaking ineffective

  • Using ES6 modularity syntax (import/esport)

It is because of the static nature of the ES6 module that static analysis of dependencies is made possible by the above /** harmony **/ tag, so webpack4.0 treeshaking must be based on the ES6 modular syntax.

  • Avoid converting ES6 modular syntax to commonJs syntax through Babel compilation
  • In the project package.json file, add a “sideEffects: False” property.

Inform Webpack that all code does not contain Side effect, and it is safe to delete the unused export. Of course, we can also configure the specified file without Side effect

  "sideEffects": [
    "./src/some-side-effectful-file.js"
  ]
Copy the code
  • Enable code compression, ES6 modular syntax to tree-shaking

In production mode, code compression minification is turned on by default, and the TerserPlugin is loaded by default. You can also replace the default plug-in with another plug-in that can remove unreferenced code, such as UglifyJsPlugin

const UglifyJsPlugin = require('uglifyjs-webpack-plugin');

module.exports = {
  optimization: {
    minimizer: [new UglifyJsPlugin()],
  },
};
Copy the code

2.2 Code Compression

  • Js code compression

Js code compression is enabled by default in webPack 4.0 production environments,

module.exports = {
  optimization: {
    minimizer: [
      new TerserPlugin({
        test: /\.js(\? . *)? $/i.cache: true.parallel: true,}),],},};Copy the code

Note here that Webpack changed the default compression plugin from uglifyjs-webpack-plugin to teser-webpack-plugin in v4.26.0 (github.com/webpack/web…). Because uglifyjs-webpack-plugin uses uglify-ES which is no longer maintained, Teaser is a branch of it.

  • CSS code compression

Currently webpack4 does not have any built-in CSS optimizations, but webpack5 will reportedly have CSS file compression built in. Compression is currently available using the ‘mini-CSS-extract-plugin’ plug-in.

const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
  plugins: [
    new MiniCssExtractPlugin({
      // Options similar to the same options in webpackOptions.output
      // both options are optional
      filename: "[name].css",
      chunkFilename: "[id].css"
    })
  ],
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              // you can specify a publicPath here
              // by default it use publicPath in webpackOptions.output
              publicPath: '.. / '}},"css-loader"}]}}Copy the code

Take a look at another demo from the official documentation, which combines COMPRESSION of JS and CSS.

const TerserJSPlugin = require("terser-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
module.exports = {
  optimization: {
    minimizer: [
      new TerserJSPlugin({}),
      new OptimizeCSSAssetsPlugin({})
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: "[name].css".chunkFilename: "[id].css"})].module: {
    rules: [{test: /\.css$/.use: [
          MiniCssExtractPlugin.loader,
          "css-loader"}]}}Copy the code

2.3 unpacking

With tree-shaking and code compression mentioned above, is the packaged volume optimized to the maximum? Asynchronous loading appears… For example, vue-router supports asynchronous routing. The whole idea is to pack all the resources needed to get to the front page into one chunk, with the rest referenced asynchronously and loaded as needed.

// a.js
import('./c').then(del= > del(1.2))
Copy the code

Review the unpacking principle we talked about in principle. When asynchronous chunkgroup is divided, the C. js will be separately packaged into a chunk output. The final package results are as follows:

(window["webpackJsonp"] = window["webpackJsonp"] || []).push([[0] and {/ * * * / "./src/chunk/c.js":
/ *! * * * * * * * * * * * * * * * * * * * * * * * *! * \! *** ./src/chunk/c.js ***! A \ * * * * * * * * * * * * * * * * * * * * * * * * /
/ *! exports provided: default */
/ * * * / (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default".function() { return del; });
/* harmony import */ var _d_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/ *! ./d.js */ "./src/chunk/d.js");


Object(_d_js__WEBPACK_IMPORTED_MODULE_0__["default"]) (100.11)

Promise.resolve(/ *! import() */).then(__webpack_require__.bind(null./ *! ./b.js */ "./src/chunk/b.js")).then(add= > add(1.2))

function del(n1, n2) {
  return n1 - n2
}

/ * * * /}}));Copy the code

When executing to C. js, Webpack invokes the asynchronously referenced file C using jSONP principles. Loaded with the script tag, SRC refers to the c file’s identity to invoke execution.

The configuration involved in unpacking is as follows, from the demo of the official documentation:

module.exports = {
  / /...
  optimization: {
    splitChunks: {
      chunks: 'async'.minSize: 30000.maxSize: 0.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

Through some configuration of splitChunks, we can set some rules for unpacking. There is one point to note here:

  • By default, webpack extracts the node_modules files from third-party libraries as a separate chunk. We can also use the same method for our own utility classes to export the files as a separate chunk. The reason for this is that the code does not change very much. Simple extraction can make full use of the browsing cache and speed up the next load.

3. Summary

Configuration optimization of WebPack can be started from the following two directions

  • Increase build speed
    • Speed up the Resolve phase to speed up file search
    • Speed up the loader execution phase to reduce the number of files to be executed by the Loader
  • Optimize build output
    • tree-shaking
    • Code compression
      • Js code compression
      • CSS code compression
    • unpacking
      • Asynchronous loading
      • Pull out third-party libraries

Author: Wu Haiyuan

  • Didi cloud full line standard cloud server limited time special, registration is a gift package for novices
  • New purchase cloud services 50% off in January, 40% off in March and 30% off in June
  • Didi cloud emissary recruitment, recommended maximum commission 50%