I have written about the basic usage of Webpack before, and used some Loader plugin to process JS, CSS, images, fonts and other modules. This paper mainly focuses on project optimization and packaging optimization. We know that with the increase of the project, the packaging speed will be slower and slower, so we can improve efficiency by some optimization methods

1. The tool

If we want to know how to optimize, we need to know what to optimize, and we can use tools to determine which files need to be optimized, or which packaging processes need to be optimized, right

1. webpack-bundle-analyzer

This is a visual packaging analysis tool. Using this plug-in will automatically start a local 8888 service to view the information of the packaged package

let BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
plugins: [
    new BundleAnalyzerPlugin()
]
Copy the code

React, React DOM, jquery, loDash

On the visualization page below, we can see the information of the index.js package and the size distribution of each package

From this we can see several issues that increase the size and time of the packaged package

  1. Jquery is not used, but it is packaged
  2. Lodash we just used the Map method, and the whole LoDash was packed in

We’ll talk about how to optimize this later, but let’s talk about this tool, okay

2. speed-measure-webpack-plugin

This plug-in tests the time spent by each plugin and loader

let SpeedMeasurePlugin = require('speed-measure-webpack-plugin') let config = { //... Exports = new SpeedMeasurePlugin().wrap(config)Copy the code

In this way, you can see the packaging information after configuration and packaging

2.include/exculde

Include parses a folder, exclude parses a folder, this is the simplest and most effective optimization, generally do webpack configuration will be configured

The code directory structure above was not configured before

module: {    rules: [      { test: /\.js$/, loader: 'babel-loader' }     ]  },
Copy the code

Packing speed Time: 7595ms

After the configuration, zoom in to the SRC directory

module: {
 rules: [
   { test: /\.js$/, 
    loader: 'babel-loader', 
    include: [path.resolve(__dirname, 'src')]  
   } 
 ]
},
Copy the code

Packing speed Time: 1558ms

Exclude takes precedence over include. I usually only configure include

3.cache-loader

Cache-loader adds some cache during the first packaging and reads the cache during the second packaging to save packaging time

module: {
 rules: [
   { test: /\.js$/, 
    loader: ['chche-loader', 'babel-loader'], 
    include: [path.resolve(__dirname, 'src')]  
   } 
 ]
},
Copy the code

Before adding cache-loader, the speed is 1594ms. After adding cache again, the speed is 818ms

4. externals

Instead of packaging some import packages, we need to fetch these dependencies from the outside at runtime. We usually use CDN cache and do not package them. We will introduce jquery and LoDash CDN in the template file (index.html)

< script SRC = "http://libs.baidu.com/jquery/2.0.0/jquery.min.js" > < / script > < script SRC = "https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js" > < / script >Copy the code

Configure externals on webpack

externals: {    'jquery': '$',    'lodash': '_',  },
Copy the code

This way, jquery and LoDash will not be packaged when they are introduced into JS

Set the externals package speed from ****Time: 818ms to Time: 606ms

5.noParse

If you already know that a package does not have dependencies, set it not to resolve dependencies in the package, such as jquery,lodash, we will first restore externals and CDN, and set noPase, module noPase attribute, receive the re

module: {
    noParse: /jquery|lodash/
}
Copy the code

After testing, packaging is actually a little faster

6. happypack

This project is no longer maintained, this plug-in is multi-threaded processing, small projects are actually not used, the larger the project effect is about obvious

When the Loader detects JS, the configuration is as follows

let Happypack = require('happypack')
module: {
    rules: [
        test: /\.js$/,
        use: 'Happypack/loader?id=js'
    ]
}
Copy the code

The configuration of plugin is as follows, which corresponds to the ID configured in loader. If multiple Loaders are used, configure multiple plugins according to their IDS

new Happypack({
    id: 'js',
    use: ['babel-loader']
})
Copy the code

7.tree-shaking

webpack@4 comes with tree-shaking

Tree-shaking is used to eliminate modules that are referenced but not used. It benefits from ES6 modules, where dependencies are fixed, independent of runtime state, and reliable static analysis is possible.

There are problems with tree-shaking, however, because methods of unused classes cannot be eliminated

8.code spliting

That’s what we call code splitting, so what is code splitting?

  • Our project will introduce a lot of third-party libraries, because the third-party libraries are almost unchanged. We want to remove these libraries so that we can cache them for the browser (usually vendor).
  • Wrap webpack’s own runtime code separately (usually the manifest)
  • Separate package for common business code (typically common)
  • Asynchronously loaded modules are packaged separately (usually with module numbers)

Previously, WebPack 4 used the CommonsChunkPlugin to extract common code, while WebPack 4 only used splitChunks

1. Firstly, we extract vendor package to generate a vendor.js file

optimization: {
    splitChunks: {
      cacheGroups: {
        vendor: {
          chunks: 'initial',
          test: /node_modules/,
          name: 'vendor',
          minChunks: 1,
        },
      }
    }
},
Copy the code

Chunks are async by default and must be changed to all or INITIAL to extract the library under node_modules

2. Extract the WebPack Runtime code and pack it with an extra manifest file

optimization: {
    runtimeChunk: {
      name: 'manifest'
    },
    splitChunks: {
      cacheGroups: {
        vendor: {
          chunks: 'initial',
          test: /node_modules/,
          name: 'vendor',
          minChunks: 1,
        },
      }
    }
},
Copy the code

3. Extract common business code and add a common.js package

optimization: {
    runtimeChunk: {
      name: 'manifest'
    },
    splitChunks: {
      cacheGroups: {
        commons: {
          chunks: 'initial',
          test: path.resolve(__dirname, 'src'),
          name: 'commons',
          minSize: 0,
          minChunks: 2,
        },
        vendor: {
          chunks: 'initial',
          test: /node_modules/,
          name: 'vendor',
          minChunks: 1,
        },
      }
    }
  },
Copy the code

4. Extract asynchronous loading code

Webpack provides two asynchronous loading methods

  • Import () //ES proposal, returns a promise, the imported module gets in then
  • require.ensure() 

All of the above asynchronous approaches rely on Promise, which is used in older browsers using ES6-Promise Polyfill

Babel7 has syntax-dynamic-import by default, so import() can be used to package on-demand loading blocks in a page. On-demand loading on Vue-Router is a good example

Import (/* webpackChunkName: "packaged module name" */' path '). Then (res => {})Copy the code

9. DllPlugin

I used two projects to do optimization, feel DllPlugin on packaging speed optimization is not very obvious, at most only a few seconds, interested in their own try, I will not say more here

The next article will take a look at the source code for Webpack and hand-write some Loaders and plugins