Have synchronous language finch: www.yuque.com/go/doc/5319…

Github:www.yuque.com/go/doc/5319…

This series is an introduction to Webpack and includes:

1, Webpack – 【 introduction – first part 】juejin.cn/post/695439…

2, Webpack – [Introduction – mid] juejin.cn/post/695438…

3, Webpack – 【 introduction – Part 2 】【 this article 】juejin.cn/post/695438…

17 Optimize performance configuration

Divided into two

  • Development environment performance optimization

  • Optimize packaging build speed

  • HMR

  • Optimized debugging function

  • * * * * s ource – map (devtool)

  • Production environment performance optimization

  • Optimize packaging build speed

  • one-of

  • Babel cache

  • Multi-process packaging

  • externals

  • dll

  • Optimize code performance (small size)

  • Cache (hash-chunkhash-contenthash)

  • tree-shaking

  • code-split

  • Lazy loading/preloading

  • PWA

Specific:

HMR: hot

Description: Hot Module replacement/hot module replacement

What it does: When a module changes, it repackages only that module, not all modules, greatly improving the build speed

  • Style files: You can use the HMR functionality because style-loader is implemented internally

  • Js file: no HMR function by default, repackage all modules

  • Note: the HMR function for JS can only handle non-entry files, because entry files import all the content, entry files change, other files change

  • HTML files: there is no HMR function by default, and this causes problems: HTML files cannot be hot updated (HMR function is not required)

  • Solution: Modify the entry entry to import the HTML file

Use:

webpack.config.js

module.exports = { ... Target: 'web', // automatic update devServer: {hot: true // hot module replacement}}Copy the code

main.js

// import './print.js' if(module.hot){// Once module.hot is true, Module.hot.accept ('./print.js', function(){// the method listens for print.js. Other modules will not be repackaged to build // will execute the following callback function print()})}Copy the code

The source – the map (devtool)

Explanation: A technique that provides source code to post-build code mapping

What it does: If the build code is wrong, the mapping can trace the original code error

Devtool: ‘eval-source-map’; Devtool for production: ‘source-map’

webpack.config.js

module.exports = {
  ...
  devtool: 'source-map',
}
Copy the code

When you execute Webpack, you can see the main.js.map file under the packaged dist folder, as shown below

Specific configuration:

[inline-|hidden-|eval-][nosources][cheap-[module-]]source-map

Configuration fields

explain

The wrong location

source-map

Externally, generate a separate file

Error code exact information, and source code error location, down to line and column

inline-source-map

Inline, does not generate a separate file, the whole inside the main.js file under the extension, only generates a source-map, fast construction

With the source – the map

hidden-source-map

Externally, generate a separate file, main.js.map

Error code exact information, and the error location of the built code, can not trace the source code error; Hide only the source code

eval-source-map

The sourceMappgingURL field is appended to each file in main.js file. Each file generates a source-map

With the source – the map

nosources-source-map

Externally, generate a separate file

Error code accurate information, cannot trace source code and post-build code errors; To hide the source code, hide it all

cheap-source-map

Externally, generate a separate file

Same as source-map, but only accurate to rows, whole rows, not columns

cheap-module-source-map

Externally, generate a separate file

Same as cheap-source-map, but module will add loader source-map

Other combinations…

How does it work?

  • The development environment

  • Speed is fast

  • eval > inline > cheap > … The eval – being – the source – the map > eval – source – the map

  • Debugging friendly

  • source-map > cheap-module-source-map > cheap-source-map

  • Eval-source-map/eval-cheap-module-source-map

Use vue and React scaffolding development, using eval-source-map by default

  • The production environment

  • Hiding source code

  • Nosoures – source – the map, hidden – source – the map

  • Small volume

  • Inlining makes the volume very large, so inlining cannot be used in production environments

  • Debugging friendly

  • Same as above

  • In conclusion, the source – the map

one-of

Only one loader is matched

A file will only match one loader. If a file matches one loader, the following file will not be used. Increase build speed

Note: You cannot have two configurations that handle the same type of file, such as js files, eslint-loader, and babel-loader; One needs to be extracted outside oneof

webpack.config.js

{rules: [{test: /\.js$/, exclude: /node_modules/, loader: 'eslint-loader', enforce: 'pre', // Specify the sequence, execute options first: {fix: true,},}, // Note: two configurations cannot handle the same type of files, such as js files, eslint-loader, babel-loader; one needs to be extracted from oneOf outside oneOf: [{test: /\.css$/, use: [...commonCssLoader], }, { test: /\.less$/, use: [...commonCssLoader, 'less-lodaer'],}, // Normally, a file can only be processed by one loader, so be sure to specify the sequence in which loader executes // eslint before executing Babel {test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader', options: { presets: [ [ '@babel/preset-env', { useBulitIns: 'usage', corejs: { version: 3, }, targets: { chrome: '60', firefox: '60', ie: '9', safari: '10', edge: '17', }, }, ], ], }, }, { test: /\.(jpg|png|gif)/, loader: 'url-loader', options: { limit: 8 * 1024, name: '[hash:10].[ext]', outputPath: 'imgs', esModule: false, // HTML is common, url-loader is es6, need to close},}, {test: / \. HTML/loader: 'HTML - loader', / / handle your img HTML}, {exclude: / \. (js | | CSS less | | HTML JPG | PNG | GIF) /, loader: 'file-lodaer', options: { outputPath: 'media', }, }, ], ] }Copy the code

Cache hash

  • Babel cache:CacheDirectory – > let the firstSecondary packaging builds faster

In the production environment, the cache is read when the file changes

{ test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader', options: { presets: [ [ '@babel/preset-env', { useBulitIns: 'usage', corejs: { version: 3, }, targets: { chrome: '60', firefox: '60', ie: '9', Safari: '10', edge: '17',},},],], cacheDirectory: true,Copy the code
  • ** File resource cache: ** Hash –> Make code live run cache easier to use

  • Hash: A unique hash value is generated each time a Webpack is built

  • Problem: Because JS and CSS use the same hash value, repackaging will invalidate all caches (changing only one file)

  • Chunkhash: Generates hash values based on chunk. If the packages are from the same chunk, the hash values are the same

  • Problem: CSS and JS still have the same hash value

  • Because CSS is introduced in JS, it belongs to the same chunk

  • Contenthash: Generates hash values based on the contents of files. Different files must have different hash values. Only changed files will generate hash values again

    Output: {filename: ‘main. [10] contenthash: js’, / / file resource cache’ main. [10] chunkhash: js’ | | ‘main. [10] hash: js’ path: resolve(__dirname, ‘dist’), publicPath: ‘/’, },

    .

    new MiniCssExtractPlugin({ filename: ‘CSS/index. [10] contenthash: CSS’, / / file resource cache ‘CSS/index. [10] hash: CSS’ | | ‘CSS/index. (hash) : 10] CSS’}),

tree-shaking

Function: Remove useless code, reduce code size

Prerequisite: es6 module must be used, mode = production environment is enabled by default tree-shaking, WebPack default UglifyJsPlugin implementation

It is best to configure “sideEffects”: [“*.css”,”*.less”] in package.json to prevent files from being shaken out

"SideEffects ": false, // all code has no sideEffects (tree shaking) ["*.css","*.less"], //.css, *.less is not tree shakingCopy the code

Code-split Code split

** Splits a chunk generated by packaging into multiple code blocks

  • Parallel loading, speed up
  • Load on demand, (SPA, single file needs to be split according to route)

Methods:

  • Multientry segmentation

Problem: Difficult to configure multiple entry, inflexible

//webpack.config.js {// single entry // entry: './ SRC /main.js', // multiple entry, with one entry, the final output has a bundle entry: {main: "./src/main.js", print: "./src/print.js" }, output: { filename: '[name].[contenthash:10].js', } }Copy the code
  • Optimization configuration

    //webpack.config.js {optimization: {/* Node_modules can be packaged separately into a chunk of the final output 2. SplitChunks: {chunks: ‘all’}}}

  • Import Dynamic import

    //main.js // import {mul, count} from ‘./print’

    . / / the console log (the mul (5, 2)) / / console log (count (5, 2))

    Import (/ webpackChunkName:); import(/ webpackChunkName:); ‘print’ */’./print’) .then(( {mul, Count}) = > {the console. The log (the mul (5, 2)) console. The log (count (5, 2))}). The catch (() = > {the console. The log (‘ file loading failure ‘)})

Execute webpack and you can see the result as shown below, print.js split into separate files

Lazy loading and preloading

Use code split – dynamic load implementation

//index.html <button id=" BTN "> </button> //main.js document.getelementById (' BTN ').onclick = function(){ Load the file only when the file is needed. Problem: When the file is too large, the loading time is too long, causing the user to wait. Normal loading && preloading Normal loading: In parallel loading, multiple files can be loaded at a time. A browser can load a maximum of six files in parallel at a time under a domain name. If the number exceeds the threshold, you need to queue up for preloading later: */ import(/* webpackChunkName: 'test', webpackPrefetch: True * / '/ test'), then (() = > {the console. The log (' record test. Js success ')}). The catch (() = > {the console. The log (' file loading failure ')})}Copy the code

PWA

Explanation: Progressive Web development application (also accessible offline)

Workbox –> workbox — webpack-plugin

// webpack.config.js const workboxWebpackPlugin = require('workbox-webpack-plugin') { ... plugins: [/ / PWA / / NPM I workbox - webpack - plugin - D new workboxWebpackPlugin. GenerateSW ({/ * 1. Help serviceworker quick start 2. Create a serviceWorker configuration file to register serviceWorker */ clientsClaim: true, skipWaiting: True})]} // main.js // PWA register serviceworker // Note that sw code must run on the server NPM I serve-g; Serve-s dist Startup server, If ('serviceWorker' in navigator){window.addeventListener ('load', () = > {the navigator. ServiceWorker. Register ('/service - worker. Js), then (() = > {the console. The log (' sw to register ')}). The catch (() = > { Console. log(' SW registration failed ')})})}Copy the code

Execute webpack and you can see

Multi-process packaging

Explanation: JS is single-threaded and queued to build slowly

Use: NPM I thread-loader -d

Both ways:

  • Disadvantages: process startup takes about 600ms, interprocess communication is also expensive, and multi-process packaging is required only when the work takes a long time, such as JS babel-loader

    // webpack.config.js { … { test: /.js$/, exclude: /node_modules/, use: { ‘thread-loader’, ‘babel-loader’ } }

    }

extranals

Description: External, reject certain packages to be packaged in the index. HTML CDN introduction

// webpack.config.js { ... Externals: {// reject jquery to be packaged; Jquery needs to be introduced in index.html CDN: 'jQuery' // package name}} // main.js // externals import $from 'jQuery' console.log($) // index.html <script crossorigin="anonymous" integrity="sha512-6ORWJX/LrnSjBzwefdNUyLCMTIsGoNP6NftMy2UAm1JBm6PRZCO1d7OHBStWpVFZLO+RerTvqX/Z9mBFfCJZ4A==" SRC = "https://lib.baomitu.com/jquery/3.6.0/jquery.slim.min.js" > < / script >Copy the code

dll

Explanation: Dynamic link library

Create webpack.dll. Js and do as follows

// webpack.dll. Js /* Use DLL technology for some libraries (third-party libraries: jQuery, react, vue...) By default, the webpack.config.js file is run: webpack --config webpack.dll.js */ const path = require("path") const webpack = require("webpack") module.exports = { ['jquery'] --> jquery jquery: ['jquery']}, output: {filename: '[name].js', path: path.join(__dirname, 'DLL '), Library: '[name]_[hash]', // What is the name of the content exposed in the library}, plugins: New webpack.DllPlugin({name: '[name]_[hash]', // Map library exposed content name path: Path. join(__dirname, 'DLL /manifest.json'), // output file path})], mode: "production"}Copy the code

Run webpack –config webpack.dll. Js and you can see the result of the package

Webpack. Config. Js configuration

// webpack.config.js // npm i add-asset-html-webpack-plugin -D { const webpack = require("webpack") const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin') ... Pluigns: [/ / DLL / / tell webpack which libraries don't participate in the packaging. When used at the same time also have to change the name of the new webpack. DllReferencePlugin ({manifest: Path.join (__dirname, 'DLL /manifest.json')}), // NPM I add-asset-html-webpack-plugin -d And automatically bring the resources in the HTML new AddAssetHtmlWebpackPlugin ({filepath: path. Join (__dirname, 'the DLL/jquery. Js)})]}Copy the code

When you execute WebPack, you can see that a single jquery has been generated and introduced in index.html

externals && dll

Externals: the CDN needs to be imported manually

DLL: only need to pack once, then only need to execute webpack; You don’t need to worry about third-party libraries after the second time. Speed up

18 Review: Summary of performance optimization

Divided into two

  • Development environment performance optimization

  • Optimize packaging build speed

  • HMR

  • Optimized debugging function

  • source-map

  • Production environment performance optimization

  • Optimize packaging build speed

  • one-of

  • Babel cache

  • Multi-process packaging

  • externals

  • dll

  • Optimize code performance (small size)

  • Cache (hash-chunkhash-contenthash)

  • tree-shaking

  • code-split

  • Lazy loading/preloading

  • PWA

19 Entry Detailed configuration

Entry: The starting point of the entry

  • String –> ‘./ SRC /index.js’ default value

  • Single entry

  • Bundle into a chunk and output a bundle file

  • The default name of chunk is main

  • array –> [‘./src/index.js’, ‘./src/add.js’]

  • Multiple entry

  • All the import files end up as a chunk, and only a bundle is exported. Add.js is packaged into index.js

  • The default name of chunk is main

  • Object – > as follows

  • Multiple entry

  • Several import files form several chunks and export several bundles

  • The name of chunk is key

    // string entry: ‘./src/index.js’

    // array entry: [‘./src/index.js’, ‘./src/add.js’]

    // object entry: { index: ‘./src/index.js’, add: ‘./src/add.js }

    / / special usage Such as DLL configuration entry: {index: [‘. / SRC/index. Js’, ‘. / SRC/count. Js’], the add: ‘the. / SRC/add. Js}

20 output Detailed configuration

Output: {// filename (specify name + directory) filename: 'js/[name].js', // output file directory (public directory for future output of all resources) path: Resolve (__dirname, 'build'), // All resources introduce the public path prefix --> imgs/a, JPG --> /imgs/a.jpg publichPath: '/', // the non-entry chunk name will be renamed, if not, filename will be used, both will be called main, webpack will be named with id 0.js 1.js 2.js... ChunkFilename: 'js/[name]_chunk.js', // the default js package is a function // the whole library exposed variable name library: '[name].js', // var main = (function(modules){}) 'window' // browser window['main'] = (function(modules){}) // libraryTarget: 'global' // node // libraryTarget: 'commonjs' // browser exports['main'] = (function(modules){}) }Copy the code

21 Module Detailed configuration

Use use: ['style-loader', 'css-loader']}, {test: ['style-loader', 'css-loader']}, /\.js$/, exclude: /node_modules/, include: resolve(__dirname, 'SRC '), enforce: 'pre', // 'post', // delay execution // Single loader using loader: 'eslint-loader', options: {... }, {// The following configuration will only take effect oneOf: []}}}Copy the code

22 Resolve Perform detailed configuration

Resolve: {$CSS: resolve(__dirname, 'SRC/CSS ')}, // Configure the extension name of the omitted file path: ['.js', '.json', '.css'], // Tells WebPack which directory to go to for parsing modules. By default, it goes to the current path level and can't find modules at the upper level: [resolve(__dirname, '../../node_modules'), 'node_modules']Copy the code

23 Configure devServer

// devServer: {// Run code directory contentBase: Resolve (__dirname, 'build'), // Monitor all files in contentBase, reload watchContentBase: true, watchOptions: Ignored: /node_modules/}, // Enable gzip compress: true, // Port number: 50000, // domain name host: 'localhost', // automatically open browser open: true, // enable HMR function hot: true, // Do not display startup server log information clientLogLevel: 'None ', // Do not display quite: true except for some basic information, // If there is an error, do not display full screen overlay: false, // server proxy --> resolve cross-domain issues in development environment proxy: {'/ API ': { target: 'http://localhost:3000', pathRewrite: { '^/api': '' } } } }Copy the code

24 Optimization detailed configuration

Const TerserWebpackPlugin = require('terser-webpack-plugin') // Optimization: {splitChunks: {chunks: "All ", // The following is the default value, you can not write miniSize: 30 * 1024, // the minimum chunk to be divided is 30KB, and the chunk to be divided is larger than 30KB. MaxSize: 0, // The maximum is not limited minChunks: MaxAsyncRequest: 5, // maxInitialRequests: AutomaticNameDelimiter: '~', // Name the connector name: true, // you can use the naming rule cacheGroups: {// Groups that split chunks // node_modules files are packed into vendors group chunk => Vendors ~xxx.js // Satisfy the common rules above, e.g., vendors~xxx.js are larger than 30KB and must be referenced at least once, VENDORS: {test: /[\\/]node_modules[\\/]/, // priority: -10}, defalut: {// Chunks to be extracted are referred to at least twice, // priority: -20, // If the current module to be packaged is the same as the previously extracted module, it will be reused instead of reuseExistingChunk: RuntimeChunk: {name: {contenthash: {contenthash: {contenthash: {contenthash: {contenthash: {contenthash: {contenthash: {contenthash: {contenthash: {contenthash: {contenthash: {contenthash: {contenthash: {contenthash: {contenthash: {contenthash: {contenthash:} entrypoint => `runtime-${entrypoint.name}` }, minimizer: New TerserWebpackPlugin({// enable caching: true, // enable multi-threaded packaging parallel: True, // enable sourceMap: true}}}Copy the code

Terser webpack – plugin (webpack.docschina.org/plugins/ter…).

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 are using Webpack V4, you must install a version of Terser-webpack-Plugin V4 instead of UglifyJsPlugin

Have synchronous language finch: www.yuque.com/go/doc/5319…

Github:www.yuque.com/go/doc/5319…

This series is an introduction to Webpack and includes:

1, Webpack – 【 introduction – first part 】juejin.cn/post/695439…

2, Webpack – [Introduction – mid] juejin.cn/post/695438…

3, Webpack – 【 introduction – Part 2 】【 this article 】juejin.cn/post/695438…