Terrible startup time

The company’s product is a relatively large background management system, and the vUE template project of WebPack3 is used, and the single project start time reaches 70s or so

Start a project is enough to eat a bowl of bean curd, but there is no bean curd how to do, then optimize the start time!

Considering the risk of upgrading the WebPack version and having to call me if anything goes wrong, I chose to optimize the build time by using plugins.

There are several ways to improve WebPack build times by looking at the resources:

  • Multiprocess file processing, processing multiple files at the same time
  • Precompile resource modules, such as long time invariant library extracted for precompile, build the results directly
  • Cache, unmodified modules directly get the processing results, do not have to compile
  • Reduce the number of files that build searches and processes

According to the above optimization directions, the following optimization schemes are given.

Multiprocess build

happypack

What happypack does is split the file parsing task into multiple sub-processes that execute concurrently.

The child process processes the task before sending the result to the main process. So you can greatly speed up Webpack’s project artifacts.

The happypacy plugin is no longer maintained on github, and it is recommended to use thread-Loader, the official multi-process plugin for Webpack. So I chose thread-Loader instead of Happypacy.

thread-loader

Thread-loader is an officially maintained multi-process loader that functions similar to happypack. It also enables subtasks to parse files in parallel to speed up the build process.

Put this loader in front of the other loaders. However, the loader is limited. Example:

  • Loader could not issue a file.
  • Loader cannot use the custom loader API.
  • Loader cannot access web package options.

Each worker is a separate Node.js process with an overhead of about 600 milliseconds. There is also the overhead of interprocess communication. Using Thread-Loader on a small project may not optimize the build speed of the project, but rather slow it down, so you need to be aware of the actual time consuming process in your project’s build components when using the loader.

In my project, I mainly use this loader to parse vue and JS files, for Vue-Loader and babel-Loader, as follows:

const threadLoader = {
  loader: 'thread-loader'.options: {
    workers: require('os').cpus().length - 1,}}module.exports = {
  module: {rules: [{test: /\.vue$/,
        use: [
          threadLoader, // Use this loader before vue-loader
          {
            loader: 'vue-loader'.options: vueLoaderConfig
          }
        ],
      },
      {
        test: /\.js$/,
        use: [
          threadLoader, // Use this loader before babel-Loader
          {
            loader: 'babel-loader'.options: {
              cacheDirectory: true}}}}Copy the code

After configuring the Thread-Loader, try to rebuild it, as shown in the figure below, saving about 10 seconds of build time, which is not bad.

Use caching to speed up secondary builds

While the use of multi-process build projects has reduced the build time by 10 seconds, the one-minute build time is still unacceptable. This toothpaste squeeze is a bit of a nuisance. Are there any cool ways to further reduce the build time?

The answer is yes, use caching.

Cache, it is not difficult to understand are the result of the building for the first time will build cached, when the second build, check to see if the corresponding cache changes, if there is no change, direct use of caching, as a result, we can imagine, when project change is not big, most of the cache is reusable, and did not build speed will have a qualitative leap.

cache-loader

When you look at the official documents on Github, you get the following results:

This loader will cache the processing results of other loaders and put this loader ahead of other loaders. In addition, saving and reading cached files by this loader incur overhead. Therefore, you are advised to use this loader before other loaders with high overhead.

The document is simple, because vue-loaders, Babel-Loaders, csS-loaders in a project can be expensive, so add cache to these loaders, then add cache-loaders to the project:

const cacheLoader = {
  loader: 'cache-loader'
}

const threadLoader = {
  loader: 'thread-loader'.options: {
    workers: require('os').cpus().length - 1,}}module.exports = {
  module: {rules: [{test: /\.vue$/,
        use: [
          cacheLoader,
          threadLoader, // Use this loader before vue-loader
          {
            loader: 'vue-loader'.options: vueLoaderConfig
          }
        ],
      },
      {
        test: /\.js$/,
        use: [
          cacheLoader,
          threadLoader, // Use this loader before babel-Loader
          {
            loader: 'babel-loader'.options: {
              cacheDirectory: true}}}}Copy the code

In the util. Js file, which generates CSS related Webpack configurations, find the generateLoaders function and modify it as follows:

  const cacheLoader = {
    loader: 'cache-loader'
  }
  
  function generateLoaders(loader, loaderOptions) {
  // Add cache-loader before css-loader
    const loaders = options.usePostCSS ? [cacheLoader, cssLoader, postcssLoader] : [cacheLoader, cssLoader]

    if (loader) {
      loaders.push({
        loader: loader + '-loader'.options: Object.assign({}, loaderOptions, {
          sourceMap: options.sourceMap
        })
      })
    }

    // Extract CSS when that option is specified
    // (which is the case during production build)
    if (options.extract) {
      return ExtractTextPlugin.extract({
        use: loaders,
        fallback: 'vue-style-loader'.// Add this configuration to solve the element-UI icon path problem
        publicPath: '.. /.. / '})}else {
      return ['vue-style-loader'].concat(loaders)
    }
  }
Copy the code

After the configuration is complete, start the project again, and you can find that the current startup time is the same. Then start the project again, and you can find that the current startup time is about 30s. As mentioned above, the cache-loader cache takes effect only after the second startup.

Although the project startup time has been optimized by more than half, but our desire is infinite, 30 seconds time is still a little far from our expectations, continue to optimize!

hard-source-webpack-plugin

HardSourceWebpackPlugin is a WebPack plug-in that provides intermediate caching steps for modules. To see the results, you need to run WebPack twice with this plug-in: the first build will take a normal amount of time. The second construction will be greatly accelerated.

Without further ado, configure directly to the project:

const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
module.exports = {
    / /...
    plugins: [
        new HardSourceWebpackPlugin()
    ]
}
Copy the code

On the second build, we will see that the build time is in the single digits, and it is only a short 7 seconds.

In the second build, I noticed that the progress of the build would jump from 10% to 80%, and even the intermediate build would be completed in an instant. This validates the claim that the plug-in provides an intermediate cache for modules.

It provides intermediate cache for modules. My understanding is that cache-loader caches the processing results of the corresponding Loader. This plug-in can even cache all the processing results of the whole project and directly reference the cache file of the final output, thus greatly improving the construction speed.

Other optimization methods

Babel-loader Enables cache

Babel-loader has a cache function. Enable the cacheDirectory configuration item. According to the official website, enabling cache increases the conversion time by about two times.

module.exports = {
  module: {
  rules: [{test: /\.js$/,
        use: [
          ...
          {
            loader: 'babel-loader'.options: {
              cacheDirectory: true // Enable cache}}}}Copy the code

Uglifyjs-webpack-plugin enables multi-process compression

Uglifyjs-webpack-plugin and other code compression tools provide multi-process code compression, enabling faster code compression.

Dynamic polyfill

I suggest you check out this article

Figure out the front end polyfill

conclusion

So far, we have completed the optimization process of the project construction time from 70s to 7S. The main use in this paper is:

tool role The optimization effect
thread-loader Multi-process parsing of files 70s -> 60s
cache-loader Some loaders with high overhead are cached 60s -> 30s
hard-source-webpack-plugin Cache module intermediate process 30s -> 7s

Step by step, the project will be optimized to almost immediately start, ah, it seems that the next fish less time, work hard, workers!