It has been a long time since Webpack 5 was released, and the scaffolding used in the company is still VERSION V4. I heard that the focus of the V5 update is mainly on the optimization of configuration performance. I wanted to use Webpack 5 to build a scaffolding to see how it felt. Since I’m mainly developing with React, I used React as an example. For those who are not familiar with Webpack5, you can check out the Webpack5 hands-on test in this article. In fact, the configuration content of Webpack 4 does not change much. Mostly performance improvements, so without further ado, here we go.

Webpack 5 requires node.js to be at least 10.13.0 (LTS)

Project initialization

mkdir webpack5-demo
cd webpack5-demo
npm init -y
Copy the code

Install base dependency files

The packages that need to be installed at the beginning are posted directly and can be copied to package.json file to execute NPM I or YARN

package.json

"devDependencies": {
    "@babel/core": "^ 7.13.8"."@babel/preset-env": "^ 7.13.8"."@babel/preset-react": "^ 7.12.13"."babel-loader": "^ 8.2.2"."chalk": "^ 4.1.0." "."clean-webpack-plugin": "^ 3.0.0"."css-loader": "^ 5.1.0"."html-webpack-plugin": "^ 5.2.0." "."ip": "^ 1.1.5." "."style-loader": "^ 2.0.0." "."progress-bar-webpack-plugin": "^ 2.1.0." "."speed-measure-webpack-plugin": "^ 1.4.2." "."webpack": "^ 5.24.2"."webpack-cli": "^ 4.5.0." "."webpack-dev-server": "^ 3.11.2"
  },
  "dependencies": {
    "react": "^ 17.0.1"."react-dom": "^ 17.0.1"
  }
Copy the code

In this case, the file directory

├─ ├─ package-lock.json └Copy the code

Configuration webpack

In order to distinguish between development and production environments for easy maintenance and special handling, we don’t configure everything in webpack.config.js.

Create a new config directory for webPack configuration files
mkdir config
cd config
touch webpack.common.js The development environment and production environment common configurations are stored in this file
touch webpack.dev.js Configurations that require special handling for the development environment are stored here
touch webpack.prod.js Configurations that require special processing for the production environment are stored here
Copy the code

When configuring Webpack, it is necessary to deal with file paths to avoid path clutter. Create a separate configuration file for path invocation, also referring to CRA

touch paths.js
Copy the code

paths.js

const path = require("path");
const fs = require("fs");
// Get the current working directory
const appDirectory = fs.realpathSync(process.cwd());
// Resolve absolute paths from relative paths
const resolveApp = (relativePath) = > path.resolve(appDirectory, relativePath);
// The default module extension
const moduleFileExtensions = ["js"."jsx"."ts"."tsx"."json"];
// Resolve the module path
const resolveModule = (resolveFn, filePath) = > {
    // Check whether the file exists
    const extension = moduleFileExtensions.find((extension) = >
        fs.existsSync(resolveFn(`${filePath}.${extension}`)));if (extension) {
        return resolveFn(`${filePath}.${extension}`);
    }
    return resolveFn(`${filePath}.js`); // If there is no default js
};

module.exports = {
    appBuild: resolveApp("build"), // Package the path
    appPublic: resolveApp("public"), // Static resource path
    appHtml: resolveApp("public/index.html"), // HTML template path
    appIndexJs: resolveModule(resolveApp, "src/index"), // Package the entry path
    appNodeModules: resolveApp("node_modules"), / / node_modules path
    appSrc: resolveApp("src"), // Main file entry path
    moduleFileExtensions, // Module extension
};
Copy the code

In this case, the file directory

The demo ├ ─ config ├ ─ paths. Js │ ├ ─ webpack.com mon. Js │ ├ ─ webpack. Dev. Js │ └ ─ webpack. Prod. Js ├ ─ node_modules ├ ─ Package - lock. Json └ ─ package. The jsonCopy the code

webpack.common.js

const paths = require("./paths");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = function (options) {
    return {
        mode: options.mode,
        entry: paths.appSrc,
        output: {
            path: paths.appBuild,
            publicPath: "/",},cache: {
            // Use persistent caching
            type: "filesystem".//memory: use content cache filesystem: Use file cache
        },
        devtool: false.module: {
            rules: [{test: /\.js$/,
                    exclude: /node_modules/,
                    use: [
                        {
                            loader: "babel-loader".options: {
                                presets: [
                                    "@babel/preset-env"."@babel/preset-react",],},},],},devServer: {},
        plugins: [
            new HtmlWebpackPlugin({
                template: "./public/index.html",}),... options.plugins, ],stats: options.stats, // Package log error and new compile-time output
    };
};
Copy the code

Entry is the entry, the default path to find is what we wrote, and here we need to provide an entry file

Return to the webpack5-demo root directory
Create a SRC directory development file

mkdir src
touch index.js Create an entry file
Copy the code
import React from "react";
import ReactDOM from "react-dom";

const App = () = > {
    return <div>App entrance</div>;
};

ReactDOM.render(<App />.document.querySelector("#root"));
Copy the code

At the time of packaging, the HtmlWebpackPlugin was used, so you had to create an HTML template to provide enough of the plugin to use

Return to the webpack5-demo root directory
Create a public directory for static resources

mkdir public
touch index.html Create an HTML template
Copy the code

Index.html template

<! DOCTYPE html><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>Webpack-React-Cli</title>
    </head>
    <body>
        <div id="root"></div>
    </body>
</html>
Copy the code

Webpack.dev.js development configuration

module.exports = require("./webpack.common") ({mode: "development".plugins: [].stats: "errors-only".// Output only when an error occurs or a new compile is available
});
Copy the code

Webpack.prod.js production configuration

const { CleanWebpackPlugin } = require("clean-webpack-plugin"); // Clear the build directory file before packing
const ProgressBarPlugin = require("progress-bar-webpack-plugin"); // Package the progress bar to beautify
const chalk = require("chalk");

module.exports = require("./webpack.common") ({mode: "production".devtool: "source-map".plugins: [
        new CleanWebpackPlugin(),
        new ProgressBarPlugin({
            format:
                `${chalk.green.bold("build[:bar]")} ` +
                chalk.green.bold(":percent") +
                " (:elapsed seconds)".clear: false.width: 60,})],stats: "normal".// Standard output
});
Copy the code

In this case, the file directory

Webpack5 - demo ├ ─ config │ ├ ─ paths. Js │ ├ ─ webpack.com mon. Js │ ├ ─ webpack. Dev. Js │ └ ─ webpack. Prod. Js ├ ─ node_modules ├ ─ Public │ ├─ ├─ ├─ download.txt └─ ├─ download.txtCopy the code

Start the service

Now our Webpack basic configuration is OK, just need to start

Configure the pageage.json script

{
  "name": "webpack5-demo"."version": "1.0.0"."description": Webpack5-based React frame."main": "index.js"."scripts": {
    "build": "webpack --config config/webpack.prod.js"."start": "webpack serve --config config/webpack.dev.js".// v5
    "dev": "webpack-dev-server --config config/webpack.dev.js" //v4
  },
  "keywords": []."author": "Jason"."license": "ISC". }Copy the code

Here’s a little change:

Start service with Webpack-dev-server in Webpack 4 and start service with Webpack serve in Webpack 5

Now it’s started successfully, but I don’t like the startup log very much, SO I have to change it and reconfigure the development server (webpack-dev-server).

Configuring the development Server

While it is recommended to run Webpack-dev-server through the CLI, we can also choose to start the server through the API.

Return to the webpack5-demo root directory
Create a server directory

mkdir server
touch index.js # Service Entrance
touch appConfig.js Configure a custom service port, IP address, and proxy address
touch logger.js Log output from the console
Copy the code

appConfig.js

module.exports = {
    deployUrl: "127.0.0.0:8080".// The local code is pushed to the specified server
    proxyUrlMap: {
        "/api": "localtion:3000".// The interface of the proxy
        "/api2": "localtion:4000".// The interface of the proxy
    },
    port: 9000.// Port number,
    host: "localhost"./ / host number
};
Copy the code

logger.js

const ip = require("ip");

const divider = chalk.gray("\n-----------------------------------");

const logger = {
    error: (err) = > {
        console.error(chalk.red(err));
    },
    appStarted: (port, host, tunnelStarted) = > {
        console.log(`Server started ! ${chalk.green("✓")}`);

        if (tunnelStarted) {
            console.log(`Tunnel initialised ${chalk.green("✓")}`);
        }
        console.log(`
${chalk.bold("Access URLs:")}${divider}
Localhost: ${chalk.magenta(`http://${host}:${port}`)}
LAN: ${
            chalk.magenta(`http://${ip.address()}:${port}`) +
            (tunnelStarted
                ? `\n    Proxy: ${chalk.magenta(tunnelStarted)}`
                : "")}${divider}
${chalk.blue(`Press ${chalk.italic("CTRL-C")} to stop`)}
    `); }};module.exports = logger;
Copy the code

index.js

const Webpack = require("webpack");
const WebpackDevServer = require("webpack-dev-server");
// WebPack development configuration file
const webpackConfig = require(".. /config/webpack.dev");
// Customize log output
const logger = require("./logger");
// Service configuration
const appConfig = require("./appConfig");

const { port, host } = appConfig; // The port number to listen to
/ / to the compiler
const compiler = Webpack(webpackConfig);
/ / devServer parameters
const devServerOptions = Object.assign({}, webpackConfig.devServer, {
    // open: true, // automatically open the browser
    compress: true./ / gzip compression
    stats: "minimal"});const server = new WebpackDevServer(compiler, devServerOptions);

server.listen(port, host, async (err) => {
    if (err) {
        return logger.error(err.message);
    }
    logger.appStarted(port, "localhost");
});
Copy the code

Configure the startup command in package.json

"scripts": {
    "build": "webpack --config config/webpack.prod.js"."start": "node server"
  },
Copy the code

In this case, the file directory

Webpack5 - demo ├ ─ config │ ├ ─ paths. Js │ ├ ─ webpack.com mon. Js │ ├ ─ webpack. Dev. Js │ └ ─ webpack. Prod. Js ├ ─ node_modules ├ ─ Public │ └ ─ index. The HTML ├ ─ server │ ├ ─ appConfig. Js │ ├ ─ index. The js │ └ ─ logger. Js ├ ─ SRC │ └ ─ index. The js ├ ─ Package - lock. Json └ ─ package. The jsonCopy the code

Resolve parsing

Use the default values for resolve

  • Modules: Use the third module’s first response to look for node_modules in the root directory
  • Extensions:importDo not add file extension, will be traversed in turnextensionsAdd the extension to match
  • Alias: creates an alias inimportrequireTo make module introduction easier
 resolve: {
    modules: [paths.appNodeModules],
    extensions: ['.js'.'.jsx'.'.css'].alias: {
        moment$: 'moment/moment.js'.'@src': paths.appSrc,
        '@public': paths.appPublic,
    },
},
Copy the code

Basic Loader Configuration

CSS and sass

The installation

npm i -D style-loader css-loader
npm i -D node-sass sass-loader postcss postcss-loader postcss-preset-env
Copy the code

The loader configuration of CSS and SASS is very simple. For compatibility, postCSS-Loader also needs to add the manufacturer identifier header of the browser

The plug-ins used are stored separately in the postcss.config.js configuration file in the root directory

postcss.config.js

module.exports = {
    plugins: {
        "postcss-preset-env": {},}};Copy the code

The module resources

Webpack 4 uses file-loader or url-loader to handle image or text classes. Now for Webpack 5, you can use Asset Modules. There is no need to configure loader

  • Asset /source Exports the source code of the resource (equivalent to raw-loader)
  • Asset/Resource sends a single file and exports the URL (equivalent to file-loader)
  • Asset /inline Exports a resource’s data URI (equivalent to url-loader)
  • Asset automatically selects between exporting a data URI and sending a separate file, previously by using urL-loader and configuring resource volume limits
// Set constants
const cssRegex = /\.css$/;
const cssModuleRegex = /\.module\.css$/;
const sassRegex = /\.(scss|sass)$/;
const sassModuleRegex = /\.module\.(scss|sass)$/;
const imageInlineSizeLimit = 4 * 1024;

module.exports = function (options) {

    return{...module: {
            rules: [{oneOf: [{...test: cssRegex,
                            exclude: cssModuleRegex,
                            use: ['style-loader', {
                                loader: 'css-loader'.options: {
                                    importLoaders: 1 // 0 => No loader(default). 1 => postcss-loader; 2 => postcss-loader, sass-loader}},'postcss-loader'],}, {test: sassRegex,
                            exclude: sassModuleRegex,
                            use: ['style-loader', {
                                loader: 'css-loader'.options: {
                                    importLoaders: 1 // Query the importLoaders parameter, which configures the number of loaders for csS-loaders before @import resources}},'postcss-loader'.'sass-loader'],}, {test: [/\.bmp$/./\.gif$/./\.jpe?g$/./\.png$/].type: 'asset'.parser: {
                                dataUrlCondition: {
                                    maxSize: imageInlineSizeLimit // 4kb}}}, {test: /\.(eot|svg|ttf|woff|woff2?) $/,
                            type: 'asset/resource'},]},}Copy the code

Test your CSS and images

# create a style. SCSS file in SRC

touch style.scss
Copy the code

Let’s start with the dot

* {
    margin: 0;
    padding: 0;
}

div {
    color: red;
}
Copy the code

Introduce styles and images

import React from "react";
import ReactDOM from "react-dom";

import npm from "@public/assets/imgs/npm.png";

import "./style.scss";

const App = () = > {
    return (
        <div>App entrance<img src={npm} />
        </div>
    );
};

ReactDOM.render(<App />.document.querySelector("#root"));
Copy the code

Look at the packing

Because persistent caching is set up, the second time is fast

In this case, the file directory

Webpack5 - demo ├ ─ build ├ ─ config │ ├ ─ paths. Js │ ├ ─ webpack.com mon. Js │ ├ ─ webpack. Dev. Js │ └ ─ webpack. Prod. Js ├ ─ Node_modules ├ ─ public │ ├ ─ assets │ │ └ ─ imgs │ │ └ ─ NPM. PNG │ └ ─ index. The HTML ├ ─ server │ ├ ─ appConfig. Js │ ├ ─ index, js │ ├─ ├─ ├─ ├─ ├─ ├─ ├─ ├─ download.jsonCopy the code

summary

Content or more plans to be divided into two to write, the first or write more simple. The second article is more optimization, for the new features of WebPack 5 optimization, code specification configuration of ESLint, the use of some plug-ins, also want to try to write an article, let’s see what the first draft is like, more than 50 likes to write the next article (ha ha ha)