preface

At present, the front-end system of the company is not very perfect. Generally, projects are written with a set of universal scaffolding built by create-React-app. The scaffolding is very generic and basic, but with tailwindcss and postcss, each compilation and packaging takes too long to be productive. Therefore, to avoid overtime, manual construction of a universal Webpack scaffolding is a current goal.

The body of the

Basic Project Configuration

Project initialization

mkdir webpack-demo-1
cd ./webpack-demo-1
yarn init -y
Copy the code

First you need to create the entry file for the project and the configuration file for Webpack. At this point the project directory is as follows:

Then, you need to install the WebPack dependency

yarn add --dev  webpack webpack-cli
Copy the code

The related versions of Webpack used here are as follows:

"Webpack" : "^ 5.44.0", "webpack - cli" : "^ 4.7.2." "Copy the code

Write content

Write something random in index.js:

class Hello { constructor() { console.log('hello webpack! ') } } const test = new Hello()Copy the code

Then, let’s run WebPack to see what it looks like

npx webpack
Copy the code

As you can see, after this run, a dist directory is added to the root directory, and a main.js file will be added to that directory

Among them:

new class{constructor(){console.log('hello webpack! ')}};Copy the code

Babel is used to convert ES6 to ES5 code. Let’s install some of Babel’s dependencies.

Configure the Babel

Install dependencies:

yarn add --dev babel-loader @babel/core @babel/preset-env @babel/plugin-transform-runtime  @babel/plugin-proposal-decorators  @babel/plugin-proposal-class-properties @babel/plugin-proposal-private-methods

yarn add @babel/runtime @babel/runtime-corejs3
Copy the code

Modify the webpack.config.js file:

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.[contenthash:8].js',
  },
  module: {
    rules: [
      {
        test: /\.(jsx|js)$/,
        use: 'babel-loader',
        exclude: /node_modules/,
      },
    ]
  }
}
Copy the code

Create the.babelrc file in the root directory:

{
  "presets": ["@babel/preset-env"],
  "plugins": [
    ["@babel/plugin-transform-runtime", {"corejs": 3}],
    ["@babel/plugin-proposal-decorators", { "legacy": true }],
    ["@babel/plugin-proposal-class-properties", { "loose": true }],
    ["@babel/plugin-proposal-private-methods", { "loose": true }]
  ]
}
Copy the code

Let’s run NPX webpack again to see the output. The project directory at this time:

Take a look at the contents of the bundle.xxxx.js file in the dist directory:

(()=>{"use strict"; new function n(){! function(n,o){if(! (n instanceof o))throw new TypeError("Cannot call a class as a function")}(this,n),console.log("hello webpack!" )}}) ();Copy the code

That should be all right. Let’s get the project running in the browser.

Run in a browser

We use directly the html-webpack-plugin, a well-known plug-in in the WebPack ecosystem, which allows our build artifacts to use the HTML files we specify as templates.

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

In the root directory, create a public folder, place an index. HTML file, and write basic HTML content to it

Use the html-webpack-plugin plugin in the wbepack.config.js file

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

module.exports = {
  // ...
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, './public/index.html'),
      inject: 'body',
      scriptLoading: 'blocking',
    }),
  ]
}
Copy the code

At this point, run NPX webpack to see the packaging results

Go to the dist folder directory and open the index.html file directly in your browser

You can now see that we typed “Hello Webpack!” It’s already displayed in the console.

Current problems

The above is a basic process, but there are still some biggest problems:

  1. Hot updates are required. It is not possible to rebuild after every update and use a packaged file for debugging;
  2. You need to clear the contents of the last package before each package.
  3. Environment split

So next, to solve these two problems first!

Hot update

Here we use webpack-dev-server as suggested on the official website

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

Then you need to add the relevant configuration to webpack.config.js:

module.exports = { // ... DevServer: {port: '8080', // Enable the port number, usually 8080 hot: true, // enable the hot Module Replacement function of Webpack. 'errors-only', // terminal only prints error compress: true, // whether gzip compression is enabled},}Copy the code

Then we need to add a “scripts” command to package.json

{/ /... "scripts": { "start": "webpack serve --open" } }Copy the code

At this point, you can use the YARN start command to open the http://localhost:8080/ page in your browser and use hot updates for debugging. What a convenience!

Remove old packaging products

This problem can be easily solved by using the clean-webpack-plugin. Note, however, that after WebPack 5.20, the Output of Webpack now supports cleaning build artifacts before each packaging. Simply add clean to the output field in webpack.config.js: True achieves the same effect as the clean-webpack-plugin

// ...

module.exports = {
  ...
  output: {
      // ...
      clean: true
  }
}
Copy the code

Environment split

Typically, a project is divided into development, pre-release, and production environments, and in this case, mainly development and production environments

Update directory structure, create build folder in the root directory, delete wbepack.config.js from the original root directory, Create webpack.base.config.js (public part), webpack.dev.config.js (development part), and webpack.prod.config.js (production part) in the build directory.

Webpack. Base. Config. Js:

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

const rootDir = process.cwd();

module.exports = {
  entry: path.resolve(rootDir, 'src/index.js'),
  output: {
    path: path.resolve(rootDir, 'dist'),
    filename: 'bundle.[contenthash:8].js',
  },
  module: {
    rules: [
      {
        test: /\.(jsx|js)$/,
        use: 'babel-loader',
        include: path.resolve(rootDir, 'src'),
        exclude: /node_modules/,
      },
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(rootDir, 'public/index.html'),
      inject: 'body',
      scriptLoading: 'blocking',
    }),
  ],
}
Copy the code

When configuring production and development environments, you need to install a plug-in for WebPack-Merge to merge configurations.

yarn add --dev webpack-merge
Copy the code

Webpack. Dev. Config. Js:

const { merge } = require('webpack-merge'); const baseConfig = require('./webpack.base.config'); Module. exports = merge(baseConfig, {mode: 'development', devServer: {port: '8080', // default: 8080, stats: 'errors-only', // terminal only prints error compress: true, // whether gzip compression is enabled},});Copy the code

Webpack. Prod. Config, js:

const { merge } = require('webpack-merge');
const baseConfig = require('./webpack.base.config');

module.exports = merge(baseConfig, {
  mode: 'production',
});
Copy the code

Finally, be careful to update the scripts command in the package.json file:

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

Continue to improve features

Supports SASS and CSS

First, add an index. SCSS file to the SRC directory and import it to the index.js file. In this case, after yarn start, you can find that webpack cannot be installed without the corresponding Loader. Identify the contents of SCSS and CSS files.

Next let’s install loader.

yarn add --dev sass dart-sass sass-loader css-loader style-loader
Copy the code

Note: If you have used creat-react-app to configure SASS, you will find that sASS can be installed in conjunction with Node-sass. If you have used creat-react-app to configure Sass, you will find that sass can be installed in conjunction with Node-sass. Create-react-app does not support Dart-Sass. However, if we manually configure it ourselves, dart-Sass should work fine.

Then, modify the webpack.base.config.js file

module.exports = {
  // ...
  module: {
    rules: [
      // ...
      {
        test: /\.(s[ac]ss|css)$/i,
        exclude: /node_modules/,
        use: ['style-loader', 'css-loader', 'sass-loader']
      },
    ]
  },
  // ...
}
Copy the code

At this point, run the project using YARN Start, and you can see that the CSS is ready to be displayed in the project.

Add postcss

Next, let’s add postCSS

yarn add --dev autoprefixer postcss postcss-loader
Copy the code

Update the webpack.base.config.js file

const autoprefixer = require('autoprefixer');

module.exports = {
  // ...
  module: {
    rules: [
      // ...
      {
        test: /\.(s[ac]ss|css)$/i,
        exclude: /node_modules/,
        use: [
          'style-loader',
          'css-loader',
          'sass-loader',
          {
            loader: "postcss-loader",
            options: {
              postcssOption: {
                plugins: [
                  ["autoprefixer"]
                ]
              }
            }
          }
        ]
      },
    ]
  },
  // ...
}
Copy the code

Pack and remove the CSS file

Install the mini-CSS-extract-plugin first

yarn add --dev mini-css-extract-plugin
Copy the code

Update webpack. Base. Config. Js

const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module.exports = { // ... module: { rules: [ // ... { test: /\.(s[ac]ss|css)$/i, exclude: /node_modules/, use: [ MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader', { loader: "postcss-loader", options: { postcssOptions: {plugins: [["autoprefixer"]]}}}]},]}, plugins: [// omit... new MiniCssExtractPlugin({filename: 'css/[name].css', }), ], }Copy the code

At this point, run YARN Build to see

As you can see, the CSS file has been removed to the specified directory.

Copy static resources to the package directory

Sometimes there may be a static file that needs to be manually downloaded and added to the project manually. Normally, we would put this resource in our public file directory and import it as script in index.html. But the reality is that the file cannot be found after being added to the public directory.

// index.html <! doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, User - scalable = no, initial - scale = 1.0, the maximum - scale = 1.0, Minimum -scale=1.0"> <meta http-equiv=" x-UA-compatible "content=" IE =edge"> <title>Document</title> </head> <body> <script  src="./js/test.js"></script> </body> </html>Copy the code

Compile result:

This is where the copy-webpack-plugin plugin comes in, which copies the specified files into the packaged artifacts when the build is packaged.

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

Update webpack. Base. Js

const CopyWebpackPlugin = require('copy-webpack-plugin');

const rootDir = process.cwd();

module.exports = {
  // ...
  plugins: [
    
    new CopyWebpackPlugin({
      patterns: [
        {
          from: '*.js',
          context: path.resolve(rootDir, "public/js"),
          to: path.resolve(rootDir, 'dist/js'),
        },
      ],
    })
    new MiniCssExtractPlugin({
      filename: 'css/[name].css',
    }),
    new OptimizeCssPlugin(),
  ],
}
Copy the code

Run YARN start again

The static JS file is now ready to load successfully

Loading image resources

The front-end project will inevitably introduce images and other resources. At this point, we try to introduce resources into the project. Introducing images in index.js:

import './index.scss' import imgTets from './assets/1.png' class Hello { constructor() { console.log('hello webpack! ') } renderImg() { const img = document.createElement('img') img.src = imgTets document.body.appendChild(img) } } const test = new Hello() test.renderImg()Copy the code

Run the project and you’ll see a familiar error:

Yarn add XXx-loader: install raw-loader, URl-loader, file-loader and yarn add XXx-loader: install raw-loader, URL-loader, file-loader and yarn add XXx-loader.

Note, however, that in WebPack 5 you don’t need to install these dependencies anymore, just add the following configuration to webpack.base.config.js:

rules: [
    // ...
    {
        test: /\.(png|jpg|gif|jpeg|webp|svg|eot|ttf|woff|woff2)$/,
        type: 'asset',
    },
]
Copy the code

Run yarn Start again, then run again, there is no problem!

Project optimization during construction

The cache

Webpack 5 has done a lot of things for us, including caching. Configuration:

// webpack.dev.config.js

module.exports = merge(baseConfig, {
  mode: 'development',
  //...
  cache: {
    type: 'memory'
  },
});
Copy the code
// webpack.prod.config.js

module.exports = merge(baseConfig, {
  mode: 'production',
  // ...
  cache: {
    type: 'filesystem',
    buildDependencies: {
      config: [__filename]
    },
  },
});
Copy the code

Then we try to run YARN Build twice to see the time difference

You can see the difference, it’s still quite big

Resolution of the code

Split each module code, extract the same part of the code, the advantage is to reduce the frequency of repeated code. SplitChunks instead of CommonsChunksPlugin have been used to split code since WebPack 4. Configuration:

// webpack.base.config.js const webpack = require('webpack'); module.exports = { //... plugins: [ new webpack.optimize.SplitChunksPlugin(), ], optimization: { splitChunks: { chunks: 'all' // code split type: all all module, async asynchronous module, INITIAL entry module}},}Copy the code

Multithreaded packaging

The packaging speed of projects is a troublesome point in large projects. Here, we use thread-loader to carry out multi-threading packaging of projects.

Install dependencies:

yarn add --dev thread-loader
Copy the code

Update webpack. Base. Config. Js

module.exports = {
  entry: path.resolve(rootDir, 'src/index.js'),
  output: {
    path: path.resolve(rootDir, 'dist'),
    filename: 'bundle.[contenthash:8].js',
    clean: true
  },
  module: {
    rules: [
      {
        test: /\.(jsx|js)$/,
        use: ['thread-loader', 'babel-loader'],
        include: path.resolve(rootDir, 'src'),
        exclude: /node_modules/,
      },
      {
        test: /\.(s[ac]ss|css)$/i,
        exclude: /node_modules/,
        use: [
          MiniCssExtractPlugin.loader,
          'thread-loader',
          'css-loader',
          'sass-loader',
          {
            loader: "postcss-loader",
            options: {
              postcssOptions: {
                plugins: [
                  ["autoprefixer"]
                ]
              }
            }
          }
        ]
      },
      {
        test: /\.(png|jpg|gif|jpeg|webp|svg|eot|ttf|woff|woff2)$/,
        type: 'asset',
      },
    ]
  },
  //...
}
Copy the code

Stage 1 Summary

So far, this is a fairly generic WebPack 5-based scaffolding. React or Vue is not yet installed and configured.

In phase 2, I need to build on this scaffolding and add react, typescript, tailwindCSS, etc. to meet my needs for daily development!