webpack learning notes

1 Introduction

Environment: nodejs 10 +, webpack 4.26+

webpack: front-end resource building tool and static module packer, which treats all front-end resources as modules, analyzes dependencies and packages them into static resource bundle s. You can compile and transform. The browser cannot recognize the latest syntax: es6 or less. webpack is equivalent to a collection of tools, which contains many gadgets.

Process: enter from the portal, analyze various resources that the portal file depends on, and introduce them to form a chunk. Analyze and compile various resources in chunk to form a bundle.

2. Five core concepts of webpack

  • Entry: entry, which file is the starting point for webpack to analyze and build the dependency graph.
  • Output: the destination and naming rules of the packaged bundle output
  • Loader: extension, file type recognized by webpack (only js is recognized by default)
  • Plugins: plug-in with stronger functions, which is equivalent to function expansion
  • Mode: mode, development and production. Selecting different modes will automatically enable different plug-ins
    • Development: process.env.NODE_ENV=development, enable: NamedChunksPlugin and NamedModulesPlugin
    • Production: process.env.NODE_ENV=production, enable: FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin, UglifyJsPlugin

3 try

Global installation: NPM I webpack webpack cli - G
Local installation: NPM I webpack webpack cli - D

3.1 package js/json

Prerequisite preparation: create a new build directory, src directory, src/index.js file, and install webpack and webpack cli in the local npm

Execution: webpack. / SRC / index.js - O. / build / build. JS -- mode = development (will compile es6 modularization into browser recognizable modularization)

index.js Code:

import data from './data.json'

console.log(data)

function add(a,b){return a+b}
console.log(add(1,2))

data.json Code:

{
  "name":"pp",
  "age":12,
  "sex":"male"
}

Execution: webpack. / SRC / index.js - O. / build / build. JS -- mode = production (indicates the production environment and compresses the code)

3.2 packaging css/less

css files are imported into index.js, and then packaged normally. An error is reported when packaging, indicating that webpack does not support css/img by default and requires a loader to translate unrecognized files
The use of loader requires [download and configuration] to take effect

webpack.config.js configuration file. If you need to configure detailed parameters, create a configuration file to use
Special note: since the build tool is based on nodejs and adopts commonjs for modularization, the configuration file export uses: module.exports

const { resolve } = require('path')

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'built.js',
    // The path must be a full path, so use the path in nodejs to get it
    path: resolve(__dirname,'build')
  },
  // loader configuration
  module: {
    rules: [
      // Detailed configuration
      {
        // test indicates the matching file type
        test: /\.css$/,
        // Which loader s are used and the execution order is from back to front
        use: [
          'style-loader', // Insert the style string in js into the style tag and put it into the head tag to take effect
          'css-loader' // Load the css file programming commonjs module into js, and the content is the style string
        ]
      }
    ]
  },
  // plugins plug-in configuration
  plugins: [

  ],
  mode: 'development',
  // mode: 'production'
}

If you need to package less: you need to configure the less loader, as shown below

To process less files, add a loader after css loader. Note: each loader can only process one file type

{
  test: /\.less$/,
  use: ['style-loader','css-loader','less-loader']
}

After configuration, relevant modules need to be downloaded: NPM I less less loader - D

3.3 packaging html

The html package uses a plug-in. The plug-in needs to be downloaded, imported and configured to take effect

Download: HTML webpack plugin: NPM I HTML webpack plugin - D
Import and configuration: webpack.config.js

const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'built.js',
    path: resolve(__dirname,'build')
  },
  // loader configuration
  module: {
    rules: []
  },
  // plugins plug-in configuration
  plugins: [
    new HtmlWebpackPlugin({  // By default, an empty html file is created and all resources of the packaged output are introduced. If a template is required, the configuration {template} needs to be passed in
      template: './src/index.html'  // Use this file as a template
    }) 
  ],
  mode: 'development',
  // mode: 'production'
}

Note: do not manually import resources in the html template file. They will be imported automatically. Just import resources in the entry file

3.4 packaged pictures

There are two kinds of referenced pictures:

  • url import in style file
  • Introduction of src in html

For the introduced in css style files, you need to install URL loader and file loader and configure webpack:

const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'built.js',
    path: resolve(__dirname,'build')
  },
  // loader configuration
  module: {
    rules: [
      {
        // test indicates the matching file type
        test: /\.css$/,
        // Which loader s are used and the execution order is from back to front
        use: [
          'style-loader', // Insert the style string in js into the style tag and put it into the head tag to take effect
          'css-loader' // Load the css file programming commonjs module into js, and the content is the style string
        ]
      },
      {
        test: /\.(jpg|png|gif)$/, // Multiple file types are written like this
        loader: 'url-loader', // A loader writes like this
        options: { // Additional configuration of this loader
          limit: 8*1024 // 8kb, less than this value will be encoded by base64
        }
      }
    ]
  },
  // plugins plug-in configuration
  plugins: [
    new HtmlWebpackPlugin({  // By default, an empty html file is created and all resources of the packaged output are introduced. If a template is required, the configuration {template} needs to be passed in
      template: './src/index.html'  // Use this file as a template
    }) 
  ],
  mode: 'development',
  // mode: 'production'
}

base64 image processing can reduce the number of requests and reduce the pressure on the server, but the image size may be larger, so it is generally used to process images below 8kb

For src imported in html: you need to Download html loader, which is specially used to handle src imported pictures in html so that they can be parsed by URL loader

{
  test: /.\html$/,
  loader: 'html-loader'
}

Here, we will find the wrong picture reference in html. The reason is: html loader is exported in common JS modularization, while URL loader is parsed in es6 modularization
Solution: turn off es6 modularization of URL loader and use the default commonjs parsing

{
   test: /\.(jpg|png|gif)$/, // Multiple file types are written like this
   loader: 'url-loader', // A loader writes like this
   options: { // Additional configuration of this loader
     limit: 8*1024, // 8kb, less than this value will be encoded by base64
     esModule: false // Turn off the es6 modularization of the loader and use commonjs for parsing (because the picture mode introduced after HTML loader parsing adopts commonjs)
   }
 }

Rename the imported image: name:'[hash:10].[ext], and take the top 10 digits of the image hash value

{
   test: /\.(jpg|png|gif)$/, // Multiple file types are written like this
   loader: 'url-loader', // A loader writes like this
   options: { // Additional configuration of this loader
     limit: 8*1024, // 8kb, less than this value will be encoded by base64
     esModule: false, // Turn off the es6 modularization of the loader and use commonjs for parsing (because the picture mode introduced after HTML loader parsing adopts commonjs)
     name:'[hash:10].[ext]'
   }
 }

3.5 packaging other resources

Other resources: for resources other than css/js/html, you can use exclude to match all resources except the excluded type. You only need to configure in the loader:

{
	 exclude: /\.(css|js|html)$/,
	  loader: 'file-loader',
	  options: {
	    name: '[hash:10].[ext]'
	  }
}

All resources excluding css/js/html will be packaged and processed by this loader

3.6 devServer

Configure the development server to automatically compile, package, open the browser, refresh and see the effect after the change

  // It will be packaged in memory without any output
  // Start: webpack dev server
  devServer: {
    contentBase: resolve(__dirname,'build'), // The directory to run the project. If HTML webpack plugin is configured, the configuration will not work
    compress: true,  // Start gzip compression
    port: 3000 // Port number
    open: true // Open browser automatically
  }

You need to install: NPM I - D webpack dev server first
Configure the instruction in package.json: "start": "webpack dev server", and then use npm run start to start
Note: after webpack 5, the startup command is changed to: webpack serve

Once the source code is modified and saved, it will be compiled automatically and displayed on the page

4 basic configuration of environment

4.1 development environment

Packaging: js, json, html, css, less, img, file and other resources

const {resolve} = require("path")
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'js/built.js', // The storage place of the entry file, which includes the combination of js and css
    path: resolve(__dirname,'build') // All resource storage locations
  },
  module: {
    rules: [
      {
        test: /\.less$/,
        use: ['style-loader','css-loader','less-loader']
      },
      {
        test: /\.css$/,
        use: ['style-loader','css-loader']
      },
      {
        test: /\.(jpg|png|jpeg|gif)$/,
        loader: 'url-loader',
        options: {
          limit: 8 * 1024,
          name: '[hash:10].[ext]',
          esModule: false,
          outputPath: 'imgs'
        }
      },
      {
        test: /.\html$/,
        loader: 'html-loader'
      },
      {
        exclude: /\.(html|js|css|less|jpg|png|jpeg|gif)$/,
        loader: 'file-loader',
        options: {
          name: '[hash:10].[ext]',
          outputPath: 'medias'
        }
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  // It will be packaged in memory without any output
  // Start: webpack dev server
  devServer: {
    contentBase: resolve(__dirname,'build'), // The directory in which the project is running
    compress: true,  // Start gzip compression
    port: 3000, // Port number
    open: true // Open browser automatically
  },
  mode: 'development'
}

4.2 production environment

In order to make the code faster, better performance and more stable (these need not be considered in the development environment)

  1. In the development environment, css and js are bundle s packaged together. In the loading process, first load js, and then create a style tag to load css, which will cause a flashing phenomenon
  2. The code needs to be compressed uniformly
  3. Style compatibility

4.2.1 extracting CSS to a separate file

Plug in to be installed: NPM I - D Mini CSS extract plugin
Step: register the plug-in and replace the style loader with MiniCssExtractPlugin.loader (sytle loader creates a style tag for the style string in js and inserts it into html. Now MiniCssExtractPlugin.loader extracts the style string in js into a separate file and introduces the style tag)

const {resolve} = require("path")
const MiniCssExtractPlugin = require('mini-css-extract-plugin')

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'js/built.js', // The storage place of the entry file, which includes the combination of js and css
    path: resolve(__dirname,'build') // All resource storage locations
  },
  module: {
    rules: [
      ...
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader,'css-loader']
      },
      ...
    ]
  },
  plugins: [
    ...
    new MiniCssExtractPlugin({
      filename: 'css/built.css'
    })
  ],
  ...
}

4.2.2 css compatibility processing

You need to use: postcss loader and postcss preset env (for accurate browser version)
Installation: NPM I - D postcss loader postcss preset env

const {
  resolve
} = require("path")
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')

// Set the node environment variable in this way, and postcss will read the environment variable to determine whether to read the production or development configuration
// process.env.NODE_ENV = "development"
process.env.NODE_ENV = "production"

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'js/built.js', // The storage place of the entry file, which includes the combination of js and css
    path: resolve(__dirname, 'build') // All resource storage locations
  },
  module: {
    rules: [{
        test: /\.less$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader', 
          {
            loader: 'postcss-loader',
            options: {
              postcssOptions: {
                ident: 'postcss',
                plugins: [
                  require('postcss-preset-env')()
                ]
              }
            }
          },
          'less-loader',
        ]
      },
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader'
        {
            loader: 'postcss-loader',
            options: {
              postcssOptions: {
                ident: 'postcss',
                plugins: [
                  require('postcss-preset-env')()
                ]
              }
            }
          }
        ]
      },
      
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    }),
    new MiniCssExtractPlugin({
      filename: 'css/built.css'
    })
  ],
}

Special note: 1. The node environment variable needs to be configured: process.env.NODE_ENV = “production”; 2, Configure the postcss loader and its parameters. This loader needs to be processed by CSS loader first. 3, Configure browserlist in package.json

Configure browserslist in package.json to specify the css compatibility style to load

  "browserslist": {
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ],
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ]
  }

For the detailed configuration of browserslist, you can query on github. Note: by default, the plug-in implements the production environment configuration, unless the Node environment variable is configured (independent of the mode in webpack.config.js)

The compiled css code adds compatible styles

#b {
  display: -webkit-flex;
  display: flex;
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
}

4.2.3 css compression

A plug-in is required: optimize CSS assets webpack plugin
Installation: NPM I - D optimize CSS assets webpack plugin
Configuration method: import the plug-in and use the new plug-in.

const {resolve} = require("path")
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'js/built.js', // The storage place of the entry file, which includes the combination of js and css
    path: resolve(__dirname,'build') // All resource storage locations
  },
  module: {
    rules: [
      ...
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader,'css-loader']
      },
      ...
    ]
  },
  plugins: [
    ...
    new MiniCssExtractPlugin({
      filename: 'css/built.css'
    }),
    new OptimizeCssAssetsWebpackPlugin()
  ],
  ...
}

Note: there is a 'cssnano': {} configuration in the postcss loader, which can also be used to compress css! Example: cssnano: CTX. Env = = 'production'? {} : false

4.2.4 JS syntax check - eslint

Eslint is used for js syntax checking and needs to be installed: eslint loader and eslint
Note: due to the requirement to check only the source code under src, no third-party libraries are checked (use exclude)

The eslint check rule needs to be set in the eslintConfig attribute in package.json. It is recommended to use the airbnb rule

Eslint config airbnb base: basic check library, excluding react plug-in. The library depends on eslint plugin import eslint
Eslint config airbnb: includes the react plug-in

Steps: (you need to install: NPM I - D eslint eslint loader eslint config airbnb base eslint plugin import)

  1. loader configuration
      {
        test: /\.js$/,
        loader: 'eslint-loader',
        enforce: "pre", // Pre compile check
        exclude: /node_modules/, // Undetected files
        include: [path.resolve(__dirname, 'src')], // Specify the directory to check
        options: { // The configuration item parameters here will be passed to the client engine of eslint 
          fix: true // Automatically fix errors
        }
      }
  1. package.json configuration, where airbnb syntax rules are used
  "eslintConfig":{
    "extends":"airbnb-base"
  }

You can use comments to cancel the check: / / eslint disable next line cancels the syntax check on the next line, / * eslint disable * / placed on the first line of the file means that the file is not checked

reference resources: https://blog.csdn.net/kai_vin/article/details/89026115

4.2.5 JS compatibility processing

JS compatibility means that some browsers do not support ES6 syntax, and babel will transcode it into ES5, relying on @ babel/core, which is called compatibility processing. The loader used is babel loader, which can convert different syntax according to the preset environment.

[basic compatibility processing]: @ Babel / preset Env,
Installation: NPM I - D Babel loader @ Babel / core @ Babel / preset env

      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        options: {
          presets: ['@babel/preset-env']
        }
      }

Only basic functions such as const and arrow functions can be converted. Promise is not supported.

[violence handling] all js compatible handling: @ babel/polyfill
Installation: npm i -D @babel/polyfill
Use: import in the entry file: import '@babel/polyfill

@babel/polyfill has been replaced by: core JS / stable + regenerator Runtime / runtime, using:

import "core-js/stable";
import "regenerator-runtime/runtime";

[load compatibility processing on demand] use core JS

 {
   test: /\.js$/,
   exclude: /node_modules/,
   loader: 'babel-loader',
   options: {
     presets: [
       '@babel/preset-env',
       {
         useBuiltIns: 'usage', // Turn on demand loading
         corejs: {version: 3}, // Specify the core JS version
         targets: {chrome:'60',firefox:'60',ie:'9',safari:'10',edge:'17'} // Specify which version is more compatible
       }
     ]
   }
 }

Note: violent treatment cannot be used with this program

4.2.6 html and js compression

js compression: just turn on mode='production '(the plug-in that works is UglifyJsPlugin)
html compression: similarly, you only need to turn on the production mode. Of course, you can also configure it in the plug-in html webpack plugin:

 new HtmlWebpackPlugin({
   template: './src/index.html',
   minify: {
     collapseWhitespace: true, // Remove spaces
     removeComments: true // Remove comment
   }
 }),

4.2.7 ultimate configuration of production environment

const { resolve } = require("path")
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')

// Set the node environment variable in this way, and postcss will read the environment variable to determine whether to read the production or development configuration
// process.env.NODE_ENV = "development"
process.env.NODE_ENV = "production"

const commonCssLoader = [
  MiniCssExtractPlugin.loader,
  'css-loader', 
  {
    loader: 'postcss-loader',
    options: {
      postcssOptions: {
        ident: 'postcss',
        plugins: [
          require('postcss-preset-env')()
        ]
      }
    }
  }
]

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'js/built.js', // The storage place of the entry file, which includes the combination of js and css
    path: resolve(__dirname, 'build') // All resource storage locations
  },
  module: {
    rules: [{
        test: /\.less$/,
        use: [
          ...commonCssLoader,
          'less-loader'
        ]
      },
      {
        test: /\.css$/,
        use: [
          ...commonCssLoader
        ]
      },
      {
        test: /\.(jpg|png|jpeg|gif)$/,
        loader: 'url-loader',
        options: {
          limit: 8 * 1024,
          name: '[hash:10].[ext]',
          esModule: false,
          outputPath: 'imgs'
        }
      },
      {
        test: /.\html$/,
        loader: 'html-loader'
      },
      {
        exclude: /\.(html|js|css|less|jpg|png|jpeg|gif)$/,
        loader: 'file-loader',
        options: {
          name: '[hash:10].[ext]',
          outputPath: 'medias'
        }
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        options: {
          presets: [
            '@babel/preset-env',
            {
              useBuiltIns: 'usage', // Turn on demand loading
              corejs: {version: 3}, // Specify the core JS version
              targets: {chrome:'60',firefox:'60',ie:'9',safari:'10',edge:'17'} // Specify which version is more compatible
            }
          ]
        }
      },
      {
        test: /\.js$/,
        loader: 'eslint-loader',
        enforce: "pre", // Check before compilation. If the attribute is true in the same test, execute it first
        exclude: /node_modules/, // Undetected files
        include: [path.resolve(__dirname, 'src')], // Specify the directory to check
        options: { // The configuration item parameters here will be passed to the client engine of eslint 
          fix: true
        }
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      minify: {
        collapseWhitespace: true, // Remove spaces
        removeComments: true // Remove comment
      }
    }),
    new MiniCssExtractPlugin({
      filename: 'css/built.css'
    }),
    new OptimizeCssAssetsWebpackPlugin()
  ],
  // It will be packaged in memory without any output
  // Start: webpack dev server
  devServer: {
    contentBase: resolve(__dirname, 'build'), // The directory in which the project is running
    compress: true, // Start gzip compression
    port: 3000, // Port number
    open: true // Open browser automatically
  },
  mode: 'production'
}

In package.json:

{
...
 "scripts": {
    ...
    "build": "webpack",
    "start": "webpack serve"
  },
  ...
  "browserslist": {
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ],
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ]
  },
  "eslintConfig":{
    "extends":"airbnb-base"
  }
}

5 webpack optimization

Optimization of two environments

Development environment: 1 optimize the speed of packaging and construction 2 optimize code debugging
Production environment: 1 optimize the speed of packaging and construction 2 optimize the performance of code operation

5.1 development environment

Question 1: if any module is modified, all modules of the whole project will be reloaded
Solution: HMR: hot module replacement. A module change will only package the module, not all modules
Add: hot:true in devServer

  devServer: {
  contentBase: resolve(__dirname, 'build'), // The directory in which the project is running
  compress: true, // Start gzip compression
  port: 3000, // Port number
  open: true, // Open browser automatically
  hot: true // Thermal loading
},

For style modification, because style loader implements HMR function, hot module update can be realized

For JS files, there is no HMR function
Solution: add HMR function

// In the entry file, listen for updates to other imported modules
if(module.hot){
	module.hot.accept('./xxx.js',function(){Callback function}
}

Note: the entry js file does not need or can not be used for HMR, because it will be reintroduced if it changes other functions

For HTML files, you cannot use the HMR function (because HTML has only one file), and you cannot respond to updates after opening it
Solution: add html file entry in webpack.config.js: entry: ['./src/index.js','./src/index.html '],

Source map: provides the mapping technology between source code and built code to facilitate the query and location of error causes
Configuration: add: devtool: 'source map' in webpack.config.js
Note: source map has multiple values:

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

Of which:
Source map: locate the external error to the [source] code, accurate to rows and columns
Inline source map: locate the [source] code for the inline error
Hidden source map: locate the external error to the [compiled] code
Eval source map: locate the [source] code for the inline error
Nosources source map: the external error is located in the [source] code, but cannot be viewed
Soap source map: the external error is located in the [source] code, which is accurate to the row but not to the column
Soap module source map: the external error is located in the [source] code. The accuracy is a little better...

The mapping generated by introversion is faster to build in packaged js, and the external mapping is at the same level of js

Recommended for development: Eval source map
Recommended for production: source map

5.2 production environment

Tags: node.js Front-end Webpack

Posted on Tue, 26 Oct 2021 22:27:44 -0400 by lampquest