Webpack Basics _ Optimization

Webpack optimizes configuration

DLL: dynamic link library

DLL, like External, indicates which libraries webPack does not participate in packaging; Some libraries can be individually packaged, with multiple libraries packaged into a single chunk

  • Purpose: Separate third-party libraries into different chunks for better performance optimization

  • Download the plugin

npm i add-asset-html-webpack-plugin -D
Copy the code
  • Modify the configuration
// webpack.config.js
// nodejs
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
// Separate CSS from JS into a separate file
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// CSS code compression
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
// pwa
// const WorkboxWebpackPlugin = require('workbox-webpack-plugin')

// dll
const webpack = require('webpack')
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin')

// Define the nodeJS environment variable: Determines which environment to use browserslist
// process.env.NODE_ENV = 'production'
// process.env.NODE_ENV = 'development'

/ / loader reuse
const commonCSSLoader = [
  // 'style-loader',
  // Extract the CSS code to a specified file
  MiniCssExtractPlugin.loader,
  / question: * * * * MiniCssExtractPlugin loader current less file can not be extracted separately in the specified file * /
  // Load the CSS into the js
  'css-loader'.* postCSS -> * postCSs-loader -> * postCSs-preset -env: Helps PostCSS recognize certain environments and load the specified configuration */
  {
    /** * You need to define the browserslist configuration */ in package.json
    loader: 'postcss-loader'.options: {
      // Tell it what compatibility configuration to do
      ident: 'postcss'.plugins: () = > [
        /** * PostCSS plug-ins * help PostCSS find the configuration in package.json browserslist, * load the specified CSS compatibility styles through the configuration */
        require('postcss-preset-env'(),],},},];// commonjs
module.exports = {
  // entry: ['./src/js/index.js', './src/index.html'],
  entry: './src/js/index.js'.output: {
    /** * Add a hash value to the file to prevent caching: [hash:8] */
    filename: 'js/built.[contenthash:8].js'.path: resolve(__dirname, 'build'),},module: {
    rules: [{* eslint-loader * eslint * set check rules in eslintConfig of package.json, recommend Airbnb */
        test: /\.js$/,
        exclude: /node_modules/.// Execute this loader first
        enforce: 'pre'.loader: 'eslint-loader'.options: {
          // Automatically fixes ESLint errors
          fix: true,}}, {/** * oneOf: improves the speed of packing builds by preventing each type of file from being judged by the following loader at once * The loader in oneOf will only match one * note: No more than two configurations in oneOf can handle the same type of file * if they do, they need to be taken out of oneOf */
        oneOf: [
          // CSS code handling
          {
            test: /\.css$/,
            use: [
              ...commonCSSLoader,
            ],
          },
          {
            test: /\.less$/,
            use: [
              ...commonCSSLoader,
              // MiniCssExtractPlugin.loader,
              // 'css-loader',
              'less-loader',]},/** * Normally, a file can only be processed by one loader; * When a file is to be processed by multiple Loaders, you must specify the order in which the loader executes it * for the.js file as follows: esLint should be executed before bable, using the Enforce attribute */
          // js syntax check: normalize project code to check for common syntax errors

          {
            // PRESET handling: babel-loader, @babel/preset-env, @babel/core
            test: /\.js$/,
            exclude: /node_modules/,
            use: [
              /** * Enable multi-process packaging: */
              {
                loader: 'thread-loader'.options: {
                  workers: 2 // Enable only two processes. The default value is -1}}, {loader: 'babel-loader'.options: {
                  // Default: tell Babel what compatibility processing to do
                  presets: [[// Can only do simple syntactic compatibility processing
                      '@babel/preset-env'.// Corejs can do complex syntactic compatibility processing
                      {
                        // Load as needed
                        useBuiltIns: 'usage'.// Specify the core-js version
                        corejs: {
                          version: 3,},// Specify which version of the browser is compatible with
                        targets: {
                          chrome: '60'.firefox: '50'.safari: '10'.edge: '17'.ie: '9',},},],/** * Enable the Babel cache * on the second build, the cache will be read to speed up the build */
                  cacheDirectory: true}}]},// Process image resources
          {
            test: /\.(png|gif|jpg|jpeg)$/,
            loader: 'url-loader'.options: {
              // Base64 processing can be applied to images smaller than 8-12KB
              limit: 8 * 1024.// Turn off ES6 parsing and use CommonJS parsing
              esModule: false.name: '[hash:10].[ext]'.outputPath: 'imgs',}},// Process img resources in HTML
          {
            test: /\.html$/.// Process img resources in HTML
            loader: 'html-loader',},// Process other resources
          {
            exclude: /\.(html|js|css|less|png|gif|jpg|jpeg)/.// Output all other resources intact
            loader: 'file-loader'.options: {
              name: '[hash:10].[ext]'.outputPath: 'media',},},],},plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'.// HTML code compression
      minify: {
        collapseWhitespace: true.removeComments: true,}}),new MiniCssExtractPlugin({
      // Put the extracted CSS code into a unified file and name it
      filename: 'css/built.[contenthash:8].css',}).// CSS code compression
    new OptimizeCssAssetsWebpackPlugin(),
    / DLL: * * * * webpack DllReferencePlugin: tell webpack which libraries do not participate in the packaging, * when used at the same time also want to change the name of the * /
    new webpack.DllReferencePlugin({
      manifest: resolve(__dirname, 'dll/manifest.json')}),/** * DLL's support plug-in * brings in a packaged file and automatically introduces the resource in HTML */
    new AddAssetHtmlWebpackPlugin({
      filepath: resolve(__dirname, 'dll/jquery.js')})/** * pwa: * 1. Help serviceworker start quickly * 2. Delete the old Serviceworker * Finally: Generate a Serviceworker profile * and register serviceWorker */ in the entry file
    // new WorkboxWebpackPlugin.GenerateSW({
    // clientsClaim: true,
    // skipWaiting: true
    // })].mode: 'production'.// mode: 'development',
  /** * externals: * Adds an unpackaged third-party library */
  // externals: {
  // // Ignored library name -> NPM package name
  // jquery: 'jQuery'
  // },
  devServer: {
    contentBase: resolve(__dirname, 'build'),
    compress: true.port: 3030.open: true.// Enable HRM function
    hot: true,},/ source - the map Settings: * * * * [inline - | hidden - | eval -] [nosources -] [being - [module -]] source - the map * source - the map: inline * error: Error code Exact information and source code error location * inline-source-map: inline * 1. Put the generated.map file in the.js file * 2. Generate only an inline source-map file * Error message: error code exact information and source code error location * hidden-source-map (prevent source code leakage) : external * 1. Put a generated source-map file in a separate.map file * Error: error code error cause, but no error location, * cannot trace source code error, only error location of built code * eval-source-map: inline * 1. A separate source-map file is generated for each file and placed in the eval function of the.js file. Error code exact information, but without any source code information (to prevent source code leakage) * cheap-source-map: external * Error message: Exact information about error code and source code location, accurate only down to line * cheap-module-source-map: * Module will add loader source-map to * */
  // Enable the post-build code to source mapping
  devtool: 'eval-source-map' // development
  // devtool: 'source-map' // production
};

Copy the code
  • Adding a Configuration File
// webpack.dll.js
const { resolve } = require("path");
const webpack = require("webpack");

/** * Need to add a configuration file * use DLL technology, the specified library (third-party libraries: jquery, React, vue...) * When the webapck directive is run, the default is to look for the webpack.config.js configuration file * Needs: You need to run the webpack.dll. Js configuration file * -> webpack --config webpack.dll
module.exports = {
  entry: {
    /** * jquery -> final package generated name * ['jquery'] -> packaged library is jquery */
    jquery: ['jquery']},output: {
    // filename: '[name].[contenthash:8].js'
    filename: '[name].js'.path: resolve(__dirname, 'dll'),
    // Library: What is the name of the content exposed to the outside of the specified packaged library
    / / such as: jquery_325221b0
    library: '[name]_[hash:8]'
  },
  plugins: [
    // webpack.DllPlugin: Package to generate a manifest.json -> provide mapping to jquery
    new webpack.DllPlugin({
      // name: the name of the content exposed by the mapping library
      name: '[name]_[hash:8]'.// Specify the path to the output file
      path: resolve(__dirname, 'dll/manifest.json')})],mode: 'production'
}
Copy the code
  • Run the command:
npx webpack --config webpack.dll.js
npx webpack
Copy the code

Summary of Webpack performance optimization

Development environment performance optimization

The main concerns are packaging speed and code debugging

Optimized package build speed: HMR hot module replacement

When building code, only modified modules are built, and other modules use caching to speed up code construction and make the development experience better

  • CSS: After style-loader processing, the HMR function is available
  • Js: It needs to be written by developers themselves
  • HTML: This feature is not required

Optimized code debugging: source-map

To make debugging more user-friendly in a development environment, figure out what each value means and use the recommended notation

  • In the development environment:
  1. Eval-source-map (recommended) : Fast
  2. Eval-cheap-module-source-map: package information is complete
  • Production environment:
  1. Source-map (recommended) : indicates a complete mapping
  2. Cheap -module-source-map: fast speed and complete information

Production environment performance optimization

Optimize packaging build speed:

oneOf:

Reduce loader traversal. In oneOf, a file type processed by the corresponding loader will not be traversed by other loaders

Babel cache:

Optimize the packaging build speed by caching the results of the first Babel build and using caching directly in subsequent packaging to improve the packaging build speed

Multi-process packaging:

The default packaging is single inheritance. Enabling multi-process packaging can improve the packaging speed. This can be used when the program is large, as each process takes approximately 600ms to start

Externals:

Make some libraries unpackaged, use additional CDN, etc., to introduce unpackaged libraries in HTML files

DLL:

Pack some libraries locally and import them when you use them.

Consider combining DLLS with code split to package the libraries in node_modules separately to reduce file size and speed up the process

Optimize the performance of code execution

File caching (Hash, chunkhash,contenthash)
  • Hash: When webpack is packaged, a new unique hash value is generated
  • Chunkhash: If the webpack entry is the same, it belongs to the same chunk, and the value of chunkhash is the same. Because CSS files and JS files belong to the same chunk, when js changes, the CSS chunkhash also changes
  • Contenthash: Generates a contenthash value based on the contents of the file; The contenthash value is guaranteed to remain the same if the contents of the file remain the same when repackaging
The tree shaking:

Reduce code size by removing code files that are referenced but not used

  • ES6 modularization must be enabled
  • The Webapck production environment starts by default
  • sideEffect[…] : Declare files that cannot be deleted, such as style files, Babel files…
Code split: code split
  • Single entry: Import as a chunk and export as a bundle
  1. Split a total JS file in a bundle into smaller JS files
  2. Use optimization to process the node_modules file
  3. Files imported through import automatically split the code
  • Multiple entries: Multiple entries import multiple chunks and export multiple bundles
  1. optimization
  2. import
Lazy loading/preloading
  • Lazy loading: some JS code is not loaded at first and is loaded only when needed; Need to be used with code split: asynchronous + code split
  • Preloading (poor compatibility) : Secretly loading other code after all the required code has been loaded
Pwa (Compatibility) : Offline accessibility technology

Websites can also be accessed offline

  • serviceWorker
  • cach

Webpack configuration details

entry:

string: ‘./src/index.js’

A bundle file, such as built-in. Js, is generated. In this case, the default name of chunk is main.js

array: […]

Array multiple entries, all entries will eventually form a chunk, the final output is only a bundle file

  • What it does: In HMR, allows HTML hot updates to take effect

Object: {… }

The object has multiple entries. If there are several entries, several chunks are generated. As a result, the corresponding number of bundle files are output

Special usage:

2 and 3 are used together and packaged according to the rules of 2 and 3 respectively. As in DLL

webpack.config.js

const HtmlWebpackPlugin = require("html-webpack-plugin");
const { resolve } = require("path");
/** * entry: * 1. String: './ SRC /index.js' * string single entry, packaged into a chunk, output a bundle file, such as built. Js * If [name].js is used, the default name of chunk is main.js * 2. array: [...]. * Array multiple entries, all entries end up as a chunk, and the final output is a bundle file *. } * Objects have multiple entries, and several entries form several chunks, resulting in the output of the corresponding number of bundles * in this case, the name of the chunk is the corresponding key * * 4. Special usage: * Use 2 and 3 together, and pack according to the rules of 2 and 3 respectively; Such as the use of */ in DLL
module.exports = {
  // entry: './src/index.js',
  // entry: [
  // './src/index.js',
  // './src/add.js'
  / /,
  // entry: {
  // index: './src/index.js',
  // add: './src/add.js'
  // },
  entry: {
    index: ['./src/index.js'.'./src/count.js'].add: './src/add.js'
  },
  output: {
    filename: '[name].js'.// filename: 'built.js',
    path: resolve(__dirname, 'built')},plugins: [
    new HtmlWebpackPlugin()
  ],
  mode: 'development'
}
Copy the code

output:

  • Filename: filename (specified name + directory), e.g. ‘js/built.js’
  • Path: output file directory (public directory for future output of all resources)
  • PublicPath: publicPath prefix for all resources, ‘imgs/a.jpg’ -> packaged ‘/imgs/a.js’
  • ChunkFilename: indicates the chunk name of a non-import file
    1. Import asynchronous loading
    2. optimization
  • Library: The entire library exposes a global variable named, which is usually used in conjunction with DLLS
  • LibraryTarget: Specifies where exposed global variables are mounted
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { resolve } = require("path");
/** * output: export * filename: filename (specified name + directory) 'js/built.js' * path: output file directory (public directory for future output of all resources) * publicPath: Import public path prefix for all resources, 'imgs/a.jpg' -> packaged '/imgs/a.js' * chunkFilename: * 1. Import async loading * 2. Optimization * library: the entire library exposes a global variable name, generally used in conjunction with DLLS. Specifies where the exposed global variable is mounted */
module.exports = {
  entry: './src/index.js'.output: {
    filename: 'js/[name].js'.// filename: 'built.js',
    path: resolve(__dirname, 'built'),
    publicPath: '/'.chunkFilename: 'js/[name]_chunk.js'.library: '[name]'.// browser
    libraryTarget: 'window'.// node
    // libraryTarget: 'global',
    // Expose global variables using commonJS syntax rules
    // libraryTarget: 'commonjs'
  },
  plugins: [
    new HtmlWebpackPlugin()
  ],
  mode: 'development'
}
Copy the code

module:

  • test
  • exclude
  • include
  • enforce
  • loader
  • options
  • oneOf
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { resolve } = require("path");

module.exports = {
  entry: './src/index.js'.output: {
    filename: 'js/[name].js'.// filename: 'built.js',
    path: resolve(__dirname, 'built'),
    publicPath: '/'.chunkFilename: 'js/[name]_chunk.js'.library: '[name]'.// browser
    libraryTarget: 'window'.// node
    // libraryTarget: 'global',
    // Expose global variables using commonJS syntax rules
    // libraryTarget: 'commonjs'
  },
  module: {
    rules: [{test: /\.css$/.// Multiple loaders use the array use
        use: [
          'use-loader'.'css-loader'] {},test: /\.js$/.// Exclude js files under node_modules
        exclude: /node_modules/.// Check only js files under SRC
        include: resolve(__dirname, 'src'),
        // Pre takes precedence over POST
        // enforce: 'pre',
        // Single loader uses loader
        loader: 'eslint-loader'.// options Specifies loader configuration
        // options: {}
      },
      {
        In oneOf, only one loader of the same type is executed
        oneOf: []}]},plugins: [
    new HtmlWebpackPlugin()
  ],
  mode: 'development'
}
Copy the code

resolve:

  • Alias: specifies the path alias of the resolution module
  • Extensions: indicates the suffix name of the omitted file path
  • Modules: Tells WebPack which directory to go to for parsing modules
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { resolve } = require("path");
/** * resolve: resolve module rules * 1. Alias: configure resolve module path alias * 2. Extentions: configure omit file path suffix * 3
module.exports = {
  entry: './src/js/index.js'.output: {
    filename: 'js/[name].js'.// filename: 'built.js',
    path: resolve(__dirname, 'built'),
    // publicPath: '/',
    chunkFilename: 'js/[name]_chunk.js'.// library: '[name]',
    // browser
    // libraryTarget: 'window',
    // node
    // libraryTarget: 'global',
    // Expose global variables using commonJS syntax rules
    // libraryTarget: 'commonjs'
  },
  module: {
    rules: [{test: /\.css$/.// Multiple loaders use the array use
        use: [
          // Style can be seen in the browser console while the program is running
          'style-loader'.'css-loader']]}},plugins: [
    new HtmlWebpackPlugin()
  ],
  mode: 'development'.resolve: {
    // Configure the resolution module path alias
    alias: {
      $css: resolve(__dirname, 'src/css')},// Omit the file path suffix
    extensions: [
      '.js'.'.json'].// Tells Webpack which directory to look for
    modules: [
      resolve(__dirname, '.. /.. /node_modules'),
      'node_modules']}}Copy the code

optimization:

  • SplitChunks: Extract the common code node_modules into separate chunks and package them separately
  • RuntimeChunk: addresses hash values of splitChunks
  • Minimizer: Compression scheme for configuring production environment: JS and CSS
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { resolve } = require("path");
const Terser = require('terser-webpack-plugin')
/** * optimization: execute */ only in production environment
module.exports = {
  entry: './src/js/index.js'.output: {
    filename: 'js/[name]_[contenthash:8].js'.// filename: 'built.js',
    path: resolve(__dirname, 'built'),
    // publicPath: '/',
    chunkFilename: 'js/[name]_[contenthash:8]_chunk.js'.// library: '[name]',
    // browser
    // libraryTarget: 'window',
    // node
    // libraryTarget: 'global',
    // Expose global variables using commonJS syntax rules
    // libraryTarget: 'commonjs'
  },
  module: {
    rules: [{test: /\.css$/.// Multiple loaders use the array use
        use: [
          'style-loader'.'css-loader']]}},plugins: [
    new HtmlWebpackPlugin()
  ],
  mode: 'production'.resolve: {
    // Configure the resolution module path alias
    alias: {
      $css: resolve(__dirname, 'src/css')},// Omit the file path suffix
    extensions: [
      '.js'.'.json'].// Tells Webpack which directory to look for
    modules: [
      resolve(__dirname, '.. /.. /node_modules'),
      'node_modules']},devServer: {
    // Run the directory of packaged code
    contentBase: resolve(__dirname, 'build'),
    // Monitor all files in the contentBase directory and reload them if they change
    watchContentBase: true.// Monitor the configuration
    watchOptions: {
      // Ignore the file
      ignored: /node_modules/
    },
    // Start gzip compression to reduce code size
    compress: true./ / the port number
    port: 5000./ / domain name
    host: 'localhost'.// Open the default browser
    open: true.// Enable HMR
    hot: true.// Log content
    // Do not display log information about starting the server
    clientLogLevel: 'none'.// Do not display anything except some basic startup information
    quiet: true.// If something goes wrong, do not display it in full screen
    overlay: false./** * Server proxy: Solve the development environment cross-domain problem */
    proxy: {
      /** * Once devServer(5000) receives a request for/API/XXX, * will automatically forward the request to another server (3000) */
      '/api': {
        target: 'http://localhost:3000'.// request path rewrite: / API/XXX -> / XXX remove/API
        pathRewrite: {
          '^/api': ' '}}}},optimization: {
    // The code splits node_modules
    // Extract the common node_modules code into a separate chunk and package it separately
    splitChunks: {
      chunks: 'all'./** * The default values are as follows: */ is not required
      // The minimum chunk to be split is 30KB
      minSize: 30 * 1024.// Maximum no limit
      maxSize: 0.// The chunk to be extracted must be referenced at least once
      minChunks: 1.// The maximum number of files to be loaded in parallel when loading on demand
      maxAsyncRequests: 5.// entry js file, maximum number of parallel requests
      maxInitialRequests: 3.// Name concatenation
      automaticNameDelimiter: '~'.// You can use naming conventions
      name: true.cacheGroups: {
        /** * Groups that split chunks: also meets the common rule */
        vendors: {
          // node_modules files are packaged into vendors group chunk: Vendors ~ XXX.js
          // 
          test: /[\\/]node_modules[\\/]/./ / priority
          priority: -10
        },
        default: {
          // The chunk to be extracted must be referenced at least twice
          minChunks: 2./ / priority
          priority: -20.// If the currently packaged module is the same as the previously extracted module, it will be reused to solve the problem of repeated packaging
          reuseExistingChunk: true,}}},/** * runtimeChunk: hash chunks in splitChunks To prevent changes in the hash value of the packaged file from causing changes in the hash value of other references to this module to prevent cache invalidation */
    runtimeChunk: {
      name: entrypoint= > `runtime-${entrypoint.name}`
    },
    /** * Configure the compression scheme for the production environment: JS and CSS */
    minimizer: [
      // After version 4.26, Terser compression was used instead of Uglify compression
      new Terser({
        // Enable caching
        cache: true.// Enable multi-process packaging
        parallel: true./ / start the source - the map
        sourceMap: true}}})]Copy the code

devServer:

Refer to the following code for detailed configuration

const HtmlWebpackPlugin = require("html-webpack-plugin");
const { resolve } = require("path");
/** * devServer: */
module.exports = {
  entry: './src/js/index.js'.output: {
    filename: 'js/[name].js'.// filename: 'built.js',
    path: resolve(__dirname, 'built'),
    // publicPath: '/',
    chunkFilename: 'js/[name]_chunk.js'.// library: '[name]',
    // browser
    // libraryTarget: 'window',
    // node
    // libraryTarget: 'global',
    // Expose global variables using commonJS syntax rules
    // libraryTarget: 'commonjs'
  },
  module: {
    rules: [{test: /\.css$/.// Multiple loaders use the array use
        use: [
          'style-loader'.'css-loader']]}},plugins: [
    new HtmlWebpackPlugin()
  ],
  mode: 'development'.resolve: {
    // Configure the resolution module path alias
    alias: {
      $css: resolve(__dirname, 'src/css')},// Omit the file path suffix
    extensions: [
      '.js'.'.json'].// Tells Webpack which directory to look for
    modules: [
      resolve(__dirname, '.. /.. /node_modules'),
      'node_modules']},devServer: {
    // Run the directory of packaged code
    contentBase: resolve(__dirname, 'build'),
    // Monitor all files in the contentBase directory and reload them if they change
    watchContentBase: true.// Monitor the configuration
    watchOptions: {
      // Ignore the file
      ignored: /node_modules/
    },
    // Start gzip compression to reduce code size
    compress: true./ / the port number
    port: 5000./ / domain name
    host: 'localhost'.// Open the default browser
    open: true.// Enable HMR
    hot: true.// Log content
    // Do not display log information about starting the server
    clientLogLevel: 'none'.// Do not display anything except some basic startup information
    quiet: true.// If something goes wrong, do not display it in full screen
    overlay: false./** * Server proxy: Solve the development environment cross-domain problem */
    proxy: {
      /** * Once devServer(5000) receives a request for/API/XXX, * will automatically forward the request to another server (3000) */
      '/api': {
        target: 'http://localhost:3000'.// request path rewrite: / API/XXX -> / XXX remove/API
        pathRewrite: {
          '^/api': ' '
        }
      }
    }
  }
}
Copy the code

webpack5:

This is primarily an optimization for Tree Shaking

module.exports = {
  // entry: './src/index.js',
  // output: {
  // filename: '[name].js',
  // path: resolve(__dirname, 'dist')
  // },
  /** ** The above configuration is the default, do not configure */
  // mode: 'development'
  mode: 'production'
}
Copy the code