preface

This article mainly starts with Webpack 4.x, and it will explain the common Webpack configuration one by one. There are detailed examples for each function point, so this article is quite long, but if you can start to follow the examples in this article, I believe you will find Webpack is no more than that.

What is WebPack and why do you use it?

1.1 What is Webpack?

Simply put, it is a module packer.

1.2 Why use it?

If like before the development of an HTML file may reference more than ten JS files, and the order can not be disorderly, because they have dependencies, at the same time for ES6+ and other new syntax, less, Sass and other CSS preprocessing can not be a good solution… You need a tool to deal with these problems.

Webpack is designed to address these issues by taking your project as a whole, starting with a main entry file (such as index.js), finding all of your project’s dependency files, processing them, and finally packaging them into one (or more) browser-aware JavaScript files.

Two, a simple packing example

2.1 Preparations

I created a folder called Webpack-project on my desktop. After using the terminal to enter the folder (if you are not familiar with the command line, please refer to my blog: Command line does not work? See here), create a package.json file using the NPM init command.

npm init
Copy the code

After you type this command, the terminal will ask you a list of information such as project name, project description, author, etc., but if you don’t want to publish the module, just press Enter. (You can also use NPM init -y to generate the package.json file once, so that the terminal doesn’t ask you questions.)

2.2 installation webpack

If you want to do it all at once, install the global webpack, webpack-CLI, and the local project webpack, webpack-CLI all first, because some modules will be used later. Install webpack-CLI when installing Webapck because some functions of Webpack module have been transferred to Webpack-CLI module after webpack4.x, so both need to be installed as follows:

NPM install webpack webpack -- cli --save-dev // This is to install the local project moduleCopy the code

tips:

The above commands are shorthand: install is I,–global is -g,–save-dev is -d (this command is used to add the configuration to the package.json development environment configuration list, which will be discussed later),–save is -s, At the same time, we can use CNPM in China, and the configuration method can be checked here, so that the installation speed will be relatively fast. As follows:

CNPM I webpack -g // This is to install the global webpack command CNPM I webpack webpack-cli -d // this is to install the local project moduleCopy the code

 

2.3 Creating a File

Create two new folders in the Webpack-project folder, SRC and dist, and then create three more files:

  • index.html— In the dist folder;
  • hello.js— put it in the SRC folder;
  • index.js— put it in the SRC folder;

At this point, the project structure is as follows:

We write the HTML code in index.html, which is used to import our packed JS file:


      
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Webpack Project</title>
</head>
<body>
    <div id='root'></div>
    <script src="bundle.js"></script>   <! -- This is the packaged JS file, which we'll temporarily call bundle.js-->
</body>
</html>
Copy the code

We export a module in hello.js:

// hello.js
module.exports = function() {
    let hello = document.createElement('div');
    hello.innerHTML = "Long time no see!";
    return hello;
  };
Copy the code

Then introduce this module (hello.js) in index.js:

//index.js 
const hello = require('./hello.js');
document.querySelector("#root").appendChild(hello());
Copy the code

Js module into the index.js module, and then we can package the index.js module into bundle.js, which can be referenced by index.html. This is the simplest webpack principle.

2.4 WebPack starts

Run the following command on the terminal to package:

Webpack SRC /index.js --output dist/bundle.js --output can be short as -oCopy the code

This is equivalent to packing the index.js file from the SRC folder into the bundle.js file from the dist folder, generating bundle.js for reference in the index.html file.

The results are as follows:

As you can see, webpack compiles both index.js and hello.js. Now open index.html and you can see the following result:

Yes, we’ve successfully packaged with WebPack, and that’s all webPack is! However, it is annoying to type such a long command in the terminal every time. Fortunately, there is a lazy method, let’s take a look.

2.5 Use WebPack through configuration files

In fact, Webpack has many functions, but also very convenient, we can create a new configuration file webpack.config.js in the root directory of the current project, we write the following simple configuration code, so far only involved in the entry configuration (equivalent to our index.js, Package from there) and export configuration (equivalent to the bundle.js we generated).

// webpack.config.js
module.exports = {
    entry: __dirname + "/src/index.js".// Import file
    output: {
        path: __dirname + "/dist".// The place where the packed files are stored
        filename: "bundle.js" // Output the file name after packaging}}Copy the code

Note: __dirname is a global variable in Node.js that points to the directory where the script is currently executed, namely C:\Users\ SJT \DeskTop\webpack-project (this is my current directory).

The path module of Node.js is used to handle the absolute path, so we can also use the following syntax, and the effect is the same as above:

// webpack.config.js
const path = require('path');
module.exports = {
    entry: path.join(__dirname, "/src/index.js"), // Import file
    output: {
        path: path.join( __dirname, "/dist"), // The place where the packed files are stored
        filename: "bundle.js" // Output the file name after packaging}}Copy the code

Note: Path. join is used to join path fragments.

With this configuration file, we can pack by simply running the webpack command in the terminal, which automatically references the configuration options in the webpack.config.js file, as shown in the following example:

Done, isn’t it more convenient, feels less low, but can it be more convenient and intelligent? Of course!

2.6 Smarter packaging

We only use webpack commands in the terminal for packaging now, but what if there are more operations to be done at the same time in the future? So we need to figure out how to integrate all of these commands, and that’s where the package.json file comes in handy. The package.json file now looks something like this:

{
  "name": "webpack-project"."version": "1.0.0"."description": ""."main": "index.js"."scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"JSON file does not support comments, please clear when referencing},"keywords": []."author": ""."license": "ISC"."devDependencies": {
    "webpack": "^ 4.23.1"."webpack-cli": "^ 3.1.2." "}}Copy the code

Modified as follows:

{" name ":" webpack - project ", "version" : "1.0.0", "description" : ""," main ":" index. Js ", "scripts" : {" start ": }, "keywords": [], "author": "", "license": "ISC", "devDependencies": {"webpack": "^4.23.1", "webpack-cli": "^3.1.2"}}Copy the code

Note: The script in package.json will execute the command with the command name you set.

Start is a special command, which can be executed by adding NPM to start. If we remember another name, such as build, we need to use NPM run and build. The NPM run build command. Now we execute the NPM start command:

OK, done, isn’t that easy, but WebPack is much more than that, let’s continue.

Build a local server

Vue and React frameworks run on local servers. Can we do the same? Of course!

3.1 webpack-dev-server Configures the local server

Webpack provides an optional local development server, which is built on Node.js and is a separate component that needs to be installed separately as a project dependency before being configured in Webpack:

cnpm i webpack-dev-server -D
Copy the code

DevServer as one of the WebPack configuration options, here are some of its configuration options:

  • contentBase: set the directory to which the server will read the files, currently set to “./dist”
  • port: Sets the port number. If omitted, the default port number is8080
  • inline: set totrue, automatically refreshes the page when the source file changes
  • historyApiFallback: set totrue, all jumps will point toindex.html

Now we add these configurations to the webpack.config.js file as follows:

// webpack.config.js
const path = require('path');
module.exports = {
    entry: path.join(__dirname, "/src/index.js"), // Import file
    output: {
        path: path.join( __dirname, "/dist"), // The place where the packed files are stored
        filename: "bundle.js" // Output the file name after packaging
    },
    devServer: {
        contentBase: "./dist".// Directory of the files loaded by the local server
        port: "8088".// Set port number to 8088
        inline: true.// Refresh files in real time after modification
        historyApiFallback: true./ / don't jump}}Copy the code

Let’s go ahead and add the startup command to the package.json file:

{
  "name": "webpack-project"."version": "1.0.0"."description": ""."main": "index.js"."scripts": {
    "build": "webpack"."dev": "webpack-dev-server --open"
  },
  "keywords": []."author": ""."license": "ISC"."devDependencies": {
    "webpack": "^ 4.23.1"."webpack-cli": "^ 3.1.2." "."webpack-dev-server": "^ 3.1.10"}}Copy the code

We change the name of the start command to build, so that it is more semantic, and most scaffolding usually uses this name, we use dev (short for development, meaning development environment) to start the local server, webpack-dev-server is the command to start the server, –open is used to automatically open the browser after the server is started. At this time, we can customize the command way to show the convenience, multiple commands can be integrated to run, i.e. we define a dev command name to run the webpack-dev-server and –open two commands at the same time.

Now enter NPM run dev at terminal to run the server:

In this way, we can view the page in http://localhost:8088/ (to exit the server, press CTRL + C and then press Y to confirm, you can exit the server and run).

3.2 Source Maps Debugging configuration

The Source Map is used to solve the problem of debugging code, which is necessary for development. It is not easy to find errors in packaged files.

Through the following configuration, we will generate a. Map file corresponding to the package file during packaging, making the compiled code more readable and easier to debug.

// webpack.config.js
const path = require('path');
module.exports = {
    entry: path.join(__dirname, "/src/index.js"), // Import file
    output: {
        path: path.join( __dirname, "/dist"), // The place where the packed files are stored
        filename: "bundle.js" // Output the file name after packaging
    },
    devServer: {
        contentBase: "./dist".// Directory of the files loaded by the local server
        port: "8088".// Set port number to 8088
        inline: true.// Refresh files in real time after modification
        historyApiFallback: true./ / don't jump
    },
    devtool: 'source-map'  // Generates a complete.map file for debugging, but also slows down packaging
}
Copy the code

After the configuration, we run NPM run build again to pack, and we see that there is an extra bundle.js.map file in the dist folder as follows:

devtool: 'source-map'

Four, Loaders

Loaders is one of the most powerful functions of WebPack. Through different loaders, WebPack has the ability to call external scripts or tools to realize the processing of files of different formats, such as converting SCSS to CSS, converting ES66, ES7 syntax to syntax that can be recognized by the current browser. JSX into JS and other functions.

Loaders needs to be installed separately and configured under modules in webpack.config.js. Loaders configuration includes the following aspects:

  • test: a regular expression that matches the extension name of the file loaders is handling (must)
  • loader: Loader name (mandatory)
  • include/exclude: Manually add files (folders) that must be processed or mask files (folders) that do not need to be processed (optional).
  • options: Additional setup options for Loaders (optional)

 

4.1 configuration CSS – loader

If we want to load a CSS file, we need to install the style-loader and csS-loader configuration:

cnpm i style-loader css-loader -D
Copy the code
// webpack.config.js
const path = require('path');
module.exports = {
    entry: path.join(__dirname, "/src/index.js"), // Import file
    output: {
        path: path.join( __dirname, "/dist"), // The place where the packed files are stored
        filename: "bundle.js" // Output the file name after packaging
    },
    devServer: {
        contentBase: "./dist".// Directory of the files loaded by the local server
        port: "8088".// Set port number to 8088
        inline: true.// Refresh files in real time after modification
        historyApiFallback: true./ / don't jump
    },
    devtool: 'source-map'.// Generates a complete.map file for debugging, but also slows down packaging
    module: {
        rules: [{test: /\.css$/.// Matches files that end in.css
                use: ['style-loader'.'css-loader']  // The loader that needs to be used must be in this order, because the calls to the Loader are compiled from right to left}}}]Copy the code

Create a style. CSS file in SRC:

/* style.css */
body {
    background: gray;
}
Copy the code

Reference it in index.js:

//index.js 
import './css/style.css';  / / import CSS

const hello = require('./hello.js');
document.querySelector("#root").appendChild(hello());
Copy the code

When we run NPM run dev, the background of the page turns gray.

What if you want to compile sass files?

4.2 configuration sass

CNPM I sass-loader node-sass -d // The sass-loader depends on node-sass, so node-sass needs to be installedCopy the code

Add sass rules:

// webpack.config.js
const path = require('path');
module.exports = {
    entry: path.join(__dirname, "/src/index.js"), // Import file
    output: {
        path: path.join( __dirname, "/dist"), // The place where the packed files are stored
        filename: "bundle.js" // Output the file name after packaging
    },
    devServer: {
        contentBase: "./dist".// Directory of the files loaded by the local server
        port: "8088".// Set port number to 8088
        inline: true.// Refresh files in real time after modification
        historyApiFallback: true./ / don't jump
    },
    devtool: 'source-map'.// Generates a complete.map file for debugging, but also slows down packaging
    module: {
        rules: [{test: /\.css$/.// Matches files that end in.css
                use: ['style-loader'.'css-loader']  // The loader that needs to be used must be in this order, because the calls to the Loader are compiled from right to left
            },
            {
                test: /\.(scss|sass)$/.// Re matches files ending in. SCSS and. Sass
                use: ['style-loader'.'css-loader'.'sass-loader']  // The loader that needs to be used must be in this order, because the calls to the Loader are compiled from right to left}}}]Copy the code

Create a new blue.scss file in the CSS folder:

/* blue.scss */
$blue: blue;
body{
    color: $blue;
} 
Copy the code

Introducing blue.scss in index.js:

//index.js 
import './css/style.css';   / / import CSS
import './css/blue.scss';   / / import SCSS

const hello = require('./hello.js');
document.querySelector("#root").appendChild(hello());
Copy the code

NPM run dev restarts the server and should produce the following result:

There are image loader, font loader and so on are not listed, interested can go to webpack official website to view, are the same routine.

Five, the Babel

Babel is actually a platform for compiling JavaScript. It compiles code to help you:

  • Enables you to use the latest JavaScript code (ES6, ES7…) Regardless of whether the new standard is fully supported by current browsers;
  • Allows you to use javascripts based extensions of languages such as React JSX;

5.1 Installation and configuration of Babel

Babel is actually a few modular packages, the core function is located in the NPM package called Babel-core, WebPack can use its different packages together, for each function or extension you need, You’ll need to install separate preset packages (the most used are babel-preset-env for ES6 and babel-preset- React for JSX).

CNPM I babel-core babel-loader babel-preset-env babel-preset- react-d // babel-preset-env env indicates the preprocessing of the current environment, Instead of using babel-Preset - ES2015 only for one environment as beforeCopy the code
// webpack.config.js
const path = require('path');
module.exports = {
    entry: path.join(__dirname, "/src/index.js"), // Import file
    output: {
        path: path.join( __dirname, "/dist"), // The place where the packed files are stored
        filename: "bundle.js" // Output the file name after packaging
    },
    devServer: {
        contentBase: "./dist".// Directory of the files loaded by the local server
        port: "8088".// Set port number to 8088
        inline: true.// Refresh files in real time after modification
        historyApiFallback: true./ / don't jump
    },
    devtool: 'source-map'.// Generates a complete.map file for debugging, but also slows down packaging
    module: {
        rules: [{test: /\.css$/.// Matches files that end in.css
                use: ['style-loader'.'css-loader']  // The loader that needs to be used must be in this order, because the calls to the Loader are compiled from right to left
            },
            {
                test: /\.(scss|sass)$/.// Re matches files ending in. SCSS and. Sass
                use: ['style-loader'.'css-loader'.'sass-loader']  // The loader that needs to be used must be in this order, because the calls to the Loader are compiled from right to left
            },
            {                             / / JSX configuration
                test: /(\.jsx|\.js)$/.use: {                    If there are multiple configurations, you can write this object
                    loader: "babel-loader".options: {
                        presets: [
                            "env"."react"]}},exclude: /node_modules/}}}]Copy the code

Now that we can support ES6 and JSX syntax, let’s try using React. However, we need to install the react and React-DOM modules first.

cnpm i react react-dom -S
Copy the code

Now let’s change the hello.js file:

// hello.js
import React, {Component} from 'react'; // These two modules must be introduced

let name = 'Alan';

export default class Hello extends Component{
    render() {
        return (
            <div>
                {name}
            </div>); }}Copy the code

Modify the index.js file:

//index.js 
import './css/style.css';  / / import CSS
import './css/blue.scss';  / / import SCSS

import React from 'react';
import {render} from 'react-dom';
import Hello from './hello'; // the.js suffix can be omitted

render(<Hello />, document.getElementById('root'));
Copy the code

After running NPM run dev at this point, you may find the following result:

This is because the official default Babel – loader | Babel corresponding version needs to be consistent: namely the Babel – loader needs to match the latest version for Babel, detail can refer to this blog.

There are two solutions:

  • Roll back an earlier version
cnpm i babel-loader@7 babel-core babel-preset-env -D
Copy the code
  • Update to highest version:
cnpm i babel-loader @babel/core @babel/preset-env webpack -D
Copy the code

NPM run dev (NPM run dev)

Arrived here is feeling very cool, not be configuration, want to use what to configure what.

5.2 Optimize Babel Configuration

Although Babel can be configured in webpack.config.js, modularity is now popular. Perhaps Babel has expanded to include more configuration items. Why don’t we extract it and put it in the.babelrc file in the root directory (webpack automatically calls the Babel configuration option in the.babelrc file)?

We will create a new.babelrc file under the project root directory:

// webpack.config.js
const path = require('path');
module.exports = {
    entry: path.join(__dirname, "/src/index.js"), // Import file
    output: {
        path: path.join( __dirname, "/dist"), // The place where the packed files are stored
        filename: "bundle.js" // Output the file name after packaging
    },
    devServer: {
        contentBase: "./dist".// Directory of the files loaded by the local server
        port: "8088".// Set port number to 8088
        inline: true.// Refresh files in real time after modification
        historyApiFallback: true./ / don't jump
    },
    devtool: 'source-map'.// Generates a complete.map file for debugging, but also slows down packaging
    module: {
        rules: [{test: /\.css$/.// Matches files that end in.css
                use: ['style-loader'.'css-loader']  // The loader that needs to be used must be in this order, because the calls to the Loader are compiled from right to left
            },
            {
                test: /\.(scss|sass)$/.// Re matches files ending in. SCSS and. Sass
                use: ['style-loader'.'css-loader'.'sass-loader']  // The loader that needs to be used must be in this order, because the calls to the Loader are compiled from right to left
            },
            {                             / / JSX configuration
                test: /(\.jsx|\.js)$/.use: {                    If there are multiple configurations, you can write this object
                    loader: "babel-loader"
                },
                exclude: /node_modules/   // Exclude matching node_modules}}}]Copy the code
{"env" : ["env", "react"]}Copy the code

Everything should be working fine at this point, so let’s move on to the powerful plug-in module.

6. Plugins

Plugins are used to extend Webpack functionality, and they take effect throughout the build process to perform related tasks. Loaders and Plugins are often confused, but they are completely different things, so to speak, Loaders are used to process source files during the packaging build process (JSX, Scss, Less..). One at a time, the plug-in does not directly manipulate individual files, it directly affects the entire build process.

6.1 How do I Use plug-ins

To use a plug-in, you need to install it via NPM, and then add an instance of the plug-in to the plugins(which are an array) configuration item in the webpack.config.js configuration file. Let’s start with a simple copyright declaration plug-in.

// webpack.config.js
const webpack = require('webpack');  // This plugin does not need to be installed. It is webPack-based and requires the introduction of the WebPack module

module.exports = {
    ...
    module: {
        rules: [{test: /\.css$/.// Matches files that end in.css
                use: ['style-loader'.'css-loader']  // The loader that needs to be used must be in this order, because the calls to the Loader are compiled from right to left
            },
            {
                test: /\.(scss|sass)$/.// Re matches files ending in. SCSS and. Sass
                use: ['style-loader'.'css-loader'.'sass-loader']  // The loader that needs to be used must be in this order, because the calls to the Loader are compiled from right to left
            },
            {                             / / JSX configuration
                test: /(\.jsx|\.js)$/.use: {                    If there are multiple configurations, you can write this object
                    loader: "babel-loader"
                },
                exclude: /node_modules/   // Exclude matching node_modules}},plugins: [
        new webpack.BannerPlugin(All Rights Reserved.)  // new an instance of the plug-in]}Copy the code

After running the NPM run build package, we can see that the bundle.js file displays as follows:

6.2 Automatically Generating HTML Files (HtmlWebpackPlugin)

So far we have used the original index.html file and imported bundle.js manually. If we import more than one JS file and change the js file name, we will have to manually change the js file name in index.html. So is it possible to automatically generate index.html and reference the packaged JS? The HtmlWebpackPlugin is designed to solve this problem:

First install the plug-in

cnpm i html-webpack-plugin -D
Copy the code

Then we make some changes to the project structure:

  1. thedistDelete the entire folder;
  2. insrcCreate a new folder under this folderindex.template.html(Name custom) file template (of course this is optional, because even if you don’t set the template,HtmlWebpackPluginThe plug-in also generates defaultshtmlFile, where we set up modules to make our development more flexible), as follows:
<! -- index.template.html -->

      
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Here is Template</title>
  </head>
  <body>
    <div id='root'>
    </div>
  </body>
</html>
Copy the code

In webpack.config.js, we introduce HtmlWebpackPlugin and configure the template that references our Settings as follows:

// webpack.config.js
const path = require('path');  // Path processing module
const webpack = require('webpack');  // This plugin does not need to be installed. It is webPack-based and requires the introduction of the WebPack module
const HtmlWebpackPlugin = require('html-webpack-plugin'); // Introduce the HtmlWebpackPlugin

module.exports = {
    entry: path.join(__dirname, "/src/index.js"), // Import file
    output: {
        path: path.join( __dirname, "/dist"), // The place where the packed files are stored
        filename: "bundle.js" // Output the file name after packaging
    },
    devServer: {
        contentBase: "./dist".// Directory of the files loaded by the local server
        port: "8088".// Set port number to 8088
        inline: true.// Refresh files in real time after modification
        historyApiFallback: true./ / don't jump
    },
    devtool: 'source-map'.// Generates a complete.map file for debugging, but also slows down packaging
    module: {
        rules: [{test: /\.css$/.// Matches files that end in.css
                use: ['style-loader'.'css-loader']  // The loader that needs to be used must be in this order, because the calls to the Loader are compiled from right to left
            },
            {
                test: /\.(scss|sass)$/.// Re matches files ending in. SCSS and. Sass
                use: ['style-loader'.'css-loader'.'sass-loader']  // The loader that needs to be used must be in this order, because the calls to the Loader are compiled from right to left
            },
            {                             / / JSX configuration
                test: /(\.jsx|\.js)$/.use: {                    If there are multiple configurations, you can write this object
                    loader: "babel-loader"
                },
                exclude: /node_modules/   // Exclude matching node_modules}},plugins: [
        new webpack.BannerPlugin(All Rights Reserved.),  // new an instance of the plug-in
        new HtmlWebpackPlugin({
            template: path.join(__dirname, "/src/index.template.html")// New an instance of the plugin and pass in the relevant parameters}})]Copy the code

Then we use the NPM run build package. You can see that the dist folder and HTML files are automatically generated as follows:

Why is the dist folder automatically generated? Because we defined the location of the export file as dist in the output export configuration item, and the export file is called bundle.js, HtmlWebpackPlugin will automatically reference the bundle.js file in index.html. If you change the exit file name in the webpack.config.js file, the index.html file will also automatically change the file name.

6.3 clean up/distThe folder (CleanWebpackPlugin)

You may have noticed that before we delete the /dist folder, our /dist folder is a bit cluttered because of the previous code examples. Webpack generates files and places them in the /dist folder, but WebPack has no way of keeping track of which files are actually used in the project.

It is generally recommended to clean up the /dist folder before each build, so only the files needed are generated, which is where the CleanWebpackPlugin comes in.

cnpm i clean-webpack-plugin -D
Copy the code
// webpack.config.js. const CleanWebpackPlugin =require('clean-webpack-plugin'); // Introduce the CleanWebpackPlugin

module.exports = {
    ...
    plugins: [
        new webpack.BannerPlugin(All Rights Reserved.),  // new an instance of the plug-in
        new HtmlWebpackPlugin({
            template: path.join(__dirname, "/src/index.template.html")// New an instance of the plugin and pass in the relevant parameters
        }),
        new CleanWebpackPlugin(['dist']),  // The name of the folder to be cleaned]}Copy the code

Plug-ins are used the same way, first introducing and then new an instance, which can pass in parameters.

Now when we run NPM run build, we’ll see that WebPack will first remove the /dist folder and then produce a new /dist folder.

6.4 hot update (HotModuleReplacementPlugin)

HotModuleReplacementPlugin (HMR) is a very useful plug-ins, can automatically refresh preview effect after we modify the code. Methods:

  1. devServerAdded to the configuration itemhot: trueParameters.
  2. becauseHotModuleReplacementPluginiswebpackModule comes with it, so importwebpackLater, inpluginsYou can use it directly in the configuration item.
// webpack.config.js. const webpack =require('webpack');  // This plugin does not need to be installed. It is webPack-based and requires the introduction of the WebPack module

module.exports = {
    ...
    devServer: {
        contentBase: "./dist".// Directory of the files loaded by the local server
        port: "8088".// Set port number to 8088
        inline: true.// Refresh files in real time after modification
        historyApiFallback: true./ / don't jump
        hot: true / / hot update},... plugins: [new webpack.BannerPlugin(All Rights Reserved.),  // new an instance of the plug-in
        new HtmlWebpackPlugin({
            template: path.join(__dirname, "/src/index.template.html")// New an instance of the plugin and pass in the relevant parameters
        }),
        new CleanWebpackPlugin(['dist']),  // Pass in the name of the folder to be cleaned
        new webpack.HotModuleReplacementPlugin() // Hot update plugin]}Copy the code

After we restart the NPM run dev project and modify the hello.js content, we will find that the browser preview will refresh automatically (it may be slow because we are using source-map and other configuration effects, which we will deal with later when the code is separated).

Vii. Project optimization and expansion

7.1 Code Separation

In the current development environment is to advocate modularization, webpack is no exception, we front webpack.config.js configuration file, in fact, did not configure many things so much, if the future increase more configuration, is not dazzling, so the best way is to split it, convenient management:

1. We create three new files in the root directory, namely webpack.common.js, webpack.dev.js, and webpack.prod.js, respectively representing the public configuration file, development environment configuration file, and production environment (the environment when the project is launched) configuration file.

2. Install a merge module plug-in:

cnpm i webpack-merge -D
Copy the code

3. Split the webpack.config.js code into the three newly created files and delete the webpack.config.js file as follows:

// webpack.common.js
const path = require('path');  // Path processing module
const webpack = require('webpack');  // This plugin does not need to be installed. It is webPack-based and requires the introduction of the WebPack module
const HtmlWebpackPlugin = require('html-webpack-plugin'); // Introduce the HtmlWebpackPlugin

module.exports = {
    entry: path.join(__dirname, "/src/index.js"), // Import file
    output: {
        path: path.join( __dirname, "/dist"), // The place where the packed files are stored
        filename: "bundle.js" // Output the file name after packaging
    },
    module: {
        rules: [{test: /\.css$/.// Matches files that end in.css
                use: ['style-loader'.'css-loader']  // The loader that needs to be used must be in this order, because the calls to the Loader are compiled from right to left
            },
            {
                test: /\.(scss|sass)$/.// Re matches files ending in. SCSS and. Sass
                use: ['style-loader'.'css-loader'.'sass-loader']  // The loader that needs to be used must be in this order, because the calls to the Loader are compiled from right to left
            },
            {                             / / JSX configuration
                test: /(\.jsx|\.js)$/.use: {                    If there are multiple configurations, you can write this object
                    loader: "babel-loader"
                },
                exclude: /node_modules/   // Exclude matching node_modules}},plugins: [
        new webpack.BannerPlugin(All Rights Reserved.),  // new an instance of the plug-in
        new HtmlWebpackPlugin({
            template: path.join(__dirname, "/src/index.template.html")// New an instance of the plugin and pass in the relevant parameters
        }),
        new webpack.HotModuleReplacementPlugin()
    ]
}
Copy the code
// webpack.dev.js
const merge = require('webpack-merge');  // Introduce the webpack-merge function module
const common = require('./webpack.common.js'); / / introduce webpack.com mon. Js

module.exports = merge(common, {   // Merge webpack.common.js into the current file
    devServer: {
        contentBase: "./dist".// Directory of the files loaded by the local server
        port: "8088".// Set port number to 8088
        inline: true.// Refresh files in real time after modification
        historyApiFallback: true./ / don't jump
        hot: true     / / thermal load}})Copy the code
// webpack.prod.js
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const CleanWebpackPlugin = require('clean-webpack-plugin'); // Introduce the CleanWebpackPlugin

module.exports = merge(common, { // Merge webpack.common.js into the current file
    devtool: 'source-map'.// Generates a complete.map file for debugging, but also slows down packaging
    plugins: [
        new CleanWebpackPlugin(['dist']),  // The name of the folder to be cleaned]})Copy the code

At this point our project catalog is as follows:

4. Set package.json to scripts:

{
  "name": "webpack-project"."version": "1.0.0"."description": ""."main": "index.js"."scripts": {
    "build": "webpack --config webpack.prod.js"."dev": "webpack-dev-server --open --config webpack.dev.js"
  },
  "keywords": []."author": ""."license": "ISC"."devDependencies": {
    "babel-core": "^ 6.26.3"."babel-loader": "^ 7.1.5." "."babel-preset-env": "^ 1.7.0"."babel-preset-react": "^ 6.24.1"."clean-webpack-plugin": "^ 0.1.19"."css-loader": "^ 1.0.0"."html-webpack-plugin": "^ 3.2.0"."node-sass": "^ 4.9.4." "."sass-loader": "^ 7.1.0"."style-loader": "^ 0.23.1"."webpack": "^ 4.23.1"."webpack-cli": "^ 3.1.2." "."webpack-dev-server": "^ 3.1.10"."webpack-merge": "^ 4.1.4." "
  },
  "dependencies": {"react": "^ 16.6.0"."react-dom": "^ 16.6.0"}}Copy the code

We changed the build command to webpack –config webpack.prod.js, which means to point the package configuration to the webpack.prod.js configuration file, whereas before we only need to use a webpack command. Since the webpack command points to the file name webpack.config.js by default, we have changed the file name, so we need to customize pointing to the new file. The same goes for the dev command.

Then we run NPM run build and NPM run dev, and the effect should be the same as before we split the code.

Note: As for package.json files, I will mention a few more words, because some of you may have some doubts about the -d, -s, or -g commands we used to install the module, because you don’t know when to add the suffix. The package.json file is actually used to install dependencies, which can be used as a dependency installation instructions list, meaning that if we upload projects or send them to other developers, we definitely don’t send /node_modules because it’s too big, impractical and unnecessary. Json file, and NPM install can install all the dependencies we need, but only if there are records in the package.json file, which is why we add -d, -s commands when installing the module. -d stands for “save-dev” and “devDependencies” in package.json. -s stands for “save” and “devDependencies” in production. Json < span style = “max-width: 100%; clear: both; min-width: 1em; Therefore, we must not forget to add the corresponding suffix command when installing modules, so that our modules have traces to follow, or other development colleagues to take over your project, will be waiting for you at the door after work (after school) do not know.

Far away, I hope not to abandon, but also want to speak in more detail!

7.2 Multiple entrances and exits

So far we have one entry file and one exit file. What if I have more than one entry file? Let’s try it:

In webpack.common.js, there are three ways to write entry: string, array and object. Usually, we use object more, so we change it to object. First, we create a new two-.js file in the SRC folder, with any name. Since there are multiple entrances, there must be multiple exits for one-to-one correspondence, so the configuration of entry and output is as follows:

// webpack.common.js. module.exports = {entry: {
        index: path.join(__dirname, "/src/index.js"),
        two: path.join(__dirname, "/src/two.js")},output: {
        path: path.join( __dirname, "/dist"), // The place where the packed files are stored
        filename: "[name].js" // Output the file name after packaging},... }Copy the code
// two.js
function two() {
    let element = document.createElement('div');
    element.innerHTML = 'I'm the second entry file';
    return element;
}

document.getElementById('root').appendChild(two());
Copy the code

NPM run dev: /dist: 2.js /dist: 2.js /dist: 2.js /dist: 2.js /dist: 2.js /dist: 2.js

7.3 Adding CSS prefixes, Separating CSS, Eliminating Redundant CSS, and Separating Images

1. Add CSS prefixes

When writing CSS, some attributes need to be prefixed manually, such as -webkit-border-radius: 10px; Can we make it auto-add in webpack? That’s a must. First of all, you must install the module:

cnpm i postcss-loader autoprefixer -D
Copy the code

After installing the two modules, create a new postcss.config.js file in the project root directory:

// postcss.config.js
module.exports = {
    plugins: [
        require('autoprefixer')  // References the Autoprefixer module]}Copy the code

Add the following styles to style.css:

/* style.css */
body {
    background: # 999;
}

#root div{
    width: 200px;
    margin-top: 50px;
    transform: rotate(45deg); /* This property produces the prefix */
}
Copy the code

Modify the CSS-loader configuration in the webpack.common.js file:

. module.exports = { ... module: {rules: [{test: /\.css$/.// Matches files that end in.css
                use: [            
                    {loader: 'style-loader'}, // Object configuration loader is used here
                    {loader: 'css-loader'},
                    {loader: 'postcss-loader'} / / use postcss - loader]},... ] },... }Copy the code

Then we run NPM run dev and the CSS style is automatically prefixed as follows:

2. The separation of CSS

The idea behind Webpack is to pack CSS and JS into a single file, but what if we want to separate out CSS?

CNPM I extract-text-webpack-plugin@next -d // add @next to install the latest, otherwise it will failCopy the code

After installing the above plug-in, import and use it in the webpack.common.js file:

// webpack.common.js. const ExtractTextPlugin =require('extract-text-webpack-plugin') // Introduce split plugins

module.exports = {
    ...
    module: {
        rules: [{test: /\.css$/.// Matches files that end in.css
                use: ExtractTextPlugin.extract({  // Here we need to call the extract method inside the separate plug-in
                    fallback: 'style-loader'.// The CSS processed by postCSS-loader and CSS-loader is finally processed by style-loader
                    use: ['css-loader'.'postcss-loader']})},... ] },plugins: [
        ...
        new ExtractTextPlugin('css/index.css') // Separate the CSS to the index.css folder in the /dist folder]}Copy the code

If you run NPM run build, you will find that the/CSS folder and index.css file are added to the /dist folder.

Now webpack4.x version of CSS separation method can use mini-CSS -extract-plugin, specific usage can refer to here, the same formula ha.

3. Eliminate redundant CSS

Sometimes we write too much CSS, we may unconsciously repeat some styles, which causes redundant code, and forget to check before going online. In this regard, we should try to optimize it, Webpack has this function.

CNPM I PurifyCSS-webpack Purify CSS glob-d after installing the three modules, we should configure them in the webpack.prod.js file, because normally we are optimizing the code in production environment. Introduce the clean-Webpack-plugin and glob plugin and use them:

// webpack.prod.js
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const CleanWebpackPlugin = require('clean-webpack-plugin'); // Introduce the CleanWebpackPlugin

const path = require('path');
const PurifyCssWebpack = require('purifycss-webpack'); // Introduce the PurifyCssWebpack plugin
const glob = require('glob');  // Introduces the glob module, which is used to scan all CSS references in HTML files

module.exports = merge(common, {   // Merge webpack.common.js into the current file
    devtool: 'source-map'.// Generates a complete.map file for debugging, but also slows down packaging
    plugins: [
        new CleanWebpackPlugin(['dist']),  // The name of the folder to be cleaned
        new PurifyCssWebpack({
            paths: glob.sync(path.join(__dirname, 'src/*.html')) // Synchronously scan all CSS references in HTML files})]})Copy the code

Let’s try adding some extra code to the style. CSS file:

/* style.css */
body {
    background: # 999;
}

#root div{
    width: 200px;
    margin-top: 50px;
    transform: rotate(45deg); /* This property produces the prefix */
}

.a{                 /* Redundant CSS */
    color: black;     
}

.b{                 /* Redundant CSS */
    width: 50px;
    height: 50px;
    background: yellow;
}
Copy the code

Then we run the NPM run build and find that there is no extra.a and.b code in the index.css package:

/* index.css */

body {
  background: # 999;
}

#root div {
  width: 200px;
  margin-top: 50px;
  -webkit-transform: rotate(45deg);
  transform: rotate(45deg);
  /* This property produces the prefix */
}
/*# sourceMappingURL=index.css.map*/
Copy the code

4. Manipulate images

So far we haven’t talked about images. To use images, we need to install two Loaders:

CNPM I url-loader file-loader -d = CNPM I url-loader file-loader -d = CNPMCopy the code

Then configure url-loader in webpack.common.js:

// webpack.common.js. module.exports = { ... module: {rules: [{test: /\.css$/.// Matches files that end in.css
                use: ExtractTextPlugin.extract({
                    fallback: 'style-loader'.use: ['css-loader'.'postcss-loader']})}, {test: /\.(png|jpg|svg|gif)$/.// Re matches the image format name
                use: [
                    {
                        loader: 'url-loader'  / / use url - loader}}],... ] },... }Copy the code

Let’s modify the style. CSS and change the background to the image background:

/* style.css */
body {
    background: url(.. /images/coffee.png) top right repeat-y;/* Set the background of the image */
}

#root div{
    width: 200px;
    margin-top: 50px;
    transform: rotate(45deg); /* This property produces the prefix */
}

.a{
    color: black;
}

.b{
    width: 50px;
    height: 50px;
    background: yellow;
}
Copy the code

The following information is displayed after the NPM run dev command is run:

But the background image becomes base64 because Webpack automatically optimizes the image and sends fewer requests, but what if I want to make it a path?

We can change the loader configuration of webpack.common.js to add options:

// webpack.common.js. module.exports = { ... module: {rules: [{test: /\.css$/.// Matches files that end in.css
                use: ExtractTextPlugin.extract({
                    fallback: 'style-loader'.use: ['css-loader'.'postcss-loader']})}, {test: /\.(png|jpg|svg|gif)$/.use: [{loader: 'url-loader'.options: {
                            limit: 1000  // Only images smaller than 1KB will be converted to base64. The example image is 1.47KB, so it will not be converted}}]},... ] },... }Copy the code

Then we run NPM run build, then we run NPM run dev, well, the image is not base64, but the image is not displayed.

The problem is the path. The path of our previous image is in.. “/images” folder, but when it is packaged, there is no such path. The image is directly at the same level as the file, so we need to create a folder for it in webpack.common.js:

// webpack.common.js. module.exports = { ... module: {rules: [{...test: /\.(png|jpg|svg|gif)$/.use: [{loader: 'url-loader'.options: {
                            limit: 1000.// Only images smaller than 1KB will be converted to base64. The example image is 1.47KB, so it will not be converted
                            outputPath: 'images'  // Set the name of the folder to store the image after packaging}}]},... ] },... }Copy the code

Continue the NPM run build package and run NPM run dev, oh my god! Pictures still don’t show! There is an images folder in the debugger, but my… /?

In css-loader, we also need to set a publicPath for the background image: ‘.. /’, as follows:

// webpack.common.js. module.exports = { ... module: {rules: [{test: /\.css$/.// Matches files that end in.css
                use: ExtractTextPlugin.extract({
                    fallback: 'style-loader'.use: ['css-loader'.'postcss-loader'].publicPath: '.. / '  // Set a public path for the background image})}, {test: /\.(png|jpg|svg|gif)$/.use: [{loader: 'url-loader'.options: {
                            limit: 1000.// Only images smaller than 1KB will be converted to base64. The example image is 1.47KB, so it will not be converted
                            outputPath: 'images'  // Set the name of the folder to store the image after packaging}}]},... ] },... }Copy the code

Now pack NPM run build and start NPM run dev, OK! No problem!

Isn’t it lively? By now we have also solved the problem of image separation without realizing it.

7.4 Compressed Code

In WebPack 4.x, js is automatically compressed when you pack, and when NPM run dev runs the server, hot updates are slow when you modify the code. This is because webPack automatically packages the code for you after you modify it, which causes slow efficiency in the development environment. So we need to separate the development environment from the production environment. This is where we can easily separate our code. Webpack.dev.js represents the configuration of the development environment, webpack.prod.js represents the configuration of the production environment. In this case, we simply configure the commands for the environment in package.json:

{... "scripts": { "build": "webpack --config webpack.prod.js --mode production", "dev": "webpack-dev-server --open --config webpack.dev.js --mode development" }, ... }}Copy the code

–mode production indicates that the package is a production environment and will compress JS, while –mode development indicates that the package is a development environment and does not need to compress JS. This also solves a long-standing warning problem:

conclusion

Ok, up to now we basically webapck commonly used functions have gone over, write a little long, thank you can carefully see here, I hope to help you, if there is a wrong place, please also give advice. In fact, Webpack has a lot of functions, here is not completely described, but I believe that you now have a certain understanding of Webpack, more Webpack exploration will not be difficult to you! If you like, pay attention to a wave of it, and there will be continuous updates…

Please go to Github for the complete code. If it is helpful, I hope you can give me a star.