Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.

This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.


πŸ‘¨πŸ’» Interviewer: Have you been involved in configuring Webpack for your company’s projects? Or did you do anything to optimize the configuration?

πŸ‘¨πŸŒΎ me: No πŸ˜…

πŸ‘¨πŸ’» Interviewer: Em…

At this moment, BGM said, 🎡 is afraid of sudden silence.


Actually this is a very sincere answer! Many people really don’t have the opportunity to participate in Webpack configuration on a project, let alone configuration optimization.

Therefore, it is easy to have the following problems:

  1. Without more practice opportunities, people tend to neglect its learning
  2. Want to learn, and do not know where to start πŸ€¦β™‚οΈ

At this point, we can start by looking at what is usually asked about Webpack in an interview

Why start with interview questions?

  1. Find out exactly what Webpack knowledge the interviewer is looking for in the candidate
  2. It is convenient for us to capture the knowledge focus of learning Webpack

Here I’ve found some representative questions to see how many you can answer πŸ˜‰

Q: Which loaders have been used in the Webpack configuration? What does it do?

Q: What plugins have been used in the Webpack configuration? What does it do?

Q: What is the difference between Loader and Plugin?

Q: How do I write a Loader? Introduce the idea?

Q: How do I write a Plugin? Introduce the idea?

Q: Is Webpack Optimize configured? Can you explain it briefly?

Q: How is Webpack level performance optimized?

Q: What is the Webpack process?

Q: How does tree-shaking work?

Q: How is Webpack Hot Update (HMR) implemented?

Q: How does the Babel plug-in in the Webpack package work?

Q. What are the similarities and differences between Webpack and Rollup?

Q: what new features are updated with Webpack5?

How’s that? Are these questions OK?

Next, we will break down and summarize these interview questions to draw a general knowledge system

After establishing the knowledge system, we will simply divide the knowledge system into three levels:

  1. Basic – can configure
  2. Advanced – can be optimized
  3. Deep – understand the principle

A layer of promotion to play strange πŸ¦–, answer different depth of Webpack interview questions πŸ€“

Come on, let’s start now 🀜

First, Webpack foundation

The first part, from the simple “configuration” requirements, first understand Webpack simple configuration and simple configuration will be involved in the interview questions.

1. Simple configuration

This part needs to master:

  1. What are the general Webpack configuration items?
  2. What are the common Loaders? How to configure it?
  3. What are the common plugins? How to configure?
  4. How is Babel configured? How to use the Babel plug-in?

1.1 Installation Dependencies

Without a doubt, start with a local installation of WebPack and WebPack-CLI

$ npm install webpack webpack-cli -D Install to local dependencies
Copy the code

Installation complete βœ…

+ webpack-cli@4.72.
+ webpack@5.44. 0
Copy the code

1.2 Working Mode

Webpack has supported 0 configuration packaging since 4, so we can test it out

  1. new./src/index.jsFile, write a simple code
const a = 'Hello ITEM'
console.log(a)
module.exports = a;
Copy the code

Directory structure at this point

β”œβ”€ SRC β”‚ β”œβ”€ β”œβ”€ download.jsonCopy the code
  1. Run directlynpx webpack, start packing

The ‘mode’ option has not been set…

This means that we have not configured mode, so we are reminded to do so

Mode: The mode configuration option tells Webpack to use the built-in optimizations for the corresponding mode. The default value is Production, and there are also development and None. The differences are as follows

options describe
development Development mode, packaging is faster, save code optimization steps
producttion Production mode, which is slow to pack, turns on tree-shaking and compression code
none Do not use any default optimization options

How do you configure it? Very simple

  1. Simply provide the mode option in the configuration object:
module.exports = {
  mode: 'development'};Copy the code
  1. Pass from CLI parameters:
$ webpack --mode=development
Copy the code

1.3 Configuration File

Although there are 0 configuration packages, in actual work, we still need to use configuration files to meet the needs of different projects

  1. Create a new configuration file, webpack.config.js, in the root directory

  2. Added basic configuration information

const path = require('path')

module.exports = {
  mode: 'development'./ / mode
  entry: './src/index.js'.// Package entry address
  output: {
    filename: 'bundle.js'.// Output the file name
    path: path.join(__dirname, 'dist') // Output file directory}}Copy the code

I don’t want to say more about this, the basic configuration

1.4 Loader

So if we change the entry to a CSS file, what happens if we package it

1. New. / SRC/main CSS

body {
  margin: 0 auto;
  padding: 0 20px;
  max-width: 800px;
  background: #f4f8fb;
}
Copy the code
  1. Modifying entry Configuration
const path = require('path')

module.exports = {
  mode: 'development'./ / mode
  entry: './src/main.css'.// Package entry address
  output: {
    filename: 'bundle.css'.// Output the file name
    path: path.join(__dirname, 'dist') // Output file directory}}Copy the code

3. Run pack command: NPX webpack

This is an error!

This is because: Webpack by default supports processing JS files, other types of files can not be processed, here must use Loader to process different types of files.

  1. The installationcss-loaderTo deal with CSS
npn install css-loader -D
Copy the code
  1. Configure the resource loading module
const path = require('path')

module.exports = {
  mode: 'development'./ / mode
  entry: './src/main.css'.// Package entry address
  output: {
    filename: 'bundle.css'.// Output the file name
    path: path.join(__dirname, 'dist') // Output file directory
  },
  module: { 
    rules: [ // Convert rules
      {
        test: /.css$/.// Matches all CSS files
        use: 'css-loader' // use: indicates the Loader name}}}]Copy the code
  1. Rerun the package commandnpx webpack

Hey hey, can pack 😁

Dist β”” ─ bundle. CSS# The result of packing
Copy the code

This is an attempt to change the entry file back to./ SRC /index.js

Here we can draw a conclusion: Loader is all about converting content that Webpack doesn’t know into content that Webpack does

1.5 Plugin

Unlike a Loader used to convert specific types of files, plugins can perform different tasks throughout the life cycle of a Webpack package

Here’s an example in use:

1. Create the./ SRC /index.html file

<! DOCTYPEhtml>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
  <title>ITEM</title>
</head>
<body>
  
</body>
</html>
Copy the code

If I want packaged resource files, such as JS or CSS files, to be automatically imported into Html, I need to use the html-webpack-plugin to help you do this

2. Install the HTmL-webpack-plugin locally

npm install html-webpack-plugin -D
Copy the code

3. Configure the plug-in

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

module.exports = {
  mode: 'development'./ / mode
  entry: './src/index.js'.// Package entry address
  output: {
    filename: 'bundle.js'.// Output the file name
    path: path.join(__dirname, 'dist') // Output file directory
  },
  module: { 
    rules: [{test: /.css$/.// Matches all CSS files
        use: 'css-loader' // use: indicates the Loader name}},plugins: [// Configure the plug-in
    new HtmlWebpackPlugin({
      template: './src/index.html'}})]Copy the code

Run the package and open the index.html file generated in the dist directory

<! DOCTYPEhtml>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
  <title>ITEM</title>
<script defer src="bundle.js"></script></head>
<body>
  
</body>
</html>
Copy the code

As you can see, it automatically introduces bundled bundle.js, which is very handy

1.6 Automatically Clearing packing Directories

Every time you pack, the package directory will leave behind the files you packed last time. In order to keep the package directory clean, you need to empty the package directory before packing

We can do this using the clean-webpack-plugin

  1. The installation
$ npm install clean-webpack-plugin -D
Copy the code
  1. configuration
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
// Import plug-ins
const { CleanWebpackPlugin } = require('clean-webpack-plugin')

module.exports = {
  // ...
  plugins: [// Configure the plug-in
    new HtmlWebpackPlugin({
      template: './src/index.html'
    }),
    new CleanWebpackPlugin() // Import plug-ins]}Copy the code

1.7 Differentiating Environments

Local development and deployment lines certainly have different requirements

Local Environment:

  • Faster build times are required
  • Debug information needs to be printed
  • Live reload or Hot Reload is required
  • Sourcemap is needed to locate problems
  • .

Production environment:

  • Need smaller package sizes, code compression +tree-shaking
  • Code splitting is required
  • Need to compress image volume
  • .

For different needs, the first thing to do is to do a good job of environmental differentiation

  1. Install cross-env locally
npm install cross-env -D
Copy the code
  1. Configuring Startup Commands

Open. / package. Json

"scripts": {
    "dev": "cross-env NODE_ENV=dev webpack serve --mode development"."test": "cross-env NODE_ENV=test webpack --mode production"."build": "cross-env NODE_ENV=prod webpack --mode production"
  },
Copy the code
  1. Get the environment variables in the Webpack configuration file
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')

console.log('process.env.NODE_ENV=', process.env.NODE_ENV) // Prints environment variables

const config = {
  entry: './src/index.js'.// Package entry address
  output: {
    filename: 'bundle.js'.// Output the file name
    path: path.join(__dirname, 'dist') // Output file directory
  },
  module: { 
    rules: [{test: /.css$/.// Matches all CSS files
        use: 'css-loader' // use: indicates the Loader name}},plugins: [// Configure the plug-in
    new HtmlWebpackPlugin({
      template: './src/index.html'}})]module.exports = (env, argv) = > {
  console.log('argv.mode=',argv.mode) // Prints the mode value
  // The config configuration can be modified in different modes
  return config;
}
Copy the code

4. Test it out

  • performnpm run build
process.env.NODE_ENV= prod
argv.mode= production
Copy the code
  • performnpm run test
process.env.NODE_ENV= test
argv.mode= production
Copy the code
  • performnpm run dev
process.env.NODE_ENV= dev
argv.mode= development
Copy the code

This allows us to dynamically modify the Webpack configuration for different environments

1.8 start devServer

1. Install webpack – dev – server

npm intall webpack-dev-server -D
Copy the code

2. Configure the local service

// webpack.config.js
const config = {
  // ...
  devServer: {
    contentBase: path.resolve(__dirname, 'public'), // Static file directory
    compress: true.// Whether to enable gzip compression
    port: 8080./ / the port number
    // open:true // Whether to automatically open the browser
  },
 // ...
}
module.exports = (env, argv) = > {
  console.log('argv.mode=',argv.mode) // Prints the mode value
  // The config configuration can be modified in different modes
  return config;
}
Copy the code

Why configure contentBase?

Because when WebPack does its packaging, static files, such as images, are copied directly to the dist directory. However, this process is time-consuming and unnecessary for local development, so after setting up contentBase, you can go directly to the corresponding static directory to read the file without moving the file, saving time and performance overhead.

  1. Start a local service
$ npm run dev
Copy the code

To see the effect, I added a paragraph of text to the HTML and put an image logo.png under public

<! DOCTYPEhtml>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
  <title>ITEM</title>
</head>
<body>
  <p>ITEM</p>
</body>
</html>
Copy the code
Public β”” ─ logo. PNGCopy the code

Open the address http://localhost:8080/

Then go to http://localhost:8080/logo.png

OK, no problem πŸ‘Œ

1.9 the introduction of CSS

Above, we talked about using CSS-loader to handle CSS, but there is no way to use CSS-Loader alone to load the style to the page. At this point, we need to install another style-loader to complete this function

Style-loader adds processed CSS to the page in the form of style tags

  1. The installationstyle-loader [The document address]
npm install style-loader -D
Copy the code
  1. Configure the Loader
const config = {
  // ...
  module: { 
    rules: [{test: /.css$/.// Matches all CSS files
        use: ['style-loader'.'css-loader']]}},// ...
}
Copy the code

⚠️ Note: The Loader execution sequence is fixed from back to front, that is, csS-loader -> style-loader

  1. Reference style file

Import the style file./ SRC /main.css in the entry file./ SRC /index.js

// ./src/index.js

import './main.css';


const a = 'Hello ITEM'
console.log(a)
module.exports = a;
Copy the code
/* ./src/main.css */ 
body {
  margin: 10px auto;
  background: cyan;
  max-width: 800px;
}
Copy the code
  1. Restart the local service, accesshttp://localhost:8080/

Now that the style is working, go ahead and modify the style

body {
  margin: 10px auto;
  background: cyan;
  max-width: 800px;
  / * new * /
  font-size: 46px;
  font-weight: 600;
  color: white;
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
}
Copy the code

Once saved, the style changes automatically

Style-loader core logic equivalent to:

const content = `${style content}`
const style = document.createElement('style');
style.innerHTML = content;
document.head.appendChild(style);
Copy the code

Introduce styles to the page by dynamically adding style tags

1.10 CSS Compatibility

Use postCSS-loader to automatically add browser prefixes for some CSS3 attributes

Transform: translateX(-50%); The postCSs-loader can be used to help with this

npm install postcss-loader postcss -D
Copy the code
const config = {
  // ...
  module: { 
    rules: [{test: /.css$/.// Matches all CSS files
        use: ['style-loader'.'css-loader'.'postcss-loader']]}},// ...
}
Copy the code

⚠️ there is a big pitfall here: after the reference document is configured, the runtime will report an error

Error: Loading PostCSS "postcss-import" plugin failed: 
Cannot find module 'postcss-import'
Copy the code

Next try installing the plugin’s set postCSS-preset -env and change the configuration to

// webpack.config.js
// Failed to configure
{
    loader: 'postcss-loader'.options: {
      postcssOptions: {
        plugins: [['postcss-preset-env', 
            {
              // Other options},],],},},},Copy the code

After the operation will still report errors, after consulting information, finally found the correct way to open, we re 😁

npm install postcss postcss-loader postcss-preset-env -D
Copy the code

Example Add the postCSS-loader

const config = {
  // ...
  module: { 
    rules: [{test: /.css$/.// Matches all CSS files
        use: [
          'style-loader'.'css-loader'.'postcss-loader']]}},// ...
}
Copy the code

Create the postCSS configuration file postcss.config.js

// postcss.config.js
module.exports = {
  plugins: [require('postcss-preset-env')]}Copy the code

Create the postcss-preset-env configuration file.browserslistrc

# newline is equivalent to and
last 2 versions # Roll back both browser versions
> 0.5% # For browsers used by more than 0.5% of the world's population, check out caniuse.com to see the share of different browsers
IE 10 # Compatible with IE 10
Copy the code

Let’s try it again

The prefix is automatically added to πŸ‘

If you’re interested in seeing the effects of different configurations of.browserslistrc, you can use autoprefixer to perform online transformations to see the effects

1.11 Importing Less or Sass

Less and sass are also unrecognized by Webpack and need to be processed by the corresponding Loader

The file type loader
Less less-loader
Sass Sass – loader node – sass or dart – sass

Less processing is relatively simple. Add the corresponding Loader directly

Sass requires not only installation of sass-Loader but also a Node-sass. Here, node-sass is recommended to be installed using Taobao image. The probability of successful installation of NPM is too small 🀣

Let’s use Sass as an example

  1. The installation
$ npm install sass-loader -D
# Taobao Mirror
$ npm i node-sass --sass_binary_site=https://npm.taobao.org/mirrors/node-sass/
Copy the code
  1. new./src/sass.scss

Sass files can be suffixes.scss(common) or.sass

$color: rgb(190.23.168);

body {
  p {
    background-color: $color;
    width: 300px;
    height: 300px;
    display: block;
    text-align: center;
    line-height: 300px; }}Copy the code
  1. Import Sass files
import './main.css';
import './sass.scss' // Import Sass files


const a = 'Hello ITEM'
console.log(a)
module.exports = a;
Copy the code
  1. Modify the configuration
const config = {
   // ...
   rules: [{test: /\.(s[ac]|c)ss$/i.// Matches all sass/ SCSS/CSS files
        use: [
          'style-loader'.'css-loader'.'postcss-loader'.'sass-loader',]},]},// ...
}
Copy the code

Take a look at the results

Successful πŸ‘

1.12 Separate style files

Previously, we relied on style-loader to add styles to the page as style tags

However, more often than not, we want to be able to introduce it to the page in the form of A CSS file

  1. Install the mini – CSS – extract – the plugin
$ npm install mini-css-extract-plugin -D
Copy the code
  1. Modify thewebpack.config.jsconfiguration
// ...
// Import plug-ins
const MiniCssExtractPlugin = require('mini-css-extract-plugin')


const config = {
  // ...
  module: { 
    rules: [
      // ...
      {
        test: /\.(s[ac]|c)ss$/i.// Matches all sass/ SCSS/CSS files
        use: [
          // 'style-loader',
          MiniCssExtractPlugin.loader, / / add the loader
          'css-loader'.'postcss-loader'.'sass-loader',]},]},// ...
  plugins: [// Configure the plug-in
    // ...
    new MiniCssExtractPlugin({ // Add a plug-in
      filename: '[name].[hash:8].css'
    }),
    // ...]}// ...
Copy the code
  1. View packaging results
β”‚ β”œβ”€ avatar.d4d42d72.png β”‚ β”œβ”€ design.html β”‚ β”œβ”€ main.3bcbae64.css# Generated style file
Copy the code

1.13 Image and Font files

The contentBase can be set to read the static file of the image class directly, so take a look at the following two images

  1. Page direct import
<! -- Local access, production environment will not find the image -->
<img src="/logo.png" alt="">
Copy the code
  1. Background image introduction
<div id="imgBox"></div>
Copy the code
/* ./src/main.css */.#imgBox {
  height: 400px;
  width: 400px;
  background: url('.. /public/logo.png');
  background-size: contain;
}
Copy the code

An error is reported directly

So in fact, Webpack doesn’t recognize the image file and needs to deal with it at packaging time

Common loaders for processing image files include:

Loader instructions
file-loader Fix the image import problem and copy the image to the specified directory, default is dist
url-loader If the value of an image is smaller than the limit value, the image is converted to Base64 encoding. If the value is larger than the limit value, the image is copied using file-loader
img-loader The compressed image
  1. The installationfile-loader
npm install file-loader -D
Copy the code
  1. Modify the configuration
const config = {
  / /...
  module: { 
    rules: [{// ...
      }, 
      {
        test: /\.(jpe? g|png|gif)$/i.// Match image files
        use:[
          'file-loader' / / using the file - loader]]}},// ...
}
Copy the code

3. Introduce pictures

<! -- ./src/index.html -->
<! DOCTYPEhtml>
<html lang="en">.<body>
  <p></p>
  <div id="imgBox"></div>
</body>
</html>
Copy the code

Introduced in the style file

/* ./src/sass.scss */

$color: rgb(190.23.168);

body {
  p {
    width: 300px;
    height: 300px;
    display: block;
    text-align: center;
    line-height: 300px;
    background: url('.. /public/logo.png');
    background-size: contain; }}Copy the code

Js file

import './main.css';
import './sass.scss'
import logo from '.. /public/avatar.png'

const a = 'Hello ITEM'
console.log(a)

const img = new Image()
img.src = logo

document.getElementById('imgBox').appendChild(img)
Copy the code

Start the service and let’s see what happens

Display normal ✌️

We can see that the name of the image file has been changed and the hash value has been added. Then I look at the package directory

Dist β”œ ─ 56482 c77280b3c4ad2f083b727dfcbf9. PNG β”œ ─ bundle. Js β”œ ─ d4d42d529da4b5120ac85878f6f69694. PNG β”” ─ index. The HTMLCopy the code

There are two additional files under the dist directory that file-loader copied from

If you want to change the name, you can add a configuration

const config = {
  / /...
  module: { 
    rules: [{// ...
      }, 
      {
        test: /\.(jpe? g|png|gif)$/i,
        use:[
          {
            loader: 'file-loader'.options: {
              name: '[name][hash:8].[ext]'}}]}, {loader: 'file-loader'.options: {
          name: '[name][hash:8].[ext]'}}},// ...
}
Copy the code

Pack it again

Dist β”œ ─ avatard4d42d52. PNG β”œ ─ bundle. Js β”œ ─ index. The HTML β”” ─ logo56482c77. PNGCopy the code

Take a look at url-loader again

  1. The installationurl-loader
$ npm install url-loader -D
Copy the code
  1. configurationurl-loader

The configuration is similar to file-loader, with a limit configuration added

const config = {
  / /...
  module: { 
    rules: [{// ...
      }, 
      {
        test: /\.(jpe? g|png|gif)$/i,
        use:[
          {
            loader: 'url-loader'.options: {
              name: '[name][hash:8].[ext]'.// Files smaller than 50K will be converted to Base64, files larger than 50K will be copied
              limit: 50 * 1024}}]},]},// ...
}
Copy the code

Look at the size of our two image files

Public β”œ ─ avatar. PNG# 167kbβ”” ─ logo. PNG# 43kb 
Copy the code

Let’s pack it and see how it looks

It is obvious that the logo.png file has been converted to base64 πŸ‘Œ

Look at font file processing

  1. Configuring font files

First, download the font file locally from iconfont.cn

In your project, create a./ SRC /fonts folder to hold the font files

Then, import to the entry file

// ./src/index.js

import './main.css';
import './sass.scss'
import logo from '.. /public/avatar.png'

// Import the font icon file
import './fonts/iconfont.css'

const a = 'Hello ITEM'
console.log(a)

const img = new Image()
img.src = logo

document.getElementById('imgBox').appendChild(img)
Copy the code

Next, use it in./ SRC /index.html

<! DOCTYPEhtml>
<html lang="en">.<body>
  <p></p>
  <! -- Use font icon file -->
  <! -- 1) iconfont = "iconfont" -->
  <! You can find the name of the icon class in iconfont.
  <i class="iconfont icon-member"></i>
  <div id="imgBox"></div>
</body>
</html>
Copy the code

Finally, add the font file configuration

const config = {
  // ...
  {
    test: /\.(woff2? |eot|ttf|otf)(\? . *)? $/i.// Matches the font file
    use: [
      {
        loader: 'url-loader'.options: {
          name: 'fonts/[name][hash:8].[ext]'.// If the size is greater than 10KB, pack it into the fonts directory
          limit: 10 * 1024,}}]},// ...
}
Copy the code

Pack it up and see how it looks

However, in webpack5, there is a built-in resource processing module, file-loader and url-loader can not be installed

1.14 Use of Resource Modules

Webpack5 adds an asset Module that allows you to use resource files (fonts, ICONS, etc.) without having to configure additional loaders.

The resource module supports the following four configurations:

  1. asset/resourceSplits resources into separate files and exports urls, similar to file-loader.
  2. asset/inlineExport the resource as a dataUrl, similar to the previous urL-loader less-than limit parameter.
  3. asset/sourceExport resource as source code. Similar raw-loader function.
  4. assetThe type is selected based on the size of the file, asset/inline is used when the file is less than 8 KB (the default), and asset/ Resource is used otherwise

Post the modified complete code

// ./src/index.js

const config = {
  // ...
  module: { 
    rules: [
      // ... 
      {
        test: /\.(jpe? g|png|gif)$/i,
        type: 'asset'.generator: {
          // Outputs the file location and file name
          // [ext] comes with a "." which is different from the URL-loader configuration
          filename: "[name][hash:8][ext]"
        },
        parser: {
          dataUrlCondition: {
            maxSize: 50 * 1024 If the value exceeds 50kb, base64 will not be transferred}}}, {test: /\.(woff2? |eot|ttf|otf)(\? . *)? $/i,
        type: 'asset'.generator: {
          // Outputs the file location and file name
          filename: "[name][hash:8][ext]"
        },
        parser: {
          dataUrlCondition: {
            maxSize: 10 * 1024 If the value exceeds 100kb, base64 will not be transferred}}},]},// ...
}

module.exports = (env, argv) = > {
  console.log('argv.mode=',argv.mode) // Prints the mode value
  // The config configuration can be modified in different modes
  return config;
}
Copy the code

The result is the same as before

1.15 JS Compatibility (Babel)

We want to use the latest Js features in development, but some of the new features are not well supported by browsers, so Js also needs to do compatibility processing, common is to convert ES6 syntax to ES5.

Here comes Babel, the most beautiful girl in the room

  1. Not configured Babel

Let’s write something about ES6

// ./src/index.js

import './main.css';
import './sass.scss'
import logo from '.. /public/avatar.png'

import './fonts/iconfont.css'

// ...

class Author {
  name = 'ITEM'
  age = 18
  email = '[email protected]'

  info =  () = > {
    return {
      name: this.name,
      age: this.age,
      email: this.email
    }
  }
}


module.exports = Author

Copy the code

To make it easier to see the source code, let’s change mode to development

Then execute the pack command

Once the package is complete, open bundle.js to see the result of the package

We can still find our code, but it’s a little less intuitive to read, so let’s set mode to None, package it in its original form, and see what happens

The packaged code does not change much, except that the image address is replaced. Now let’s see what happens to the packaged result after configuring Babel

  1. Install dependencies
$ npm install babel-loader @babel/core @babel/preset-env -D
Copy the code
  • babel-loaderLoad ES2015+ code and convert it to ES5 using Babel
  • @babel/coreBabel compiles the core package
  • @babel/preset-envThe default for Babel compilation can be understood as a superset of Babel plug-ins
  1. Configure the Babel preset
// webpack.config.js
// ...
const config = {
  entry: './src/index.js'.// Package entry address
  output: {
    filename: 'bundle.js'.// Output the file name
    path: path.join(__dirname, 'dist'), // Output file directory
  },
  module: { 
    rules: [{test: /\.js$/i,
        use: [
          {
            loader: 'babel-loader'.options: {
              presets: [
                '@babel/preset-env'],}}]},// ...]},/ /...
}
// ...
Copy the code

After the configuration is complete, perform the packaging

The ES6 class notation I just wrote has been converted to the ES5 constructor form

As far as compatibility is concerned, we can also specify which browsers are compatible

To avoid webpack.config.js being too bloated, it is recommended to extract the Babel configuration file

.babelrc.js is added to the root directory

// ./babelrc.js

module.exports = {
  presets: [["@babel/preset-env",
      {
        // useBuiltIns: false default, regardless of browser compatibility configuration, to introduce all polyfills
        // useBuiltIns: Entry introduces browser-incompatible polyfills based on configured browser compatibility
        // useBuiltIns: Usage polyfills according to the browser compatibility configured and the API used in your code to implement on-demand additions
        useBuiltIns: "entry".corejs: "3.9.1".// Is the core-js version number
        targets: {
          chrome: "58".ie: "11",},},],],};Copy the code

There you have it: a simple Babel preset

Other common Babel presets are:

  • @babel/preset-flow
  • @babel/preset-react
  • @babel/preset-typescript

If you are interested, you can learn about it yourself. There is no extension here. Let’s talk about the use of plug-ins

  1. Configure the Babel plug-in

For proposed new features that are not yet in the ECMA specification, Babel cannot handle them. Plug-ins must be installed, such as:

// ./ index.js

import './main.css';
import './sass.scss'
import logo from '.. /public/avatar.png'

import './fonts/iconfont.css'

const a = 'Hello ITEM'
console.log(a)

const img = new Image()
img.src = logo

document.getElementById('imgBox').appendChild(img)

// Add the use of decorators
@log('hi')
class MyClass {}function log(text) {
  return function(target) {
    target.prototype.logger = () = > `${text}.${target.name}`}}const test = new MyClass()
test.logger()
Copy the code

Let’s do the packing

Not surprisingly, no πŸ™…πŸ»β™€οΈ

How can I use it? Babel actually provides the corresponding plug-in:

  • @babel/plugin-proposal-decorators
  • @babel/plugin-proposal-class-properties

Install:

$ npm install babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties -D
Copy the code

Open.babelrc.js with plug-in configuration

module.exports = {
  presets: [["@babel/preset-env",
      {
        useBuiltIns: "entry".corejs: "3.9.1".targets: {
          chrome: "58".ie: "11",},},],plugins: [["@babel/plugin-proposal-decorators", { legacy: true }],
    ["@babel/plugin-proposal-class-properties", { loose: true}]]};Copy the code

This is packaged and converted to browser-supported JS code in bundle.js

Similarly, we can use different plug-ins according to our actual needs

2 SourceMap Configuration selection

SourceMap is a mapping that allows us to reverse locate the source code if an error occurs after the project is run

2.1 devtool configuration

const config = {
  entry: './src/index.js'.// Package entry address
  output: {
    filename: 'bundle.js'.// Output the file name
    path: path.join(__dirname, 'dist'), // Output file directory
  },
  devtool: 'source-map'.module: { 
     // ...
  }
  // ...
Copy the code

After packaging, the SourceMap file ending with.map is generated in the dist directory

β”œβ”€ β”œβ”€ avatard4d02.png β”œβ”€ bundle.js β”œβ”€ bundle.jsCopy the code

In addition to the source-map type, there are many other types available, such as:

  • eval
  • eval-source-map
  • cheap-source-map
  • inline-source-map
  • cheap-module-source-map
  • inline-cheap-source-map
  • cheap-module-eval-source-map
  • inline-cheap-module-source-map
  • hidden-source-map
  • nosources-source-map

So what’s the difference? How to choose?

2.2 Configuration Item Differences

  1. To make it easier to compare them, let’s create a new project
β”œβ”€ β”œβ”€ download.txt β”œβ”€ download.txt β”œβ”€ download.txtCopy the code
  1. Open the./src/Author.js
class Author {
  name = 'ITEM'
  age = 18
  email = '[email protected]'

  info =  () = > {
    return {
      name: this.name,
      age: this.age,
      email: this.email
    }
  }
}

module.exports = Author
Copy the code
  1. Open the./src/index.js
import Author from './Author'

const a = 'Hello ITEM'
console.log(a)

const img = new Image()
img.src = logo

document.getElementById('imgBox').appendChild(img)

const author = new Author();

console.log(author.info)
Copy the code
  1. Open thepackage.json
{
  "name": "webpack-source-map"."version": "1.0.0"."description": ""."main": "index.js"."license": "MIT"."scripts": {
    "build": "webpack"
  },
  "devDependencies": {
    "@babel/core": "^ 7.6.4." "."@babel/preset-env": "^ 7.6.3." "."babel-loader": "^ 8.0.6"."html-webpack-plugin": "^ 3.2.0"."webpack": "^ 5.44.0"."webpack-cli": "^ 4.7.2." "}}Copy the code
  1. Open thewebpack.config.js
// Multi-entry packing
module.exports = [
  {
    entry: './src/index.js'.output: {
      filename: 'a.js'}}, {entry: './src/index.js'.output: {
      filename: 'b.js'}}]Copy the code

Run the pack command NPM run build to see the results

β”œβ”€ a.exercises ─ b.exercisesCopy the code

Don’t worry about what is in the packing result a.js B.js, the purpose of this step is to test multi-entry packing

The purpose of the transformation into multiple entrances is to facilitate our later comparison

  1. Use separate package entry for different configuration items, openwebpack.config.jsModify the
const HtmlWebpackPlugin = require('html-webpack-plugin')

// 1) Define different packaging types
const allModes = [
  'eval'.'source-map'.'eval-source-map'.'cheap-source-map'.'inline-source-map'.'cheap-eval-source-map'.'cheap-module-source-map'.'inline-cheap-source-map'.'cheap-module-eval-source-map'.'inline-cheap-module-source-map'.'hidden-source-map'.'nosources-source-map'
]

// 2) Loop through different SourceMap modes to generate multiple packaging entries
module.exports = allModes.map(item= > {
  return {
    devtool: item,
    mode: 'none'.entry: './src/main.js'.output: {
      filename: `js/${item}.js`
    },
    module: {
      rules: [{test: /.js$/,
          use: {
            loader: 'babel-loader'.options: {
              presets: ['@babel/preset-env']}}}]},plugins: [
      3) output to different pagesnew HtmlWebpackPlugin({
        filename: `${item}.html`}}})]Copy the code
  1. Mock code error
// ./src/index.js

import Author from './Author'

const a = 'Hello ITEM'
// Error console.log is intentionally used
console.log11(a)
 
const img = new Image()
img.src = logo

document.getElementById('imgBox').appendChild(img)

const author = new Author();

console.log(author.info)
Copy the code
  1. Try to pack

Error!!

Error: SourceMap schema name is incorrect, their concatenation is regular and meaningful

We check in accordance with the rules ^ (inline – | hidden – | eval -)? (nosources-)? (cheap-(module-)?) ? Source-map $Check

Cheap-eval-source-map and cheap-module-eval-source-map seem to have some problems

// After modification
const allModes = [
  'eval'.'source-map'.'eval-source-map'.'cheap-source-map'.'inline-source-map'.'eval-cheap-source-map'.'cheap-module-source-map'.'inline-cheap-source-map'.'eval-cheap-module-source-map'.'inline-cheap-module-source-map'.'hidden-source-map'.'nosources-source-map'
]
Copy the code

Do the packaging again

There are still errors!! Then change

I checked the error message, most likely the version of HTML-webpack-pugin is too old, not compatible with Webpack5, let’s upgrade the version to “HTML-webpack-plugin “: “^5.3.2” and try again, OK

Dist β”œβ”€ JS β”‚ β”œβ”€ cheap-module-source-map.js#... There are corresponding.map filesβ”‚ β”œβ”€ Heavy Exercises - Cheap -module-source-map.js#... There areβ”‚ β”œβ”€ Anti-Flag - Cheap - Source-map.js β”‚ β”œβ”€ Cheap - Module-source-Map.js β”‚ β”œβ”€ Cheap - Source-Map.js β”‚ β”œβ”€ Cheap - Module-Source-Map.js#... There is noβ”‚ β”œ ─ eval - being - source - map. Js#... There is noβ”‚ β”œ ─ eval - source - map. Js#... There is noβ”‚ β”œ ─ eval. Js#... There is noβ”‚ β”œ ─ hidden - source - map. Js#... There areβ”‚ β”œβ”€ β”œβ”€ Inline-cheap module-source-map.js. β”‚ β”œβ”€ Inline-cheap module-source-Map.js#... There is noβ”‚ β”œ ─ the inline - being - source - map. Js#... There is noβ”‚ β”œ ─ the inline - source - map. Js#... There is noβ”‚ β”œ ─ nosources - source - map. Js#... There areβ”‚ β”œβ”€ Nosource-Map.js. β”‚ β”œβ”€ Source-Map.js#... There areβ”‚ β”” ─ the source - map. Js. Map β”œ ─ being - the module - the source - the map. The HTML β”œ ─ being - source - map. HTML β”œ ─ eval - being - the module - source - map. HTML β”œ ─ Eval-cheap source-map.html β”œβ”€ Eval.html β”œβ”€ Eval.html β”œβ”€ Hidden Source-Map.html β”œβ”€ Inline-cheap source-map.html β”œβ”€ Inline-cheap source-map.html β”œβ”€ Inline-cheap Source-Map.html β”œβ”€ Inline-cheap Source-Map.html β”œβ”€ Inline-cheap Source-Map.html Nosources - source - map. HTML β”” ─ the source - map. HTMLCopy the code

We can easily see from the directory structure that eval and inline mode have no corresponding. Map file

Next, we create a service in the dist directory and open it in the browser

And then, let’s do it one by one

evalMode:

  1. The generated code executes via eval πŸ‘‡πŸ»

  1. The source code location is marked πŸ‘‡πŸ» at @sourceurl

  1. The error location cannot be located, only a file can be located

  2. No need to generate SourceMap file, fast packaging

source-mapMode:

  1. The corresponding SourceMap file is generated, and the packaging speed is slow
  2. Locate the error line information in the source code πŸ‘‡πŸ»

eval-source-mapMode:

  1. The generated code executes via eval πŸ‘‡πŸ»

2. IncludedataUrlSourceMap file in the form

  1. The error row information can be located in the compiled code

  1. Generate SourceMap in the form of dataUrl, which is slow to pack

eval-cheap-source-mapMode:

  1. The generated code is executed through eval
  2. Contains SourceMap files in the form of dataUrl
  3. The error line information can be located in the compiled code
  4. No need to locate column information, fast packaging

eval-cheap-module-source-mapMode:

  1. The generated code is executed through eval
  2. Contains SourceMap files in the form of dataUrl
  3. The error line information can be located in the compiled code
  4. No need to locate column information, fast packaging
  5. Locate the error line in the source code πŸ‘‡πŸ»

inline-source-mapMode:

  1. Import the SourceMap file as a dataUrl πŸ‘‡πŸ»

. The rest is the same as source-map mode

hidden-source-mapMode:

  1. The SourceMap effect is not visible, but the SourceMap file is generated

nosources-source-mapMode:

  1. You can see the error location πŸ‘‡πŸ»

  1. But there’s no way to actually source it

Next, let’s summarize a little bit:

devtool build rebuild According to the code SourceMap file describe
(none) soon soon There is no There is no Unable to locate error
eval fast Fast (cache) The compiled There is no Locate the file
source-map slow slow The source code There are Locate row and column
eval-source-map slow General (cache) The compiled There are (dataUrl) Locate row and column
eval-cheap-source-map general Fast (cache) The compiled There are (dataUrl) Locate the line
eval-cheap-module-source-map slow Fast (cache) The source code There are (dataUrl) Locate the line
inline-source-map slow slow The source code There are (dataUrl) Locate row and column
hidden-source-map slow slow The source code There are Unable to locate error
nosource-source-map slow slow The source code There is no Locate the file

Compare validation rules ^ (inline – | hidden – | eval -)? (nosources-)? (cheap-(module-)?) ? Source-map $analyzes the keywords

The keyword describe
inline SourceMap is introduced in the code in the form of dataUrl
hidden Generate the SourceMap file, but do not use it
eval eval(...)Form to introduce SourceMap through the dataUrl form
nosources Don’t generate SourceMap
cheap You only need to locate row information, not column information
module Shows the error location in the source code

Well, that’s it for SourceMap

2.3 Recommended Configuration

  1. Local development:

Recommendation: eval – being – the module – the source – the map

Reason:

  • It doesn’t matter if native developers pack slowly for the first time becauseevalBecause of caching, rebuild will be quick
  • In development, we don’t write too long per line of code, just need to locate the line, so pluscheap
  • We want to be able to find errors in source code, not packaged, so we need to addmodele
  1. Production environment:

Recommended: (none)

Reason:

  • I just don’t want anyone to see my source code

3. Three hash values

The Fingerprint policy for Webpack files is to append the file name with a hash value. Caching is an advantage especially when using CDN, but if you pack filenames without hash suffixes, you will definitely suffer from caching πŸ˜‚

For example: filename: “[name][hash:8][ext]”

What do they all mean? Please look at the table below πŸ‘‡πŸ»

A placeholder explain
ext File name extension
name The file name
path File relative path
folder File folder
hash Unique hash value generated for each build
chunkhash Generates a hash value based on chunk
contenthash Generates a hash value based on the file content

Hash, chunkhash, contenthash

  • Hash: Any change to a file changes the build hash value of the entire project.
  • Chunkhash: Changes only affect the hash value of the chunk where the file resides.
  • Contenthash: Each file has a separate hash value. Changes to the file only affect its own hash value.

Two, Webpack advanced

In the second part, we will move in the direction of “can optimize” πŸƒ

In addition to configuration optimization, we also need to learn how to develop Loader and Plugin

1. Optimize the construction speed

1.1 Construction time-consuming analysis

Here we need to use the speed-measure-webpack-plugin, we refer to the documentation to configure

  1. So let’s just install it
$ npm i -D speed-measure-webpack-plugin
Copy the code
  1. Modify our configuration file webpack.config.js
.// Take time to analyze
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
const smp = newSpeedMeasurePlugin(); .constconfig = {... }module.exports = (env, argv) = > {
  // The config configuration can be modified in different modes


  return smp.wrap(config);
}
Copy the code
  1. Perform packaging

The error 🀦 🏻 came ️

This exposes one of the drawbacks of using this plugin:

  • Some Loader or Plugin versions are incompatible and need to be degraded

Here we downgrade the mini-CSS-extract-plugin to ^2.1.0 -> ^1.3.6

Reinstall the dependencies and pack again

Error: publicPath: ‘./’

output: {
    filename: 'bundle.js'.// Output the file name
    path: path.join(__dirname, 'dist'), // Output file directory
    publicPath: '/'
  },
Copy the code

Try again

Success!

Note: In Webpack 5.x it is not cost-effective to downgrade or change the configuration of the plugin to use time-wasting analysis. I will continue to use it for demonstration purposes, but it is not recommended to use it in normal development.

1.2 Optimize the Resolve Configuration

1.2.1 alias

Alias: an alias used to create an import or require, used to simplify module references.

const path = require('path')...// Path processing method
function resolve(dir){
  return path.join(__dirname, dir);
}

 const config  = {
  ...
  resolve: {// Configure the alias
    alias: {
      '~': resolve('src'),
      The '@': resolve('src'),
      'components': resolve('src/components'),}}};Copy the code

Once the configuration is complete, we can do it in the project

// Use the SRC alias ~
import '~/fonts/iconfont.css'

// Use the SRC alias @
import '@/fonts/iconfont.css'

// Use the components alias
import footer from "components/footer";
Copy the code
1.2.2 extensions

Webpack default configuration

const config = {
  / /...
  resolve: {
    extensions: ['.js'.'.json'.'.wasm'],}};Copy the code

If the user imports the module without an extension, for example

import file from '.. /path/to/file';
Copy the code

Webpack then attempts to parse modules in left-to-right order from the extensions configuration array

Note that:

  1. High frequency file name suffix in front;
  2. After manual configuration, the default configuration will be overwritten

If you want to keep the default configuration, you can use… The extended operator represents the default configuration, for example

const config = {
  / /...
  resolve: {
    extensions: ['.ts'.'... '],}};Copy the code
1.2.3 modules

Tells WebPack which directory to search for when parsing a module, the common configuration is as follows

const path = require('path');

// Path processing method
function resolve(dir){
  return path.join(__dirname, dir);
}

const config = {
  / /...
  resolve: {
     modules: [resolve('src'), 'node_modules'],}};Copy the code

Telling Webpack to find the files that need to be parsed in the SRC directory first saves you a lot of time

1. ResolveLoader

ResolveLoader has the same set of attributes as the Resolve object above, but is only used to resolve the Loader package for WebPack.

In general, keep the default configuration, but if you have a custom Loader, you need to configure it, it may not be able to find the Loader error

  • For example: we put our own loader under the loader folder

How can we configure it

const path = require('path');

// Path processing method
function resolve(dir){
  return path.join(__dirname, dir);
}

const config = {
  / /...
  resolveLoader: {
    modules: ['node_modules',resolve('loader')]}};Copy the code

1.3 externals

The externals configuration option provides the method to “exclude dependencies from the output bundle”. This feature is usually most useful to library developers, but there are a variety of applications that use it.

For example, importing jQuery from CDN instead of packaging it:

  1. The introduction of the link
<script
  src="https://code.jquery.com/jquery-3.1.0.js"
  integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk="
  crossorigin="anonymous"
></script>
Copy the code
  1. Configuration externals
const config = {
  / /...
  externals: {
    jquery: 'jQuery',}};Copy the code
  1. Using jQuery
import $ from 'jquery';

$('.my-element').animate(/ *... * /);
Copy the code

We can use this approach to strip away the dependencies that don’t need to be changed and save a lot of time packaging builds.

1.3 Narrowing the Scope

When configuring loader, specify the directory that loader uses or the directory that loader excludes more accurately. Use include and exclude to implement this function. Common examples are as follows:

  • Include: resolves modules that meet the requirements
  • Exclude: excludes the qualified modules
  • Exclude has a higher priority

For example, when configuring Babel

const path = require('path');

// Path processing method
function resolve(dir){
  return path.join(__dirname, dir);
}

const config = {
  / /...
  module: { 
    noParse: /jquery|lodash/,
    rules: [
      {
        test: /\.js$/i,
        include: resolve('src'),
        exclude: /node_modules/,
        use: [
          'babel-loader',]},// ...]}};Copy the code

1.3 noParse

  • You don’t need to parse dependent third-party large class libraries, etc., and you can configure this field to speed up builds
  • Modules ignored with noParse are not parsed in their filesimport,requireSuch as grammar
const config = {
  / /...
  module: { 
    noParse: /jquery|lodash/, rules:[...] }};Copy the code

1.4 IgnorePlugin

Prevents the following regular expression matching module from being generated when import or require is called:

  • requestRegExpA regular expression that matches (test) the resource request path.
  • contextRegExpMatches (test) a regular expression for the resource context (directory).
new webpack.IgnorePlugin({ resourceRegExp, contextRegExp });
Copy the code

The following examples demonstrate several uses of this plug-in.

  1. Install the moment plugin.
$ npm i -S moment
Copy the code
  1. Configuration IgnorePlugin
/ / introduce webpack
const webpack = require('webpack')

const config = {
  ...
  plugins: [// Configure the plug-in.new webpack.IgnorePlugin({
      resourceRegExp: /^\.\/locale$/,
      contextRegExp: /moment$/,})]};Copy the code

The purpose is to eliminate the non-Chinese voice in the plug-in, which can greatly save the volume of packaging

1.5 Multi-Process Configuration

Note: In fact, in small projects, enabling multi-process packaging can actually increase the time cost because of the overhead of starting the process and communicating between processes.

1.5.1 thread – loader

Loaders configured after thread-loader will run in a separate worker pool

  1. The installation
$ npm i -D  thread-loader
Copy the code
  1. configuration
const path = require('path');

// Path processing method
function resolve(dir){
  return path.join(__dirname, dir);
}

const config = {
  / /...
  module: { 
    noParse: /jquery|lodash/,
    rules: [
      {
        test: /\.js$/i,
        include: resolve('src'),
        exclude: /node_modules/,
        use: [
          {
            loader: 'thread-loader'.// Enable multi-process packaging
            options: {
              worker: 3,}},'babel-loader',]},// ...]}};Copy the code
1.5.2 happypack ❌

The same tool for enabling multi-process packaging, webpack5 has been deprecated.

1.6 Using cache

Using caching can greatly increase the rate of repeat builds

1.6.1 babel-loader Enables caching
  • Babel has a large time cost ratio in the process of js translation, so it caches the execution results of babel-loader, and directly reads the cache when repackaging
  • Cache location:node_modules/.cache/babel-loader

The configuration is as follows:

const config = {
 module: { 
    noParse: /jquery|lodash/,
    rules: [
      {
        test: /\.js$/i,
        include: resolve('src'),
        exclude: /node_modules/,
        use: [
          // ...
          {
            loader: 'babel-loader'.options: {
              cacheDirectory: true // Enable caching}}},],// ...]}}Copy the code

How do other loaders cache results?

Cache-loader can do this for us

1.6.2 cache – loader
  • Cache the processing results of some Loaders with high performance overhead
  • Cache location:node_modules/.cache/cache-loader
  1. The installation
$ npm i -D cache-loader
Copy the code
  1. Configure cache – loader
const config = {
 module: { 
    // ...
    rules: [{test: /\.(s[ac]|c)ss$/i.// Matches all sass/ SCSS/CSS files
        use: [
          // 'style-loader',
          MiniCssExtractPlugin.loader,
          'cache-loader'.// Get the result of the previous Loader conversion
          'css-loader'.'postcss-loader'.'sass-loader',]},// ...]}}Copy the code
1.6.3 hard – source – webpack – the plugin
  • The hard-source-webpack-plugin provides an intermediate cache for modules, reducing the repeat build time by about 80%, but the module cache is already built into Webpack 5, so you don’t need to use this plug-in
1.6.4 dll ❌

This approach to module caching is no longer recommended in Webpack 5.x because it has a better experience of caching built in

1.6.5 Cache Persistent Cache

Improve build speed by configuring webpack modules and chunks generated by the cache cache.

const config = {
  cache: {
    type: 'filesystem',}};Copy the code

2. Optimize the construction results

2.1 Analysis of construction results

With the help of the plug-in Webpack-bundle-Analyzer, we can intuitively see problems in the packaging results, such as the size of files, dependencies of each module, and whether files are repetitive, which is greatly convenient for us to diagnose problems during project optimization.

  1. The installation
$ npm i -D webpack-bundle-analyzer
Copy the code
  1. Configure the plug-in
// Import plug-ins
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin


const config = {
  // ...
  plugins: [// ...
    // Configure the plug-in
    new BundleAnalyzerPlugin({
      // analyzerMode: 'disabled', // do not start the HTTP server to display the packaged report
      // generateStatsFile: true, // Whether to generate stats.json file})]};Copy the code
  1. Modifying a Startup Command
 "scripts": {
    // ...
    "analyzer": "cross-env NODE_ENV=prod webpack --progress --mode production"
  },
Copy the code
  1. Execute compile commandnpm run analyzer

After the package is complete, the web service whose address is http://127.0.0.1:8888 is automatically started

If we just want to keep the data and not start the Web service, we can add two configurations

new BundleAnalyzerPlugin({
   analyzerMode: 'disabled'.// Do not start the HTTP server that displays packaged reports
   generateStatsFile: true.// Whether to generate stats.json file
})
Copy the code

This will only produce a state.json file when the packaging is performed again

2.2 compressed CSS

  1. Install optimize – CSS – assets – webpack – the plugin
$ npm install -D optimize-css-assets-webpack-plugin 
Copy the code
  1. Modify thewebapck.config.jsconfiguration
// ...
/ / compress CSS
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')
// ...

const config = {
  // ...
  optimization: {
    minimize: true.minimizer: [
      // Add the CSS compression configuration
      new OptimizeCssAssetsPlugin({}),
    ]
  },
 // ...
}

// ...
Copy the code
  1. View packaging results

2.3 compressed JS

Packaging in a build environment turns JS compression on by default, but after manually configuring the Optimization option, js compression is no longer done by default, requiring manual configuration.

Because Webpack5 has built-in terser-webpack-plugin, we do not need to install it repeatedly and can refer to it directly. The specific configuration is as follows

const TerserPlugin = require('terser-webpack-plugin');

const config = {
  // ...
  optimization: {
    minimize: true.// Enable minimization
    minimizer: [
      // ...
      new TerserPlugin({})
    ]
  },
  // ...
}
Copy the code

2.4 Clearing Useless CSS

Purgecss-webpack-plugin extracts CSS separately and cleans up unused CSS

  1. Installing a plug-in
$ npm i -D purgecss-webpack-plugin
Copy the code
  1. Add the configuration
// ...
const PurgecssWebpackPlugin = require('purgecss-webpack-plugin')
const glob = require('glob'); // File matching mode
// ...

function resolve(dir){
  return path.join(__dirname, dir);
}

const PATHS = {
  src: resolve('src')}const config = {
  plugins: [// Configure the plug-in
    // ...
    new PurgecssPlugin({
      paths: glob.sync(`${PATHS.src}/ * * / * `, {nodir: true]}})}),Copy the code

2.5 the Tree – shaking

Tree-shaking reduces the size of the package by weeding out unused code

  • Webpack is supported by default and needs to be set up in.bablercModel: false, can be enabled by default in the production environment

For more about tree-shaking, read πŸ‘‰πŸ». From the past to the present, talk about tree-shaking

module.exports = {
  presets: [["@babel/preset-env",
      {
        module: false.useBuiltIns: "entry".corejs: "3.9.1".targets: {
          chrome: "58".ie: "11",},},],plugins: [["@babel/plugin-proposal-decorators", { legacy: true }],
    ["@babel/plugin-proposal-class-properties", { loose: true}]]};Copy the code

2.6 the Scope Hoisting

In this way, the function declaration and memory overhead can be reduced by placing multiple modules in the same Scope and renaming them to prevent naming conflicts.

  • Webpack is supported by default and is enabled by default in production
  • Only ES6 code is supported

3. Optimize the runtime experience

The core of runtime optimization is improving the loading speed of the first screen, and the main way is

  • Reduce the volume of files to be loaded on the first screen. Files that are not needed on the first screen can be preloaded or loaded on demand

3.1 Entry point segmentation

Configure multiple packaging entry, multi-page packaging, here but more introduction

3.2 splitChunks subcontract configuration

Optimization.splitchunks is implemented based on the SplitChunksPlugin plug-in

By default, it only affects on-demand chunks, because modifying initial chunks affects script tags in the project’s HTML file.

Webpack will automatically split chunks based on the following criteria:

  • New chunks can be shared, or modules can come fromnode_modulesfolder
  • The new chunk size is greater than 20KB (before min+gz)
  • When chunks are loaded on demand, the maximum number of parallel requests is less than or equal to 30
  • The maximum number of concurrent requests is less than or equal to 30 when the initialization page is loaded
  1. Default Configuration
module.exports = {
  / /...
  optimization: {
    splitChunks: {
      chunks: 'async'.// Valid values are 'all', 'async' and 'initial'
      minSize: 20000.// The minimum size of chunk generated (β‰ˆ 20KB)
      minRemainingSize: 0.// Ensure that the minimum chunk size left after splitting exceeds the limit to avoid zero-size modules
      minChunks: 1.// The minimum number of chunks that must be shared before splitting.
      maxAsyncRequests: 30.// Maximum load times on demand (asynchronous)
      maxInitialRequests: 30.// The number of js files that can be loaded at the same time (including the entry files)
      enforceSizeThreshold: 50000.cacheGroups: { // Configure the extraction module scheme
        defaultVendors: {
          test: /[\/]node_modules[\/]/,
          priority: -10.reuseExistingChunk: true,},default: {
          minChunks: 2.priority: -20.reuseExistingChunk: true,},},},},};Copy the code
  1. Use in projects
const config = {
  / /...
  optimization: {
    splitChunks: {
      cacheGroups: { // Configure the extraction module scheme
        default: false.styles: {
            name: 'styles'.test: /\.(s? css|less|sass)$/,
            chunks: 'all'.enforce: true.priority: 10,},common: {
            name: 'chunk-common'.chunks: 'all'.minChunks: 2.maxInitialRequests: 5.minSize: 0.priority: 1.enforce: true.reuseExistingChunk: true,},vendors: {
            name: 'chunk-vendors'.test: /[\\/]node_modules[\\/]/,
            chunks: 'all'.priority: 2.enforce: true.reuseExistingChunk: true,},/ /... According to different projects to refine the split content}},}},Copy the code

3.3 Code lazy loading

For some resources that are not needed for the first screen loading, we can achieve it by lazy loading. Here is a small 🌰

  • Need: Click on the picture to add a description to the picture

1. Create an image description

desc.js

const ele = document.createElement('div')
ele.innerHTML = 'I'm a picture description'
module.exports = ele
Copy the code

2. Click on the picture to introduce the description

index.js

import './main.css';
import './sass.scss'
import logo from '.. /public/avatar.png'

import '@/fonts/iconfont.css'

const a = 'Hello ITEM'
console.log(a)

const img = new Image()
img.src = logo

document.getElementById('imgBox').appendChild(img)

// Load as needed
img.addEventListener('click'.() = > {
  import('./desc').then(({ default: element }) = > {
    console.log(element)
    document.body.appendChild(element)
  })
})
Copy the code

3. Check the effect

  • Click on the former

  • After clicking on

3.4 the prefetch and preload

Above, we used asynchronous loading to introduce the description of the picture. However, if the file that needs asynchronous loading is relatively large, loading when clicking will also affect our experience. At this time, we can consider using Prefetch to carry out pre-fetching

3.4.1 track prefetch
  • Prefetch: A resource is fetched when the browser is idle

Modify the above code

// Load as needed
img.addEventListener('click'.() = > {
  import( /* webpackPrefetch: true */ './desc').then(({ default: element }) = > {
    console.log(element)
    document.body.appendChild(element)
  })
})
Copy the code
3.4.2 preload
  • Preload: Preloads key resources that will be used later
  • ⚠️ will pull resources in advance, if not special needs, use with caution

Official website example:

import(/* webpackPreload: true */ 'ChartingLibrary');
Copy the code

4. Write a Loader

TODO

5. Write a Plugin

TODO

The mystery of the missing blogger

In fact, I intend to sort out a problem related to webpack knowledge system from August, but I did not expect to sort it out for several months πŸ˜‚

And has not finished sorting πŸ˜…

In order to avoid the end of the article rotten, or decided to update the section

This will be supplemented by the next article: In-depth WebPack

The general contents include:

  • Webpack debugging
  • Webpack build process
  • The Thermal renewal (HRM) principle
  • Webpack core library Tapabel is introduced
  • The tree – shaking principle
  • Babel & AST syntax tree

Like, follow, comment, support a wave

Your support, is my writing power, like, attention, comment, support a wave of πŸ‘

Pay close attention to my public number: the front end moves the brick worker, constructs the knowledge system together

The resources

  • Webpack official documentation
  • postcss-loader
  • file-loader
  • Learn the way to Webpack5