This article will document the next one-time optimization for WebPack

The status quo

As the project grew, the number of components became more and more, and the project became larger, the compilation time of WebPack became longer and longer. Our current project was compiled once between 40s — 70s, which was a very inefficient operation. There are many ways to optimize, and many have been done in the previous project. This article will explain optimization from the perspective of caching

The following are only a few cache-related optimization methods, including

  • babel-loadercacheDirectory
  • cache-loader
  • dllDynamic link library
  • HardSourceWebpackPlugin

Let’s start with the conclusion. The first one is existing in the project, the second and third ones have little effect, and the fourth one has achieved the expected effect

Our WebPack version: 4.41.2, system: MAC OS

Bottleneck analysis

The first step in optimization should be to analyze the current performance, and here we use the speed-measurement-webpack-plugin for speed analysis

// Install NPM install --save-dev speed-measurement-webpack-pluginCopy the code
// Usage mode
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
 
const smp = new SpeedMeasurePlugin();
 
const webpackConfig = smp.wrap({
  plugins: [
    new MyPlugin(),
    new MyOtherPlugin()
  ]
});

Copy the code

The result is similar to the following. You can see the time consuming of each Loader and Plugin. With this, we can “apply the right medicine”.

One caveat: The HardSourceWebpackPlugin and speed-measure-webpack-Plugin don’t work together, which has been frustrating for me for a long time

Babel – cacheDirectory loader

Babel-loader allows JavaScript files to be translated using Babel and Webpack. Sometimes if we are running Babel-Loader slowly, we can consider making sure that we translate as few files as possible. You might use /\.m? Js $/, which could potentially translate the node_modules directory or other unneeded source code, resulting in performance degradation

You can exclude files that do not need to be compiled. For example, the contents under node_modules and bower_components folders are not escaped below

module: {
  rules: [{test: /\.m? js$/,
      exclude: /(node_modules|bower_components)/,
      use: {
        loader: 'babel-loader'.options: {
          presets: ['@babel/preset-env'].plugins: ['@babel/plugin-proposal-object-rest-spread']}}}]}Copy the code

You can also speed up Babel-Loader at least twice by using the cacheDirectory option. This caches the result of the translation to the file system. The default value of cacheDirectory is false. If yes, the specified directory is used to cache the execution results of the Loader. Later WebPack builds will attempt to read the cache to avoid the potentially high-performance recompilation process of Babel each time it is executed. If set to an empty value (loader: ‘babel-Loader? CacheDirectory ‘) or true (loader: ‘babel-loader? CacheDirectory =true’), loader will use the default cache directory node_modules/.cache/ babel-Loader, if node_modules is not found in any root directory, Will be degraded back to the operating system default temporary file directory.

{
  test: /\.js$/,
  use: 'babel-loader? cacheDirectory'.include: [resolve('src'), resolve('test') ,resolve('node_modules/webpack-dev-server/client')]}Copy the code

cache-loader

What if we want to cache the results of other loaders besides Babel-Loader?

The answer is that cache-loader can be used. Add a cache-loader before some performance expensive loaders to cache the results to disk

The installation

npm install --save-dev cache-loader
Copy the code

configuration

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

⚠️ Please note that there is some time overhead to save and read these cached files, so use this loader only for loaders with high performance overhead

In addition to the default configuration, cache-Loader provides several other options. See Cache-Loader for more information

DLL cache scheme

What is a DLL?

DLL files are dynamic link libraries. In a dynamic link library, functions and data can be called for other modules

Why use DLLS?

The reason for this is that a dynamic link library that contains a large number of reusable modules only needs to be compiled once. Modules that are included in the dynamic link library during subsequent builds will not be recompiled and will use code from the dynamic link library directly. Since most of the DYNAMIC link libraries contain commonly used third-party modules, such as Vue React and React-dom, as long as the versions of these modules are not upgraded, the dynamic link libraries do not need to be recompiled

How to use it?

To complete the following three steps:

  • Pull away. Take the basic modules that web pages depend on and package them into separate dynamic link libraries. A dynamic link library can contain multiple modules
  • To obtain. When a module to be imported exists in a dynamic link library, the module cannot be repackaged and is retrieved from the dynamic link library
  • Load. All the dynamic link libraries that the page depends on need to be loaded

This was previously done using DllPlugin and DllReferencePlugin, but the configuration was complex and required manual re-generation of the DLL if the files were updated. The AutoDllPlugin is selected here, which automatically completes the functions of the above two plug-ins. This is a plug-in used by vue-CLI

Installation:

webpack 4

npm install --save-dev autodll-webpack-plugin
Copy the code

webpack 2 / 3

NPM install - save - dev [email protected]Copy the code

Basic use:

plugins: [
  new HtmlWebpackPlugin({
    inject: true.template: './src/index.html',}).new AutoDllPlugin({
    inject: true.// will inject the DLL bundles to index.html
    filename: '[name].js'.entry: {
      vendor: [
        'react'.'react-dom']}})]Copy the code

Before optimization

The optimized

First compilation:

Second compilation:

Optimized for several s, the effect is not great

The reason for this is that webPack4 performance is good enough and vue-CLI does away with it

HardSourceWebpackPlugin

Installation:

npm install --save-dev hard-source-webpack-plugin
# or
yarn add --dev hard-source-webpack-plugin
Copy the code

Configuration:

// webpack.config.js
var HardSourceWebpackPlugin = require('hard-source-webpack-plugin');

module.exports = {
  context: // ...
  entry: // ...
  output: // ...
  plugins: [
    new HardSourceWebpackPlugin()
  ]
}
Copy the code

Before optimization

As you can see, it takes 50s

The optimized

First start

Second start

It only takes 7 s, a reduction of 43 s and a speed increase of about 80 percent. The purpose of optimization is achieved!

Thermal regeneration rate

I saw that the issue mentioned hot update and said it would be a little slower. I made use of our project to do some tests. The following is the test data

Before optimization

js: 2443ms 1634ms 1844ms 2532ms 1443ms 1248ms

html: 1094ms 1232ms 1119ms 1490ms 1264ms

css: 1422ms 1186ms 1341ms 1562ms 1183ms

The optimized

js: 2429ms 2436ms 2860ms 2528ms 1917ms 1487ms 1450ms 1450ms 1557ms 2198ms

html: 2855ms 1569ms 1400ms 1298ms 1204ms 1299ms 1578ms 1485ms 2028ms

css: 2035ms 1406ms 1415ms 1600ms 1773ms 1604ms

Sometimes a little slower, but generally acceptable. However, there are some implications, so two NPM script commands have been added to the project. If you don’t want to enable them, you can just run NPM dev:noCache

"scripts": {
  "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js --cache=true"."dev:noCache": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js --cache=false"
}
Copy the code

In the build/webpack. Dev. Conf. Js

if (args.cache) {
  devConfig = merge(devConfig, {
    plugins: [new HardSourceWebpackPlugin()]
  })
}
Copy the code

Again:

HardSourceWebpackPlugin and speed-measured-webpack-plugin cannot be used together

Looking to the future

Webpack 5 has been released with an attractive feature — persistent caching (said to be the same idea as HardSourceWebpackPlugin).

Improve build speed by caching generated WebPack modules and chunks. Cache is set to type: ‘memory’ in development mode and disabled in production mode

module.exports = {
  cache: {
    // 1. Set the cache type to file system
    type: 'filesystem'.buildDependencies: {
      // 2. Add your config to buildDependency so that the cache is invalid when changing the config
      config: [__filename],

      // 3. If you have other things to build on, you can add them here
      // Note that webpack, loader, and any modules referenced from your configuration will be added automatically,}}};Copy the code

conclusion

The above exploration, the author spent a lot of time, food is the original sin, or more accumulation. Another emotion is that the development of the front end is so fast that many things may be outdated, and the only way is to maintain continuous learning and a solid foundation of knowledge

Above, hope to be helpful to everybody

reference

  • How does Webpack 4 gracefully package cached files

  • The hard-earned WebPack DLL configuration may be obsolete

  • After two years, WebPack 5 is officially released!