This article is from Thirty Minutes to Master Webpack performance optimization

What is a webpack

It is a module packer, it analyzes the dependencies of each module, finally packaged into our common static files,.js,.css,.jpg,.png and so on.

Webpack related knowledge

  • HMR

  • resolve

  • optimization

  • dll

  • webpack5

  • production

  • development

  • eslint

  • babel

  • pwa

  • loader

  • plugin

  • devtool

  • Performance optimization

  • tree shaking

  • code split

  • caching

  • lazy loading

  • library

  • shimming

Webpack fundamentals

The first step:

  • The WebPack analysis dependency starts with an entry file.

  • From Entry, WebPack reads the entry file (essentially a string) along its path, including the Entry file and all dependencies module that it depends on.

  • Webpack converts entry – related information into an AST (Abstract syntax tree).

The second step:

  • Iterate through the Dependencies array breadth-first, turn all of them into AST, and map the corresponding dependencies and the ID of the item.

  • Finally, a dependency graph is generated, and the dependencies of all modules are completed separately.

Step 3:

  • Now that we have the dependency graph, we call the bundle function, which returns the string that we constructed, we take the string, and we export it as bundle.js.

Webpack performance optimization

1. Optimization of construction speed

  • Resolve: Speed up file reading because it affects tree-shaking. Set resolve for all libraries that have strong integrity, such as react.min.js.
Resolve: {// The default value is node_modules. Modules: [path.resolve(__dirname, 'node_modules')], // Set as few values as possible to reduce the search step of entry file mainFields: ['main'], // make webpack use library min file directly, avoid library alias: {'react': path.resolve(__dirname, './node_modules/react/dist/react.min.js'), } }Copy the code
  • NoParse: The field that tells Webpack which files do not need to be parsed can be used to exclude parsing of modular library files.
module: {
    noParse:[/jquery|chartjs/, /react\.min\.js$/],
}
Copy the code
  • When configuring the Loader, use test, exclude, and include to narrow the search scope

    To match.js files, add babel-loader, which is used to transfer ES6 code. Exclude is used to exclude code from packaging certain files.

 module: {
    rules: [
        {
            test: /\.js$/,
            loader: 'babel-loader',
            exclude: /node_modules/,
        }
    ]
},
Copy the code
  • DllPlugin is used to reduce the compilation times of base modules. The principle is to extract the dependent base modules and pack them into DLL files, such as react and react-dom. As long as the versions of these modules are not upgraded, they only need to be compiled once.

    1. Use DllPlugin to configure a webpack.dll.config.js to build the DLL file
    // webpack.dll.config.js const path = require('path'); const DllPlugin = require('webpack/lib/DllPlugin'); module.exports = { entry:{ react:['react','react-dom'], polyfill:['core-js/fn/promise','whatwg-fetch'] }, Output :{filename:'[name].dl.js ', path:path.resolve(__dirname, 'dist'), Library :'_dll_[name]', // DLL global variable name}, Plugins :[new DllPlugin({name:'_dll_[name]', // DLL global variable name path:path.join(__dirname,'dist','[name].manifest.json'), // Describe the generated manifest file})]}Copy the code

    The name value of the DllPlugin parameter must be the same as the output.library value, and the generated manifest file will reference the output.library value.

    The resulting file:

    | - polyfill. DLL. Js | -- polyfill. Manifest. Json | -- the react. DLL. Js └ ─ ─ the react. The manifest. JsonCopy the code
    1. Use the DllReferencePlugin to import xx.manifest.json in the main config file
    //webpack.config.json const path = require('path'); const DllReferencePlugin = require('webpack/lib/DllReferencePlugin'); module.exports = { entry:{ main:'./main.js' }, //... Omit the output, such as loader configuration plugins: [new DllReferencePlugin ({manifest: the require (". / dist/react. The manifest. Json ')}). new DllReferenctPlugin({ manifest:require('./dist/polyfill.manifest.json') }) ] }Copy the code

    The final build generates main.js

  • Use HappyPack to enable multi-process Loader conversion

    In the whole construction process, the most time-consuming is the Loader’s file conversion operation, and the Webpack running on Node.js is a single-threaded model, that is, it can only be processed one file at a time, not in parallel. HappyPack can split tasks into child processes and send results to the main process. JS is a single-threaded model and can only be improved in this multi-process manner.

    npm i -D happypack // webpack.config.json const path = require('path'); const HappyPack = require('happypack'); module.exports = { //... Module :{rules:[{test:/\.js$/,  use:['happypack/loader?id=babel'] exclude:path.resolve(__dirname, 'node_modules') }, { test:/\.css/, use:['happypack/loader?id=css'] } ], plugins:[ new HappyPack({ id:'babel', loaders:['babel-loader?cacheDirectory'] }), new HappyPack({ id:'css', loaders:['css-loader'] }) ] } }Copy the code
  • Enable multi-process compressed JS files using ParallelUglifyPlugin

2. Optimize the development experience

After you modify the source code during development, you need to automatically build and refresh the browser to see the effect. This process can be automated using Webpack, which listens for changes to files, and DevServer, which refreshes the browser.

  • DevServer uses automatic refresh

  • Enable the module hot replacement HMR

3. Optimize output quality – Compress file volume

  • UglifyJSPlugin: Remove invalid codes in JS, remove log input codes, shorten variable names and other optimizations.

  • UglifyESPlugin: Compress ES6 code when running it directly.

  • Compress CSS:

    css-loader? minimize

    PurifyCSSPlugin

  • Removing dead code that you don’t need. It works only if the code is modular in ES6.

4. Optimize output quality – speed up network requests

4.1 Use CDN to accelerate static resource loading
  1. The principle of CND acceleration

    By deploying resources around the world, CDN enables users to access resources nearby and speeds up access. To access the CDN, you need to upload static resources of web pages to the CDN service. When accessing these resources, the URL provided by the CDN service is used.

    CDN will enable caching for resources for a long time. For example, if a user obtains index. HTML from the CDN, the user will still use the previous version until the cache expires, even if the index. HTML is replaced later. Industry practice:

    • HTML files: store them on your own server with cache disabled and do not access CDN.

    • Static JS, CSS, images and other resources: enable CDN and cache, and file name with Hash value calculated by the content, so that the Hash will change as the content changes, the file name will change, and it will be re-downloaded regardless of how long the cache time.

    In addition, under the HTTP1.x protocol, browsers limit the number of concurrent requests to the same domain name to 4 to 8. Therefore, all static resources can be placed on different CDN services, such as JS files under js.cdn.com and CSS files under css.cdn.com. This brings a new problem: increased domain name resolution time, which can be solved by dnS-prefetch to reduce domain name resolution time. Urls such as //xx.com omit the protocol. The advantage of this is that the browser automatically determines whether to use HTTP or HTTPS when accessing resources based on the mode of the current URL.

  2. In summary, the build needs to satisfy the following points:

  • The STATIC resource import URL is changed to the absolute path to the CDN service

  • The file name of a static resource must have a Hash value calculated based on the content

  • Different types of resources are stored on CDN of different domain names

4.2 Multi-page applications extract common code between pages to take advantage of caching
  • Webpack4 previously used the CommonsChunkPlugin plugin for common module extraction

  • Webpack4 uses SplitChunksPlugin plug-in for common module extraction

module.exports = { optimization: { splitChunks: { cacheGroups: { vendor: { name: "vendor", test: / / \ \ / node_modules / \ \ / /, chunks: "all", priority: 10 / / priority}, common: {name: "common", the test: /[\\/]src[\\/]/, minSize: 1024, chunks: "all", priority: 5 } } } }, };Copy the code

5. Optimize output quality – improve code runtime efficiency

5.1 Pre-evaluation using Prepack (immature)
5.2 Use the Scope collieries

6. Use the output analysis tool webpack-bundle-Analyzer

7. Other Tips

  • When configuring babel-loader, use: [‘ babel-loader? CacheDirectory ‘] cacheDirectory is used to cache Babel compilation results and speed up recompilation. Also note that the node_modules folder is excluded, since the files use ES5 syntax and there is no need to use Babel conversion.

  • Configure externals to exclude code that does not need to be packaged because it has been introduced using the

    Let’s say we developed a library that references the LoDash package, and when we went through webPack packing, we realized that if we put the LoDash package into it, the package file would be very large. So we can externals it in. In other words, your own library does not package the LoDash itself, but the user environment provides it.

    externals: { "lodash": { commonjs: Import _ from 'lodash' = require('lodash') commonjs2: "lodash", // if our library is running in node.js, import _ from 'lodash' = const _ = require('lodash') commonjs2: "lodash", // same as amd: "Lodash ", // if our library is loaded with require.js etc., equivalent to define(["lodash"], factory); Root: "_" // If our library is used in browsers, we need to provide a global variable '_', equivalent to var _ = (window._) or (_); }}Copy the code

    In general, externals is configured so that the code import _ from ‘lodash’ can be interpreted in any environment without introducing Lodash itself.

  • Configuring the performance parameter outputs the performance check configuration of the file.

  • Configure profile: true to capture performance information for Webpack builds to analyze what is causing the builds to perform poorly.

  • Configure cache: true, whether to enable caching to improve build speed.

  • You can use url-loader to convert small images into Base64 and embed them in JS or CSS to reduce loading times.

  • Compress the image through imagemin-webpack-Plugin and make Sprite image through Webpack-Spritesmith.

  • Set devtool to cheap-module-eval-source-map in your development environment because this source map is the fastest to generate and speeds up builds. Set devtool to cheap-module-source-map in production.

Webpack performance optimization practices

Speed up packing

1> Exclude and include are used to exclude or include modules in a specified directory. For example, babel-loader sets exclude node_modules. The JS files in node_modules are already compiled to ES5 and no conversion is necessary. Exclude and include exist at the same time. Exclude has a higher priority.

2> Set cacheDirectory in babel-loader to cache Babel compilation results and speed up recompilation.

3> @babel/preset- Env’s modules configuration item set to false disables the conversion of module statements, otherwise tree-shaking is invalid.

4> commonsChunksPlugin and splitChunks can reduce heavy module packaging, improve packaging speed, and reduce resource volume.

Improve request efficiency

1> Output publicPath, can use CDN link, users can request resources more quickly

Reduce the resource file size

1 > uglify. Js and terser. Js

Refer to the article

Understand webpack principles and write a 100-line Webpack by hand