Performance optimization of Web pack packaging tool

webpack Packaging Optimization

 

I won't explain more about what is webpack here. If you don't know, this article is not suitable for you to read now.

 

webpack5 basic configuration

Let's first look at a basic configuration, webpack based on webpack 5 confing. JS file.

/*
* webpack.confing.js
*/
const { resolve } = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  // pattern
  mode: "development",
  // entrance
  entry: "./src/index.js",
  // Export
  output: {
    filename: "built.js",
    path: resolve(__dirname, "build"),
    // Custom output static resource file name (picture)
    assetModuleFilename: "assets/[hash][ext]",
  },
  // modular
  module: {
    rules: [
      // Configuration of loader
      {
        test: /\.css$/,
        // Use loader to process files
        /**
         * use The execution order in the array is from right to left and from bottom to top
         * style-loader  Create a style tag and import the style file into the header
         * css-loader Change the css module into a commonjs module and load it into js. The content of the file is the style string
         */
        use: ["style-loader", "css-loader"],
      },
      {
        test: /\.less$/,
        use: [
          "style-loader",
          "css-loader",
          // Compile less file into css file
          // You need to download less loader and less
          "less-loader",
        ],
      },
      {
        test: /\.(png|svg|jpg|jpeg|gif)$/i,
        // webpack 5 has built-in resource types and has abandoned the previous URL loader and file loader
        type: "asset/resource",
      },
      {
        test: /\.html$/,
        // Process image files in html, introduce img files, and then let URL loader process them
        loader: "html-loader",
      },
      {
        // Dealing with other resources
        exclude: /\.(html|js|css|less|png|svg|jpg|jpeg|gif)$/i,
        type: "asset/resource",
      },
    ],
  },
  // plug-in unit
  plugins: [
    // HtmlWebpackPlugin
    // By default, an empty html file will be created, and all packaged resources will be automatically introduced.
    // A structured html file is required
    new HtmlWebpackPlugin({
      template: "./src/index.html",
    }),
  ],
  /**
   * Download yarn add webpack dev server -- dev
   * Run npx webpack serve
   */
  devServer: {
    // Path after project construction
    contentBase: resolve(__dirname, "build"),
    // Automatically open browser
    open: true,
    // Port number
    port: 5555,
    // Turn on gzip compression
    compress: true,
  },
};
Copy code

The above code is the most basic code of our article, and the packaging optimization later is configured based on this file.

HMR hot update

The HMR - hot module replacement function will replace, add or delete modules during the operation of the application without reloading the whole page.

advantage:

  1. Retain the application state lost during a full reload of the page.
  2. Only update changes to save valuable development time.
  3. When CSS/JS is modified in the source code, it will be updated in the browser immediately, which is almost equivalent to changing the style directly in the browser devtools.

Problem picture

We can see from the above figure that when we change the style, our js file is executed again, which is a problem for our project. Our optimization direction is to change the file and only the file is reloaded.

code

According to the above problem, we only need to change the code at devServer.

/*
* webpack.confing.js
*/
...
module.exports = {
...
  devServer: {
    // Path after project construction
    contentBase: resolve(__dirname, "build"),
    // Automatically open browser
    open: true,
    // Port number
    port: 5555,
    // Turn on gzip compression
    compress: true,
    // New -- > enable hot update
    // The module hot replacement function will replace, add or delete modules during program operation without reloading the whole page.
    hot: true,
  },
};
Copy code

Effect picture

 

devtool source code debugging mode

Choose a source map format to enhance the debugging process. Different values can significantly affect the speed of build and rebuild.

  1. For the source code debugging of the development environment

    Eval source map - initializing the source map is slow, but provides a faster speed when rebuilding and generates the actual file. The number of lines can be mapped correctly because it will be mapped to the original code. It generates the best quality source map for the development environment.

  2. Source code debugging for production environment

    (none) (omit the devtool option) - no source map is generated. This is a good choice.

Rule.oneOf matching rule

Matching rules for loader.

/*
* webpack.confing.js
*/
...
module.exports = {
...
// modular
  module: {
    rules: [
      // Configuration of loader
      {
        oneOf: [
          // The following loader will execute the matching file only once.
          {
            test: /\.css$/,
            // Use loader to process files
            /**
             * use The execution order in the array is from right to left and from bottom to top
             * style-loader  Create a style tag and import the style file into the header
             * css-loader Change the css module into a commonjs module and load it into js. The content of the file is the style string
             */
            use: ["style-loader", "css-loader"],
          },
          {
            test: /\.less$/,
            use: [
              "style-loader",
              "css-loader",
              // Compile less file into css file
              // You need to download less loader and less
              "less-loader",
            ],
          },
          {
            test: /\.(png|svg|jpg|jpeg|gif)$/i,
            // webpack 5 has built-in resource types and has abandoned the previous URL loader and file loader
            type: "asset/resource",
          },
          {
            test: /\.html$/,
            // Process image files in html, introduce img files, and then let URL loader process them
            loader: "html-loader",
          },
          {
            // Dealing with other resources
            exclude: /\.(html|js|css|less|png|svg|jpg|jpeg|gif)$/i,
            type: "asset/resource",
          },
        ],
      },
    ],
  },
...
}
Copy code

In this way, the matching of loader s during packaging will improve the speed.

File cache

In the production environment, we can cache our packaged css, js and other resources into the browser to improve the speed of the second page we enter. So we have to be right about webpack confing. js to configure and cache js files, css files and picture files.

First, we have to download the following loader:

yarn add babel-loader @babel/core @babel/preset-env mini-css-extract-plugin --dev

Optimization steps

  1. Extract css files from packaged files as separate files.
  2. Use babel cache for js files.

Extract css and cache file resources

/*
* webpack.confing.js
*/
...
// Add plug-in
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
...
// pattern
mode: "production",
output: {
    filename: "built.[contenthash:10].js",
    ...
  },
...
  // modular
  module: {
    rules: [
      // Configuration of loader
      {
        oneOf: [
          // The following loader will execute the matching file only once.
          {
            test: /\.css$/,
            // Replace style loader with
            use: [MiniCssExtractPlugin.loader, "css-loader"],
          },
          {
            test: /\.less$/,
            use: [
            // Replace style loader with
              MiniCssExtractPlugin.loader,
              "css-loader",
              "less-loader",
            ],
          },
         ...
         // Add js loader
          {
            test: /\.js$/,
            exclude: /node_modules/,
            use: {
              loader: "babel-loader",
              options: {
                presets: ["@babel/preset-env"],
                // Enable babel cache
                // On the second build, the previous cache is read
                // Used to cache the execution results of the loader. After the webpack is built, it will try to read the cache,
                // To avoid the possible Babel consumption of high performance during each execution 
                // Recompilation process.
                cacheDirectory: true,
              },
            },
          },
        ],
      },
    ],
  },
  // plug-in unit
  plugins: [
    ...
    //Add plug-in
    new MiniCssExtractPlugin({
      filename: "css/built.[contenthash:10].css",
    }),
  ],
};
Copy code
  1. Hash: a unique hash value will be generated each time wepack is built.

    • Because of the problem of using both has and css values.
    • Repackaging will invalidate all caches. (maybe I only changed one file)
  2. Chunk hash: hash value generated according to chunk. If the package comes from the same chunk, the hash value is the same.

    • Question: the hash values of js and css are still the same
    • Because css is introduced in js, it belongs to the same chunk
  3. contenthash: generates a hash value according to the contents of the file.

    • The hash value of different files must be different, so that the code can run online and the cache can be used better.

Add jsloader and set cache

/*
* webpack.confing.js
*/
...
module.exports = {
...
  // modular
  module: {
    rules: [
      // Configuration of loader
      {
        oneOf: [
         ...
         // Add js loader
          {
            test: /\.js$/,
            exclude: /node_modules/,
            use: {
              loader: "babel-loader",
              options: {
                presets: ["@babel/preset-env"],
                // Enable babel cache
                // The second read before the cache is built
                // Used to cache the execution results of the loader. After the webpack is built, it will try to read the cache,
                // To avoid the possible Babel consumption of high performance during each execution 
                // Recompilation process.
                cacheDirectory: true,
              },
            },
          },
        ],
      },
    ],
  },
...
};
Copy code

Next, we write the test code. Create a new server JS file and create a new server for testing. Download the express framework.

yarn add express --dev

/*
* server.js
*/
const express = require("express");

const server = express();

// Cache for one hour
server.use(express.static("build", { maxAge: 1000 * 3600 }));

server.listen(5555, () => {
  console.log("Server started successfully!", "http://localhost:5555/");
});
Copy code

Check cache steps

  1. webpack packaging
  2. Start the node server js
  3. http://localhost:5555/

 

 

tree shaking to remove useless code

Use premise

  1. ES6 modularization must be used
  2. Open production environment

Advantages: when packaging the production environment, we can ignore the unused code and delete the unused code from the packaging file to reduce the volume of the packaging file.

As long as we meet the above two preconditions, I will automatically delete the invalid code during the packaging of webpack.

Add test file

/*
* testTreeShaking.js
*/
export const test1 = () => {
  console.log("test1");
};

export const test2 = () => {
  console.log("test2");
};
Copy code

Modify file

/*
* index.js
*/
...
import { test1 } from "./testTreeShaking";
test1();
// test2 function is not introduced. Ignored when packing.
Copy code
/*
* webpack.confing.js
*/
...
module.exports = {
...
// Modify packaging mode
mode: 'production',
...
};
Copy code

package. Configuration in JSON

/*
* ackage.json
*/
"sideEffects": false 
// No side effects (tree shaking can be performed)
// css / @babel/polyfill files may be killed
"sideEffects": ["*.css", "*.less"]
Copy code

For webpack packaging, view the packaging file as shown in the figure below:

You can see that the test2 function is not packaged into the package file.

code Split

There are three types of code segmentation:

  1. Multi entry files are automatically code split
  2. optimization.splitChunks control code splitting
  3. optimization.splitChunks + import() for code splitting

Advantages: Code segmentation can effectively reject js files that are too large.

Multi entry files are automatically code split

/*
* webpack.confing.js
*/
...
module.exports = {
...
entry: {
    // Multiple entries: there is one entry, and the final output has a bundle
    index: "./src/index.js",
    test: "./src/testTreeShaking.js",
  },
...
};
Copy code

The effect of webpack packaging is shown in the following figure:

optimization.splitChunks control code splitting

/*
* webpack.confing.js
*/
...
module.exports = {
...
// New code
optimization: {
    /*
    1. Node can be_ The code in modules packs a chunk separately for final output
    2. Automatically analyze whether there are public files in the multi entry chunk. If so, it will be packaged into a single chunk
  */
    splitChunks: {
      //This indicates which chunk s will be selected for optimization. When a string is provided, the valid values are all, async, and initial.
      //Setting to all can be particularly powerful because it means that chunks can be shared between asynchronous and non asynchronous chunks.
      chunks: "all",
    },
  },
...
};
Copy code

The effect of webpack packaging is shown in the following figure:

optimization.splitChunks + import() for code splitting

/*
* webpack.confing.js
*/
...
module.exports = {
...
// New code
optimization: {
    /*
    1. Node can be_ The code in modules packs a chunk separately for final output
    2. Automatically analyze whether there are public files in the multi entry chunk. If so, it will be packaged into a single chunk
  */
    splitChunks: {
      //This indicates which chunk s will be selected for optimization. When a string is provided, the valid values are all, async, and initial.
      //This means that non chunk can be set between asynchronous and all.
      chunks: "all",
    },
  },
...
};
Copy code
/*
* index.js
*/
/*
  Through js code, a file is packaged into a chunk separately
  import Dynamic import syntax: a file can be packaged separately
*/
import("./testTreeShaking").then(
  (res) => {
    console.log("res", res);
    res.test1();
  }
);

Copy code

The effect of webpack packaging is shown in the following figure:

File preload

difference

  1. Reload using file
  2. Load the browser first when it is idle

Modify file

/*
*index.html
*/
...
<button id="btn">load testTreeShaking file</button>
...
Copy code
/*
*index.js
*/
console.log("load index file");
Copy code
/*
*testTreeShaking.js
*/
export const test1 = () => {
  console.log("test1");
};

export const test2 = () => {
  console.log("test2");
};
console.log("load index file");
Copy code

File lazy loading

/*
*index.js
*/
console.log("load index file");
document.getElementById("btn").onclick = function () {
  // Lazy load: load only when the file needs to be used
  import("./testTreeShaking").then(({ test1 }) => {
    test1();
  });
};
Copy code

Lazy loading effect drawing

Preload

/*
*index.js
*/
console.log("load index file");
document.getElementById("btn").onclick = function () {
  // Preload prefetch: js files will be loaded in advance before use
  // Normal loading can be considered as parallel loading (loading multiple files at the same time)
  // Preload prefetch: after other resources such as webpackPrefetch are loaded, the browser is idle, and then load resources secretly
  import(/* webpackPrefetch: true */ "./testTreeShaking").then(({ test1 }) => {
    test1();
  });
};
Copy code

Preload rendering

PWA progressive network application

PWA can be used to do many things. Offline is the most important function when the application is running. This is achieved by using a web technology called Service Workers.

PWA renderings of Taobao

Add the workbox-webpack-plugin plug-in and adjust the webpack.. config. JS file:

yarn add workbox-webpack-plugin --dev

Modify webpack confing. js

/*
* webpack.confing.js
*/
...
// Add plug-in
const WorkboxWebpackPlugin = require("workbox-webpack-plugin");
module.exports = {
...
  // plug-in unit
  plugins: [
    ...
    //Add plug-in
    new WorkboxWebpackPlugin.GenerateSW({
      /*
        1. Help serviceworker start quickly
        2. Delete old serviceworker

        Generate a serviceworker configuration file~
      */
      clientsClaim: true,
      skipWaiting: true,
    }),
  ],
};
Copy code

Register Service Worker

/*
* index.js
*/
/*
   sw The code must run on the server
      --> nodejs
      -->
        npm server Start the server and expose all resources in the build directory as static resources
*/
// Register serviceWorker
// Handling compatibility issues
if ("serviceWorker" in navigator) {
  window.addEventListener("load", () => {
    navigator.serviceWorker
      .register("/service-worker.js")
      .then(() => {
        console.log("sw Registration succeeded~");
      })
      .catch(() => {
        console.log("sw Registration failed~");
      });
  });
}
Copy code

Next, we will the previous server JS files are copied and used directly:

  1. Pack first
  2. node server.js start server
  3. http://localhost:5555/

 

 

Multi process packaging

yarn add thread-loader --dev

Modify webpack confing. JS file:

/*
* webpack.confing.js
*/
...
module.exports = {
...
  // modular
  module: {
    rules: [
      // Configuration of loader
      {
        oneOf: [
         ...
         // Add js loader
          {
            test: /\.js$/,
            exclude: /node_modules/,
            use: [
              /* 
                New code
                Turn on multi process packaging. 
                The process start-up is about 600ms, and the process communication also has overhead.
                Only when the work takes a long time, multi process packaging is required
              */
              {
                loader: "thread-loader",
                options: {
                  // The number of worker s generated. The default is (number of cpu cores - 1), or,
                  // In require ('os') Fallback to 1 when cpus() is undefined
                  workers: 2, // 2 processes
                },
              },
              ...
            ],
          },
        ],
      },
    ],
  },
...
};
Copy code

You can check the effect by running webpack. Because it takes 600ms to start the multi process packaging, it is more suitable for projects with larger code volume.

Multi process packaging rendering is not enabled

Open multi process packaging rendering

externals

Prevent some import ed packages from being packaged into bundle s, but obtain these external dependencies from the outside at runtime.

Modify webpack confing. JS file, add externals attribute

/*
* webpack.confing.js
*/
...
module.exports = {
...
  externals: {
    jquery: "jQuery",
  },
};
Copy code

Select the CDN link of JQ and add it to the index html

Free CDN address

/*
* index.html
*/ 
<!DOCTYPE html>
<html lang="zn">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>webpack Packaging Optimization</title>
  <!-- New code -->
  <script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>
</head>

<body>
  <h1>webpack Packaging Optimization</h1>
  <div class="color_1"></div>
  <div class="color_2"></div>
  <img src="./img/zp.jpg" />
</body>

</html>
Copy code
/*
* index.js
*/
import $ from "jquery";
console.log("$", $);
Copy code

webpack is packaged for testing.

 

 

 

  

 

Keywords: Front-end Webpack

Added by amity on Fri, 11 Feb 2022 08:47:32 +0200