preface

Looking back, I have been doing front-end development for more than 3 years, and most of the projects I have done are business requirements iteration, with relatively little experience in technical optimization. Last year, thanks to the trust of the leader, he gave us the opportunity to start the technical optimization of the company’s project. The first task of technical optimization is performance optimization (compilation optimization + build volume optimization) for the overall front-end project.

I. Project Introduction:

  1. Project Description: Our team is mainly engaged in SaaS projects, including b-terminal intelligent customer service, online customer service, intelligent e-marketing, C-terminal chatbot and other services.
  2. Project scale: Product: 10 employees; Front end: 10 people; Back end: 24 people; Test: 10 people, and a number of sales, operations, etc.
  3. Personal responsibilities: I was mainly responsible for the front-end project development of the privatized business line, and then I did some chores in the front-end technical architecture and project optimization

Recently, when doing project review, I found that project optimization brought me a lot of harvest and growth. Today I would like to share with you a webpack performance optimization experience.

Ii. Project Background

Why do Webpack optimizations?

The front-end technology stack used in the project of our team is VUE + VUex + Webpack. As the business code is too complex, the number of components is too large, and there are many third-party dependencies introduced, problems such as slow compilation speed of Webpack, large volume of compiled and packaged code, slow page loading speed and so on appear.

Iii. Practice Process:

Before doing webPack performance optimization, we first investigated its build process. After webpack starts, it will start according to the entry configuration, recursively traverse the files it depends on for parsing, and then transform and output dist files. Briefly speaking, the Webpack building process can be divided into two processes: parsing and compiling and packaging output. Therefore, performance optimization starts from these two aspects.

Compiler optimization

1. First, compile speed analysis

I used the speed-measure-webpack-plugin to analyze the specific execution time of each loader and plugin. The configuration is as follows:

const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
const smp = new SpeedMeasurePlugin();

const webpackConfig = smp.wrap({
  plugins: [new MyPlugin(), new MyOtherPlugin()],
});
Copy the code

The figure shows that the compilation takes 81.56 seconds, among which vue-loader, CSS-loader, URl-loader, and babel-loader take a long time. Therefore, it can be analyzed that Webpack takes too long to search and parse files, because Loader is used for file parsing and conversion.

2. Analyze the time consuming situation and optimize the compilation of the appropriate medicine

File search time too long optimization measures

1) Configure module.noParse to tell WebPack not to parse certain files such as JQuery and Lodash that can run directly in the browser, so there is no need to search and parse. The configuration is as follows:

module.exports = {
  //...
  module: {
    noParse: /jquery|lodash/,
  },
};
Copy the code

2) Configure loader to narrow the search scope by using test, include, and exclude. For example:

module.exports = {
  //...
  module: {
    rules: [
      {
        test: /\.js$/,
        use: {
          loader: 'babel-loader',
        },
        include: [resolve('src')],
        exclude: /node_modules/,
      }
    ]
  }
}
Copy the code

Optimization measures for file parsing too long

It is also very simple to use threadloader, as long as the threadloader is placed in front of other loaders. Loaders after thread-loader will run in a separate worker pool. According to the official statement, each worker costs about 600ms. Therefore, in order to prevent high delay when starting the worker, the official provides the optimization of worker pool. The usage is as follows:

const threadLoader = require('thread-loader'); Const jsWorkerPool = {// Number of workers generated, which defaults to (CPU core -1) // if require(' OS ').cpus() is undefined, then 1 workers: 2, // Delete worker processes when idle // default is 500ms // can be set to infinity, so that in monitor mode (--watch) can keep worker persistent poolTimeout: 2000} threadLoader.warmup(jsWorkerPool, ['babel-loader']); module.exports = { // ... module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: [ { loader: 'thread-loader', options: jsWorkerPool }, 'babel-loader' ] } ] } }Copy the code

Note: For small projects with few files, there is no need to use thread-Loader to start multiple processes, which will slow down the packaging process because it takes extra time to start processes.

2) Make good use of cache

  • Loader enables caching, such as using the loader’s own cache or usingcache-loader
    • Loader cache
    loader: 'babel-loader? cacheDirectory=ture'Copy the code
    • cache-loader

    Add this loader before some of the more performance expensive loaders to cache the results to disk.

    module.exports = {
      module: {
        rules: [
          {
            test: /\.ext$/,
            use: [
              'cache-loader',
              ...loaders
            ],
            include: path.resolve('src')
          }
        ]
      }
    }
    Copy the code

Note: There is some time overhead in saving and reading these cache files, so it is best to use the Loader cache only for performance heavy Loaders.

  • Using module cache to improve the speed of secondary construction Hard-source-webpack-plugin is used to provide intermediate cache for modules to quickly improve the speed of secondary construction.
module.exports = {
  //...
  plugin: [
      new HardSourceWebpackPlugin({
        cachePrune: {
          maxAge: Infinity,
          sizeThreshold: Infinity
        }
      })
  ]
}
Copy the code

Optimized way to compress and package code for too long

1) The compression code plug-in opens the multi-process parallel and cache mode

  • Terser-webpack-plugin (Webpack4 recommended, supports ES6 syntax)
const TerserJSPlugin = require('terser-webpack-plugin'); Module. exports = {optimization: {minimizer: [new TerserJSPlugin({cache: true,// enable cache parallel: True // Enable multiple processes})],},};Copy the code

If you are using WebPack V5 or above, you do not need to install this plugin. Webpack V5 comes with the latest Terser-webpack-plugin. If you use Webpack V4, you must install the version of Terser-webpack-Plugin V4.

  • Uglifyjs-webpack-plugin (webpack3, 4 can be used)
const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); Module. exports = {optimization: {minimizer: [new UglifyJsPlugin({cache: true,// enable cache parallel: True // Enable multiple processes})],},};Copy the code

Using the above optimization means, the overall compilation speed can be improved by about 80%. After the optimization of our project, the compilation time was accelerated from the original 81.56 seconds to 13.268 seconds, and the compilation speed increased by 83%. The most significant optimization effect above is the Hard-source-Webpack. Although the first build time does not change much, the second build speed increases by 60-70%.

Construction volume optimization

1. Analyze the bundle volume

Use Webpack-bundle-Analyzer to analyze the size of each module that generates bundles after packaging. The configuration is as follows:

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
  plugins: [
    new BundleAnalyzerPlugin()
  ]
}
Copy the code

As you can see from the above figure, the bundle contains many large third-party dependencies, business code JS/CSS, and image resources. Therefore, packaging volume optimization can be considered from these aspects.

2. Optimize the construction volume according to local conditions

Bundle Removes third-party dependencies

Configure externals to prevent imported packages from being packaged into the bundle and instead obtain external dependencies at runtime.

module.exports = { //... externals: { vue: 'Vue', vuex: 'Vuex', 'vue-router': 'VueRouter', axios: 'axios', ... }};Copy the code

Index.html reintroduces the required third-party dependency js:

< script SRC = "https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js" > < / script > < script SRC = "https://cdn.bootcdn.net/ajax/libs/vuex/3.0.1/vuex.min.js" > < / script > < script SRC = "https://cdn.bootcdn.net/ajax/libs/vue-router/3.0.1/vue-router.min.js" > < / script > < script SRC = "https://cdn.bootcdn.net/ajax/libs/axios/0.19.0/axios.min.js" > < / script >Copy the code

By configuring externals, the volume of bundles generated by packaging is significantly reduced, since these configured third-party dependencies are not packaged. After this method is used in our project, volume files are greatly reduced. The volume of index.js is reduced from 4.34m to 2.74m, which is 36% smaller, as shown in the figure:

Tree Shaking Removes useless JavaScript

// Add {"sideEffects": ["*.css", "babel-polyfill"]}Copy the code

By configuring sideEffects, Tree Shaking is enabled, and webpack automatically removes unreferenced JS files. Projects that have redundant business files but are not easy to remove are especially good for Tree Shaking, which can significantly reduce packaging volume.

Iv. Summary thinking:

I also encountered some problems during the webpack optimization process. To summarize, webpack optimization mainly needs to pay attention to the following points:

  • The webpack configuration needs to be different from the version. The use of WebPack 3.x and Webpack 4.x is quite different, so you need to refer to the official document to modify the configuration. An error will be reported if the same configuration is used for undifferentiated versions
  • Be good at using visual tools to analyze and make optimization plans according to local conditions. Only suitable for their own project optimization plan is the best.

(Xunzi said, “He who makes a false horse will not gain his feet and lead a thousand miles; False boats cannot move water but cut off rivers. The gentleman is born not different also, good false in things also.

Analysis tools are available for reference: Five visual solutions to analyze webPack performance bottlenecks

This article is participating in the “Nuggets 2021 Spring Recruitment Campaign”, click to see the details of the campaign