preface

Recently, I was doing a test exercise about webpack5 manual construction project. I found many articles on the Internet, and found that many articles would have all kinds of errors according to their steps. Therefore, I referred to all kinds of articles on the Internet and completed this article with my own actual construction. If there are any mistakes, please point them out in the comments section.

Time 2021/09/28

Initializing the Environment

Generate package. Json

yarn init -y
Copy the code

Install WebPack and webpack-clj

yarn add -D webpack webpack-cli

Webpack – CLI is webpack’s command-line tool for using Webpack on the command line.

Configuring React Support

Download React and react-dom

 yarn add react react-dom
Copy the code

Let’s start our project with react and JSX support.

Supporting JSX requires additional configuration of Babel to handle JSX files and translate JSX into JS that browsers can recognize.

Here we need to use the following libraries:

  • babel-loader
  • @babel/core
  • @babel/preset-env
  • @babel/plugin-transform-runtime
  • @babel/preset-react

Let’s tease out a little bit about what these babels do

babel-loader

First of all, for the JSX files in our project, we need to use a “translator” to convert the JSX files in the project into JS files. Babel-loader acts as this translator here.

@babel/core

Babel-loader only recognizes JSX files. The @babel/core core library is required for internal core translation. The @babel/core module is responsible for internal core translation.

@babel/preset-env

@babel/prest-env are presets in Babel translation that take care of basic ES 6+ syntax, such as const/let… Translation becomes a low-level compatibility syntax that browsers can recognize.

It is important to note that @babel/prest-ent does not polyfill some es6+ higher version syntactic implementations, such as Promise. You can think of it as syntactic transformations that do not include the implementation of high level modules (polyfills).

@babel/plugin-transform-runtime

Babel /plugin-transform-runtime (preset) @babel/ preth-runtime (preset) does not turn for higher versions of the built-in modules, such as Promise/Generate, etc. So @babel/ plugin-transform-Runtime helps us to do this by implementing a polyfill for a lower version browser after using a Promise module in our project.

In fact, @babel/polyfill can be installed directly to achieve the same effect as @babel/plugin-transform-runtime, but this method is not recommended, because it has the disadvantages of polluting the global scope, full introduction causes too much mention and repeated injection between modules.

At this point, we can compile es6+ code into compatible JS code that browsers can recognize at lower versions, but we are missing the most important point.

@babel/preset-react

These plug-ins work with JS files, and we need to be able to recognize and work with JSX files. This brings us to our all-important @babel/preset- React.

@babel/preset- React is a set of presets that have a set of Babel plugins built in to transform JSX code into the JS code we want.

Plug-in installation

Let’s install these five plug-ins

yarn add -D @babel/core @babel/preset-env babel-loader @babel/plugin-transform-runtime @babel/preset-react

webpackTo configure:

Create a new webpack.config.js root directory. The current directory structure is as follows:

webpack.config.js

const path = require("path");
module.exports = {
  entry: "./src/index.js".output: {
    path: __dirname + "/dist".filename: "index_bundle.js",},module: {
    rules: [{test: /\.jsx? $/,
        use: "babel-loader",},],},};Copy the code

New root directory. Babelrc

{
    "presets": [
      "@babel/preset-env"."@babel/preset-react"]."plugins": [["@babel/plugin-transform-runtime",
        {
          "regenerator": true}}]]Copy the code

Create a SRC folder in the root directory and create index.js

import React from "react";
import ReactDOM from "react-dom";
function App() {
  return <div>hello webpack5!</div>;
}
ReactDOM.render(<App />.document.getElementById("root"));
Copy the code

Create a public folder in the root directory and create index.html inside it

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

configurationhtmlpage

All we’re dealing with right now is configuration for single-page applications, and we desperately need an HTML presentation page.

This brings us to the html-webpack-plugin,

The html-webpack-plugin creates an HTML file and inserts the webpack-packed static file into the HTML file when using webpack.

yarn add --dev html-webpack-plugin
Copy the code

We use this file as the template file for the plugin, as well as the reactdom.reander (… , document.getelementById (‘root’)), a div with id=root has been created as the render node in the page.

Use the plugin in webpack.config.js

const path = require("path");
const htmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
  entry: "./src/index.js".output: {
    path: __dirname + "/dist".filename: "index_bundle.js",},module: {
    rules: [{test: /\.jsx? $/,
        use: "babel-loader",}]},plugins: [
    new htmlWebpackPlugin({
      filename: "index.html".template: path.resolve(__dirname, "./public/index.html"),})]};Copy the code

webpack-dev-server

The above is enough to build a SPA single-page application, but we can’t preview the code every time we change it by executing a package command.

This would be too much trouble, but don’t worry webPack provides us with a devServer configuration that allows us to hot-reload our code every time we update it.

yarn add -D webpack-dev-server
Copy the code

Then configure webpack.config.js

const path = require("path");
const htmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
  mode: "development".entry: "./src/index.js".output: {
    path: __dirname + "/dist".filename: "index_bundle.js",},module: {
    rules: [{test: /\.jsx? $/,
        use: "babel-loader",}]},plugins: [
    new htmlWebpackPlugin({
      filename: "index.html".template: path.resolve(__dirname, "./public/index.html"),})],devServer: {
   // When using [HTML5 History API], arbitrary '404' responses are replaced with 'index.html'
    historyApiFallback: true.open: true.// Automatically open the browser
    // Defaults to true
    hot: true.// Whether to enable code compression
    compress: true.// Start port
    port: 9000,}};Copy the code

Add it to the scripts of package.json

 "scripts": {
    "start": "webpack serve"   // Start the project
    "build":"webpack"         // Perform webpack packaging
  },
Copy the code

The console then executes YARN Start and you can see that a very simple React project has been set up

clean-webpack-plugin

Clean-webpack-plugin is a plug-in that cleans files. Each time the disk space is packed, the packed resources are stored in the disk space. When the disk space is packed again, you need to clear the local packed resources first to reduce the disk space occupied by them. The clean-webpack-plugin does this for us.

The installation

yarn add -D clean-webpack-plugin 
Copy the code

Modify the webpack. Config. Js

const path = require("path");
const htmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
module.exports = {
  mode: "development".entry: "./src/index.js".output: {
    path: __dirname + "/dist".filename: "index_bundle.js",},module: {
    rules: [{test: /\.jsx? $/,
        use: "babel-loader",}]},plugins: [
    new htmlWebpackPlugin({
      filename: "index.html".template: path.resolve(__dirname, "./public/index.html"),}),new CleanWebpackPlugin(),
  ],
  devServer: {
    // When using [HTML5 History API], arbitrary '404' responses are replaced with 'index.html'
    historyApiFallback: true.open: true.// Automatically open the page
    // Defaults to true
    hot: true.// Whether to enable code compression
    compress: true.// Start port
    port: 9000,}};Copy the code

The file name hash

Hash the packaged file name

In webpack.config.js, filename is followed by a placeholder

output: {
    path: __dirname + "/dist".// [contenthash:8] - This application packages output file level updates, resulting in output file name changes
    filename: "[name]-[contenthash:8].js",},Copy the code

Configuring a Path Alias

Add it to webpack.config.js

 resolve: {
    alias: {
      "@": path.resolve(__dirname, "./src"),},mainFiles: ["index"."main"],},Copy the code

At this point, the full configuration of webpack.config.js is:

const path = require("path");
const htmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
module.exports = {
  mode: "development".entry: "./src/index.js".output: {
    path: __dirname + "/dist".// [contenthash:8] - This application packages output file level updates, resulting in output file name changes
    filename: "[name]-[contenthash:8].js",},resolve: {
    alias: {
      "@": path.resolve(__dirname, "./src"),},mainFiles: ["index"."main"],},module: {
    rules: [{test: /\.jsx? $/,
        use: "babel-loader",}]},plugins: [
    new htmlWebpackPlugin({
      filename: "index.html".template: path.resolve(__dirname, "./public/index.html"),}),new CleanWebpackPlugin(),
  ],
  devServer: {
    // When using [HTML5 History API], arbitrary '404' responses are replaced with 'index.html'
    historyApiFallback: true.open: true.// Automatically open the page
    // Defaults to true
    hot: true.// Whether to enable code compression
    compress: true.// Start port
    port: 9000,}};Copy the code

Configure images and fonts

New rule in webpack.cofig. Js module

The Assets module comes with Webpackage 5 and does not need to be downloaded

    {
        test: /\.(png|jpe? g|svg|gif)$/,
        type: "asset/inline"}, {test: /\.(eot|ttf|woff|woff2)$/,
        type: "asset/resource".generator: {
          filename: "fonts/[hash][ext][query]",}},Copy the code

Js compressed

Webpack 5 comes with the latest Terser-webpack-plugin, which does not require manual installation.

The terser-webpack-plugin enables parallel: true by default, and the default number of parallel runs is os.cpus().length-1, and the number of parallel runs in this article is 4.

Webpack.prod. js can be configured as follows:

const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
    optimization: {
        minimizer: [
            new TerserPlugin({
              parallel: 4.terserOptions: {
                parse: {
                  ecma: 8,},compress: {
                  ecma: 5.warnings: false.comparisons: false.inline: 2,},mangle: {
                  safari10: true,},output: {
                  ecma: 5.comments: false.ascii_only: true,},},}),]}}Copy the code

The complete configuration of webpack.config.js is as follows:

const path = require("path");
const htmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");
module.exports = {
  mode: "development".entry: "./src/index.js".output: {
    path: __dirname + "/dist".// [contenthash:8] - This application packages output file level updates, resulting in output file name changes
    filename: "[name]-[contenthash:8].js",},resolve: {
    alias: {
      "@": path.resolve(__dirname, "./src"),},mainFiles: ["index"."main"],},module: {
    rules: [{test: /\.jsx? $/,
        use: "babel-loader"}, {test: /\.(png|jpe? g|svg|gif)$/,
        type: "asset/inline"}, {test: /\.(eot|ttf|woff|woff2)$/,
        type: "asset/resource".generator: {
          filename: "fonts/[hash][ext][query]",},},],},plugins: [
    new htmlWebpackPlugin({
      filename: "index.html".template: path.resolve(__dirname, "./public/index.html"),}),new CleanWebpackPlugin(),
  ],
  optimization: {
    minimizer: [
      new TerserPlugin({
        parallel: 4.terserOptions: {
          parse: {
            ecma: 8,},compress: {
            ecma: 5.warnings: false.comparisons: false.inline: 2,},mangle: {
            safari10: true,},output: {
            ecma: 5.comments: false.ascii_only: true},},}),],},devServer: {
    // When using [HTML5 History API], arbitrary '404' responses are replaced with 'index.html'
    historyApiFallback: true.open: true.// Automatically open the page
    // Defaults to true
    hot: true.// Whether to enable code compression
    compress: true.// Start port
    port: 9000,}};Copy the code

The configuration of CSS

Next, let’s add some styles to the project to beautify it.

The React project has a lot of ARGUMENTS about CSS, but we’re going to deal with CSS in webpack anyway.

The loader used here is as follows:

  • postcss-loader
  • css-loader
  • MiniCssExtractPlugin.loader

Let’s analyze the corresponding configuration of the functions of these loaders one by one:

postcss-loader

What is PostCSS? You might think of it as a preprocessor, or a post-processor, or whatever. Well, it’s nothing. It can be understood as a plug-in system.

Postcss, which I’m not going to go into here, is Babel and it’s two behemoths. It has its own independent architecture, and you need to be aware that we use postCSS-loader to handle the generated CSS.

Step 1: Install the post-CSS:

yarn add -D postcss-loader postcss
Copy the code

Postcss-loader supports both direct configuration of rules in the loader and configuration of a separate file. Here we choose to use a separate file for configuration:

We will create a new postcss.config.js file in the project root directory:

module.exports = {
  plugins: [
    require('autoprefixer'),
    require('cssnano') ({preset: 'default',})],}Copy the code

Here we use two postCSS plugins:

  • autoprefixerPlugins are for uscssContent added browser vendor prefix compatible.
  • cssnanoThe role of is to compress us as little as possiblecssThe code.

Next we will install the two plug-ins:

yarn add -D cssnano autoprefixer@latest
Copy the code

css-loader

Css-loader is used to parse @import/require statements in our CSS files.

yarn add -D css-loader
Copy the code

MiniCssExtractPlugin.loader

This plug-in extracts CSS into a separate file. It creates a CSS file for each JS file that contains CSS. It supports loading CSS and SourceMaps on demand.

Here we need to mention the difference between it and style-loader. Here we use MiniCssExtractPlugin instead of style-loader.

Style-loader adds the generated CSS to the HEADER tag of the HTML to create a restrained style, which is obviously not what we want. So here we use MiniCssExtractPlugin. The role of the loader is split the generated CSS became independent CSS file.

yarn add -D mini-css-extract-plugin

generatecssFinal configuration file

Next we generate the final configuration file for the CSS file:

const path = require("path");
const htmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
  mode: "development".entry: "./src/index.js".output: {
    path: __dirname + "/dist".// [contenthash:8] - This application packages output file level updates, resulting in output file name changes
    filename: "[name]-[contenthash:8].js",},resolve: {
    alias: {
      "@": path.resolve(__dirname, "./src"),},mainFiles: ["index"."main"],},module: {
    rules: [{test: /\.jsx? $/,
        use: "babel-loader"}, {test: /\.css$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
          },
          "css-loader"."postcss-loader",]}, {test: /\.(png|jpe? g|svg|gif)$/,
        type: "asset/inline"}, {test: /\.(eot|ttf|woff|woff2)$/,
        type: "asset/resource".generator: {
          filename: "fonts/[hash][ext][query]",},},],},plugins: [
    new htmlWebpackPlugin({
      filename: "index.html".template: path.resolve(__dirname, "./public/index.html"),}),new CleanWebpackPlugin(),
    new MiniCssExtractPlugin({
      filename: "assets/[name].css",})],optimization: {
    minimizer: [
      new TerserPlugin({
        parallel: 4.terserOptions: {
          parse: {
            ecma: 8,},compress: {
            ecma: 5.warnings: false.comparisons: false.inline: 2,},mangle: {
            safari10: true,},output: {
            ecma: 5.comments: false.ascii_only: true},},}),],},devServer: {
   // When using [HTML5 History API], arbitrary '404' responses are replaced with 'index.html'
    historyApiFallback: true.open: true.// Automatically open the page
    // Defaults to true
    hot: true.// Whether to enable code compression
    compress: true.// Start port
    port: 9000,}};Copy the code

Configuration is less

yarn add less less-loader --dev
Copy the code

Note 1: The order of style-loader, CSs-loader, and less-loader cannot be changed, because webpack is converted from back to front, that is,.less is converted to.css, and then.css is added to style.

Note 2: When we introduce antD. less, we need to start javascriptEnabled=true for less-loader

{
   loader: MiniCssExtractPlugin.loader,
  },
  "css-loader"."postcss-loader".// When parsing antd.less, the following format must be used otherwise Inline JavaScript is not enabled will be reported
  { loader: "less-loader".options: { lessOptions: { javascriptEnabled: true}}},],},Copy the code

The current configuration of webpack.config.js is as follows

const path = require("path");
const htmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
  mode: "development".entry: "./src/index.js".output: {
    path: __dirname + "/dist".// [contenthash:8] - This application packages output file level updates, resulting in output file name changes
    filename: "[name]-[contenthash:8].js",},resolve: {
    alias: {
      "@": path.resolve(__dirname, "./src"),},mainFiles: ["index"."main"],},module: {
    rules: [{test: /\.jsx? $/,
        use: "babel-loader"}, {test: /\.(sa|sc|le|c)ss$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
          },
          "css-loader"."postcss-loader".// When parsing antd.less, the following format must be used otherwise Inline JavaScript is not enabled will be reported
          { loader: "less-loader".options: { lessOptions: { javascriptEnabled: true}}},],}, {test: /\.(png|jpe? g|svg|gif)$/,
        type: "asset/inline"}, {test: /\.(eot|ttf|woff|woff2)$/,
        type: "asset/resource".generator: {
          filename: "fonts/[hash][ext][query]",},},],},plugins: [
    new htmlWebpackPlugin({
      filename: "index.html".template: path.resolve(__dirname, "./public/index.html"),}),new CleanWebpackPlugin(),
    new MiniCssExtractPlugin({
      filename: "assets/[name].css",})],optimization: {
    minimizer: [
      new TerserPlugin({
        parallel: 4.terserOptions: {
          parse: {
            ecma: 8,},compress: {
            ecma: 5.warnings: false.comparisons: false.inline: 2,},mangle: {
            safari10: true,},output: {
            ecma: 5.comments: false.ascii_only: true},},}),],},devServer: {
    // When using [HTML5 History API], arbitrary '404' responses are replaced with 'index.html'
    historyApiFallback: true.open: true.// Automatically open the page
    // Defaults to true
    hot: true.// Whether to enable code compression
    compress: true.// Start port
    port: 9000,}};Copy the code

CSS compression

CssMinimizerWebpackPlugin compression using CSS file.

The csS-minimizer-webpack-plugin is much more accurate with query strings in source maps and assets than the optimize- CSS-assets-webpack-plugin. It also supports running in cache and concurrent mode.

CssMinimizerWebpackPlugin will search the CSS file during Webpack building, optimization, the compression of CSS.

Installation:

yarn add  -D css-minimizer-webpack-plugin
Copy the code

Webpack.prod. js can be configured as follows:

const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");

module.exports = {
  optimization: {
    minimizer: [
      new CssMinimizerPlugin({
          parallel: 4,
        }),
    ],
  }
}
Copy the code

The complete webpack.config.js configuration is as follows

const path = require("path");
const htmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin"); / / js compressed
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); / / CSS
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); / / CSS compression
module.exports = {
  mode: "development".entry: "./src/index.js".output: {
    path: __dirname + "/dist".// [contenthash:8] - This application packages output file level updates, resulting in output file name changes
    filename: "[name]-[contenthash:8].js",},resolve: {
    alias: {
      "@": path.resolve(__dirname, "./src"),},mainFiles: ["index"."main"],},module: {
    rules: [{test: /\.jsx? $/,
        use: "babel-loader"}, {test: /\.(sa|sc|le|c)ss$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
          },
          "css-loader"."postcss-loader".// When parsing antd.less, the following format must be used otherwise Inline JavaScript is not enabled will be reported
          { loader: "less-loader".options: { lessOptions: { javascriptEnabled: true}}}, {loader: "resolve-url-loader".options: {
              keepQuery: true,}}, {loader: "sass-loader".options: {
              sourceMap: true,},},],}, {test: /\.(png|jpe? g|svg|gif)$/,
        type: "asset/inline"}, {test: /\.(eot|ttf|woff|woff2)$/,
        type: "asset/resource".generator: {
          filename: "fonts/[hash][ext][query]",},},],},plugins: [
    new htmlWebpackPlugin({
      filename: "index.html".template: path.resolve(__dirname, "./public/index.html"),}),new CleanWebpackPlugin(),
    new MiniCssExtractPlugin({
      filename: "assets/[name].css",})],optimization: {
    minimizer: [
      new TerserPlugin({
        parallel: 4.terserOptions: {
          parse: {
            ecma: 8,},compress: {
            ecma: 5.warnings: false.comparisons: false.inline: 2,},mangle: {
            safari10: true,},output: {
            ecma: 5.comments: false.ascii_only: true,}}}),new CssMinimizerPlugin({
        parallel: 4,})]},devServer: {
    // When using [HTML5 History API], arbitrary '404' responses are replaced with 'index.html'
    historyApiFallback: true.open: true.// Automatically open the page
    // Defaults to true
    hot: true.// Whether to enable code compression
    compress: true.// Start port
    port: 9000,}};Copy the code

Compile progress bar

Generally speaking, the first compilation time of medium-sized projects is 5-20s, and each progress bar is much more urgent. We can check the compilation progress through progress-bar-webpack-plugin, which is convenient for us to grasp the compilation status.

Installation:

yarn add progress-bar-webpack-plugin --dev
Copy the code

Webpack.common. js can be configured as follows:

const chalk = require('chalk') const ProgressBarPlugin = require('progress-bar-webpack-plugin') module.exports = { Plugins: [// new ProgressBarPlugin({format: ` : MSG [: bar] ${chalk. Green. Bold (' : percent ')} (: elapsed s) `})],} copy codeCopy the code

Nice bold and green highlights for progress percentage.

Contains content, progress bar, progress percentage and consumed time. The progress bar has the following effects:

Eslint & prettier

prettier

yarn add --dev --exact prettier
Copy the code

After the installation, we created the.prettierrc.js configuration file in the root directory of the project

Let’s add some basic configuration

Module. exports = {printWidth: 0, // tabWidth: 2, // tabWidth: 2, // tabWidth: 1, // tabWidth: 2, // tabWidth: 2, // tabWidth: 2, // tabWidth: 2, // tabWidth: 2, // JsxSingleQuote: true, // trailingComma: 'es5', // arrowParens: 'get', / / arrow function only when necessary to use () htmlWhitespaceSensitivity: 'CSS', / / HTML Spaces sensitivity}Copy the code

Prettierignore Lets Prettier ignore to check some files:

//.prettierignore 
**/*.min.js
**/*.min.css

.idea/
node_modules/
dist/
build/
Copy the code

ESlint

yarn add eslint --dev
Copy the code

Initialize eslint

NPX esLint --init Copies codeCopy the code

Eslint returns a list of interactive prompts to us to select the configuration we need

When Prettier and ESLint work together, they can conflict. We need to install the eslint-config-prettier plugin and override some of the ESLint rules.

yarn add -D eslint-config-prettier
Copy the code

After installation, we changed the ESLint configuration file a little so that when there was a conflict, prettier was used in preference to the rule where Prettier was used:

// .eslint.js module.exports = { env: { browser: true, es2021: true, }, extends: [' esLint :recommended', 'plugin: recommended', // Adding 'prettier' expands the rule 'esLint' where 'prettier' conflicts with 'prettier',], parserOptions: { ecmaFeatures: { jsx: true, }, ecmaVersion: 12, sourceType: 'module', }, plugins: ['react'], rules: {},}Copy the code

In the meantime, let’s add.eslintignore ignores some of the ts directory files that we don’t need to check for esLint, such as some of the script files built.

.eslintrc.js
node_modules
public
Copy the code

lint-staged

It would be inefficient to run Lint through your project. It is better to just have files in staging do code validation. This will save a lot of time, and the tool you’ll need is Lint-staged

yarn add lint-staged --dev
Copy the code

Add the lint-lanterns.config.js configuration file:

module.exports = {
  "src/**/*.{js,ts,vue}": [
    "eslint --fix --ext .js,.ts,.vue",
    "prettier --write",
  ],
};
Copy the code

This way, NPX Lint-staged execution on the command line allows you to manually run ESLint +prettier in staging for code style verification

husky

In order to ensure that the code entering git repository is consistent and consistent, we also need to force a code verification before committing. Using Husky, we can automatically run the code verification command at commit time

yarn add husky --dev
Copy the code

We are installing Husky version 6.0 and above, and we will first add a script to package.json according to the documentation:

{
  "prepare": "husky install"
}
Copy the code

Run the Husky installation script:

yarn prepare
Copy the code

Running this command generates a.husky folder in the project root directory

Add a Git hook:

NPX husky add. husky/pre-commit "NPX Lint-staged" copied codeCopy the code

After executing this command, a pre-commit file was automatically generated in the.husky directory, but my tests on the company Windows machine didn’t work, so we added the file manually

Create a new pre-commit file under the.husky directory and write:

#! /bin/sh . "$(dirname "$0")/_/husky.sh" npx lint-stagedCopy the code

Now every time you commit, Lint-staged rules will be automatically validated. If Lint fails, the current COMMIT will be aborted until you have fixed all errors

The version information of each package is as follows

All of the above has been measured. If there are any errors, they may be caused by version iteration. All versions of dependent packages are posted below

  "devDependencies": {
    "@babel/core": "^ 7.15.5"."@babel/plugin-transform-runtime": "^ 7.15.0"."@babel/preset-env": "^ 7.15.6"."@babel/preset-react": "^ 7.14.5"."autoprefixer": "^ 10.3.6." "."babel-loader": "^ 8.2.2"."clean-webpack-plugin": "^ 4.0.0"."css-loader": "^ 6.3.0"."css-minimizer-webpack-plugin": "^ 3.0.2." "."cssnano": "^ 5.0.8"."eslint": "^ 7.32.0"."eslint-config-prettier": "^ 8.3.0"."eslint-plugin-react": "^ 7.26.0"."gh-pages": "^ 3.2.3"."html-webpack-plugin": "^ 5.3.2." "."husky": "^ 7.0.0." "."less": "^ 4.4.1"."less-loader": "^ 10.0.1." "."lint-staged": "^ 11.1.2." "."mini-css-extract-plugin": "^ 2.3.0." "."node-sass": "^ the 6.0.1." "."postcss": "^ 8.3.8"."postcss-loader": "^ 6.1.1." "."prettier": "Against 2.4.1."."progress-bar-webpack-plugin": "^ 2.1.0." "."resolve-url-loader": "^ 4.0.0"."sass": "^ 1.42.1"."sass-loader": "^ 12.1.0"."webpack": "^ 5.54.0"."webpack-cli": "^ 4.8.0"."webpack-dev-server": "^ 4.3.0"
  },
  "dependencies": {
    "husky": "^ 7.0.2"."react": "^ 17.0.2"."react-dom": "^ 17.0.2"
  }
Copy the code

Webpack environment split

There are huge differences in build goals between development and production environments. For code clarity and simplicity, write separate WebPack configurations for each environment

Create a new Config folder in the root directory, and then create three WebPack configuration folders

webpack-merge

Merge generic and environment-specific configurations using Webpack-Marge.

Install webpack – merge:

yarn add webpack-merge --dev
Copy the code

Then split the webpack.config.js into three configuration files in the config folder according to the contents we have now configured.

webpack.common.js

const path = require('path')
const htmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin') / / CSS
const chalk = require('chalk')
const ProgressBarPlugin = require('progress-bar-webpack-plugin') // Compile the progress bar
module.exports = {
  resolve: {
    // Configure the path alias
    alias: {
      The '@': path.resolve(__dirname, './src'),},mainFiles: ['index'.'main'],},module: {
    rules: [{test: /\.jsx? $/,
        use: 'babel-loader'}, {test: /\.(le|c)ss$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
          },
          'css-loader'.'postcss-loader'.// When parsing antd.less, the following format must be used otherwise Inline JavaScript is not enabled will be reported
          { loader: 'less-loader'.options: { lessOptions: { javascriptEnabled: true}}},],}, {test: /\.(png|jpe? g|svg|gif)$/,
        type: 'asset/inline'}, {test: /\.(eot|ttf|woff|woff2)$/,
        type: 'asset/resource'.generator: {
          filename: 'fonts/[hash][ext][query]',},},],},plugins: [
    new htmlWebpackPlugin({
      filename: 'index.html'.template: path.resolve(__dirname, '.. /public/index.html'),}),new CleanWebpackPlugin(),
    new MiniCssExtractPlugin({
      filename: 'assets/[name].css',})./ / the progress bar
    new ProgressBarPlugin({
      format: `  :msg [:bar] ${chalk.green.bold(':percent')} (:elapsed s)`,})],}Copy the code

webpaack.dev.js

const { merge } = require('webpack-merge')
const common = require('./webpack.common')
module.exports = merge(common, {
  mode: 'development'.// Start source map
  devtool: 'eval-cheap-module-source-map'.cache: {
    type: 'filesystem'.// Use file caching
  },
  entry: './src/index.js'.devServer: {
    historyApiFallback: true.open: true.// Automatically open the page
    // Defaults to true
    hot: true.// Whether to enable code compression
    compress: true.// Start port
    port: 8888,}})Copy the code

webpack.prd.js

const TerserPlugin = require('terser-webpack-plugin') / / js compressed
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin') / / CSS compression
const { merge } = require('webpack-merge')
const common = require('./webpack.common')

module.exports = merge(common, {
  mode: 'production'.entry: './src/index.js'.output: {
    path: __dirname + '/.. /dist'.// [contenthash:8] - This application packages output file level updates, resulting in output file name changes
    filename: '[name]-[contenthash:8].js'.// Clear the directory before compiling
    clean: true,},//terser-webpack-plugin enables the parallel: true configuration by default, and the default number of concurrent runs: os.cpus().length -1,
  // Parallel is configured to 4, using multiple processes to run compression concurrently to improve build speed.
  optimization: {
    / / by configuration optimization. RuntimeChunk = true, to create an additional runtime code chunk, reduce entry the chunk size, improve the performance.
    // runtimeChunk: true,
    minimizer: [
      new TerserPlugin({
        parallel: 4.terserOptions: {
          parse: {
            ecma: 8,},compress: {
            ecma: 5.warnings: false.comparisons: false.inline: 2,},mangle: {
            safari10: true,},output: {
            ecma: 5.comments: false.ascii_only: true,}}}),new CssMinimizerPlugin({
        parallel: 4,}),],},})Copy the code

Finally, modify the webpack running and packaging commands in package.json

"scripts": {
    "start": "webpack serve --config config/webpack.dev.js"."build": "webpack --config config/webpack.prd.js",},Copy the code

The source code

github

The last

I spent two days summarizing this article with reference to various articles. If you find it helpful, please give it a thumbs up