preface

Original intention of this article: Most of my projects are developed with create-React-app. From development to packaging, I just type commands and don’t see webpack.config.js configuration file.

The importance of learning Webpack is self-evident. Even though there are so many mature scaffolding on the market, such as CRA for general projects, ANTDPro for SPA management system, TSDX for packaging component library and so on, if you do not understand the principle or even basic usage of these packaging tools, One of these days you’re going to have a problem you don’t know how to solve.

In the form of problem-oriented, this paper will analyze the important configuration of Webpack one by one in the actual construction process. The depth is suitable, and the overall content is relatively basic, which is suitable for the reference of friends who are new to the pit Webpack.

In this example, the Webpack version is 5.x, and the Webpack-CLI version is 4.x

Without further ado, let’s get started

The body of the

1. Initialize the project

mkdir webapck-ts-react
cd webapck-ts-react
yarn init 
yarn add webpack webpack-cli -D
Copy the code

The following structure is initialized in the empty project:

🤔 Question 1: What is Webpack?

👉 expand to find out

– Webpack is a packaging tool; Package project files conforming to ‘ES Module’ and ‘CommonJS’ modular specifications into a static resource (deployable to the server)

A diagram illustrates the role of WebPack

Try NPX webpack instead:

webpack
Copy the code

Js file. If we don’t configure anything in webpack.config.js, we will package it according to the corresponding gateway by default. The default command is similar to:

// webpack.config.js

const path = require('path')

module.exports = {
  entry: './src/index.js'.output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js'}}Copy the code

If you run NPX webpack on the command line again, the result is the same

In the configuration item, entry is an entry and can be configured as a relative path. Output is the exit and path must be set to an absolute path.

Why do output paths require absolute paths?

The reason for the above difference is that the entry can be defined as the entry in the project, but the exit can theoretically be any value on the disk, so the path of output must be an absolute path.

Filename of output can write any fixed value in a single-entry project, but cannot write a fixed value in a multi-entry project. [name] is a variable placeholder indicating an unfixed value.

🤔 QUESTION 2: Why NPX webpack instead of direct Webpack?

👉 expand to find out

The webpack command has two default modes: global and local (local); If you execute webpack directly and use global Webpack compilation, the result is the same. If NPX webpack is used, it will look for the webpack directive to execute in the current project
/node_modules/bin/webpack

🤔 Q3: Isn’t it enough to have webpack command globally? Why install Webpack locally?

👉 expand to find out

Global installations are fixed versions (such as the latest 5.x), and older projects need to use earlier versions of WebPack (such as 4.x), so local versions are used to prevent version conflicts

But NPX webpack is too much trouble every time, so we can do the following configuration in package.json:

// package.json."scripts": {
    "build": "webpack"},...Copy the code

NPX webpack is the same as NPX webpack

2. Process the image Loader

Next we take advantage of webPack’s modular packaging features and create a new module dedicated to loading images on the page:

// src/loadImg.js

import Img from './images/picture.jpg'

const Image = document.createElement('img')
Image.src = Img

document.body.appendChild(Image)
Copy the code

Index. Introduced in js

require('./loadImg')

function sum (a, b) {
  return a + b
}

console.log(sum(1.2))
Copy the code

An error occurs when yarn Build is executed:

Webpack only recognizes.js and.json files by default.

🤔 Q4: What is loader?

👉 expand to find out

Webpack can only understand JavaScript and JSON files, which is a built-in capability of WebPack available out of the box. Loader enables WebPack to process other types of files and convert them into valid modules for use by applications and to be added to dependency diagrams.

In Webpack 4.x, file-loader, url-loader, or raw-loader is used to process images. However, in Webpack 5.x, file-loader, urL-loader, or raw-loader is not required. For image and font files, you can use the Type: asset declaration to process the files directly.

Here we use 5.x method to process the picture:

const path = require('path')

module.exports = {
  entry: './src/index.js'.output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js'
  },
+  module: {
+    rules: [
+      {
+        test: /\.(png|jpg|jpeg|gif|webp)$/,
+        type: 'asset'+} +] +}}Copy the code

Packing successfully:

Create a new HTML file and introduce the packaged main.js file test, noting that the script tag must defer:

dist/index.html

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

🤔 as you have noticed, the operation of creating additional files directly in the dist folder is not right, don’t worry, there will be a plugin to help us deal with it automatically.

Open dist/index.html preview and everything is fine:

3. Process the CSS file Loader

Let’s create a new CSS file

// src/css/index.css

body {
  background-color: burlywood;
  color: blueviolet;
}
Copy the code

The introduction of

src/index.js
require('./loadImg')
+ import './css/index.css'

function sum (a, b) {
  return a + b
}

console.log(sum(1.2))
Copy the code

Webpack can only recognize JS files and JSON files by default. All other files need to be recognized by Loader. Install the Loader to process the CSS

yarn add style-loader css-loader -D
Copy the code

Loader used to parse the. CSS file specified in the configuration file

.module.exports = {
...
  module: {
    rules: [{test: /\.(png|jpg|jpeg|gif|webp)$/,
        type: 'asset'
      },
+      {
+        test: /\.css$/,
+        use: ['style-loader'.'css-loader'[+}]}}Copy the code

Yarn build again, no error and the style works

🤔 Q5: CSS-loader is used to parse CSS, so what is style-loader used for?

👉 expand to find out

Css-loader can only recognize and package CSS files, while style-loader inserts the packaged CSS styles into the HEAD of the HTML to make them effective on the page

4. Package mode

Next, solve the packaging mode warning problem:

Simply specify the mode configuration item in webpack.config.js

There are two modes of mode parameter: development and production. The default mode is production. Each mode has a default configuration:

Mode: development

// webpack.development.config.js

module.exports = {
 mode: 'development'
 devtool: 'eval'.cache: true.performance: {
   hints: false
 },
 output: {
   pathinfo: true
 },
 optimization: {
   moduleIds: 'named'.chunkIds: 'named'.mangleExports: false.nodeEnv: 'development'.flagIncludedChunks: false.occurrenceOrder: false.concatenateModules: false.splitChunks: {
     hidePathInfo: false.minSize: 10000.maxAsyncRequests: Infinity.maxInitialRequests: Infinity,},emitOnErrors: true.checkWasmTypes: false.minimize: false.removeAvailableModules: false
 },
 plugins: [
   new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("development")})]}Copy the code

Mode: production

// webpack.production.config.js
module.exports = {
  mode: 'production'.performance: {
   hints: 'warning'
 },
 output: {
   pathinfo: false
 },
 optimization: {
   moduleIds: 'deterministic'.chunkIds: 'deterministic'.mangleExports: 'deterministic'.nodeEnv: 'production'.flagIncludedChunks: true.occurrenceOrder: true.concatenateModules: true.splitChunks: {
     hidePathInfo: true.minSize: 30000.maxAsyncRequests: 5.maxInitialRequests: 3,},emitOnErrors: false.checkWasmTypes: true.minimize: true,},plugins: [
   new TerserPlugin(/ *... * /),
   new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("production")}),new webpack.optimize.ModuleConcatenationPlugin(),
   new webpack.NoEmitOnErrorsPlugin()
 ]
}
Copy the code

5. Package react projects with Babel

The current WebPack configuration already packs JS and CSS as well as image files. As we all know, the core of the React project is to transform its JSX syntax, which brings us to Babel.

🤔 Question 6: What is Babel?

👉 expand to find out

Babel is a toolchain for converting ECMAScript 2015+ version code into backwardly compatible JavaScript syntax so it can run in current and older versions of browsers or other environments. Other things you can do for yourself are:

  1. The syntax conversion

  2. Polyfill missing features in the target environment (via @babel/ Polyfill module)

  3. Source code conversion (Codemods)

How to use Babel: A core package, @babel/core, must be installed. The rest of the functionality can be realized by configuring plugins or preset presets. Here we can use the @babel/ preset-React preset (preset is a set of plugins). Considering the use of Babel in Webpack, the babel-loader is also used

yanr add @babel/core @babel/preset-react babel-loader -D
Copy the code

Of course React and React – DOM also need to be installed into production dependencies

yarn add react react-dom
Copy the code

Create an index. JSX file and write the react code

// src/index.jsx

import React from 'react'
import ReactDOM from 'react-dom'

ReactDOM.render(<div>React Component Test</div>.document.getElementById('root'))
Copy the code

Change the packaging entry and add JSX parsing rules in the configuration file:

// webpack.config.js

const path = require('path')

module.exports = {
-  // entry: './src/inde.jsx',
+  entry: './src/index.jsx'.mode: 'development'.output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js'
  },
  module: {
    rules: [{test: /\.(png|jpg|jpeg|gif|webp)$/,
        type: 'asset'
      },
      {
        test: /\.css$/,
        use: [ 'style-loader'.'css-loader']
      },
+      {
+        test: /\.jsx? /,
+        use: [
+          {
+            loader: 'babel-loader',
+            options: {
+              presets: ['@babel/preset-react'] +} +} +] +}]}}Copy the code

Run the YARN Build package command. Add node root to dist/index.html file

.</head>
<body>
  <div id="root"></div>
</body>
</html>
Copy the code

It can be found that the compilation succeeded:

6. To configure the plugin

(1) HTML – webpack – the plugin

In the previous operation, we manually modified the contents in the dist folder for many times. This operation is definitely not allowed, so we need to configure the template and automatically generate the TEST HTML file with the help of htML-webpack-plugin

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

The SRC directory starts with a new index.html template file

// src/index.html<! 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, </title> <body> <div id="root"></div> </body> </ HTML >Copy the code

Modified configuration items and added plug-ins

// webpack.config.js

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

module.exports = {
  entry: './src/index.jsx'.mode: 'development'.output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js'
  },
  module: {... },plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'}})]Copy the code

After yarn Build is packaged, the template file is automatically generated in the dist folder and the main.js package file is automatically imported

(2) the clean – webpack – the plugin

As the name suggests, this plug-in is simple enough to automatically delete all old packaging files before each packaging generates a new one

yarn add clean-webpak-plugin -D
Copy the code
// webapck.config.js

const { CleanWebpackPlugin } = require('clean-webpack-plugin')...module.exports = {
  entry: './src/index.jsx'.mode: 'development'.output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[chuankhash].[name].js'
  },
  optimization: {
    splitChunks: {
      chunks: 'all'}},module: {... },plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    }),
    new CleanWebpackPlugin()
  ]
}
Copy the code

7. Support TS version of React project compilation

How to make Webpack support TS, in fact, the problem is the same as how to support JSX syntax, for the code conversion work is up to loader to do.

There are two options for supporting the TS writing of the React component, both of which require installing typescript locally

yarn add typescript -D
Copy the code

Generate the tsconfig.json configuration file

yarn tsc --init
Copy the code

Scheme 1: @babel/preset-typescript of babel-loader

One way is to use babel-loader and add preset to support TS resolution:

yarn add @babel/preset-typescript -D
Copy the code

SRC /index.jsx is changed to SRC /index.tsx and entry in the package configuration file is changed to entry: ‘./ SRC /index.tsx’ adds a preset to babel-Loader’s presets array: @babel/preset-typescript

// webpack.config.js

const path = require('path')

module.exports = {
  entry: './src/index.tsx'.mode: 'development'.output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[chuankhash].[name].js'
  },
  module: {
    rules: [{...test: /\.tsx? /,
        use: [
          {
            loader: 'babel-loader'.options: {
+              presets: ['@babel/preset-react'.'@babel/preset-typescript']}}]},}Copy the code

The yarn build command can successfully package the yarn.

However, a lot of typescript syntax is not supported in this scenario. For example, if we create a new Comp component that intentionally writes the wrong type definition:

const Comp = () = > {
  const list: number[] = ['1'.'abc']
  let peekValue: string
  peekValue = list.pop()
  return (<>
    <div>This is the COMP component {peekValue}</div>
  </>)}export default Comp
Copy the code

Run yarn build and you can see:

Although this solution can package TS, it cannot verify TS error syntax during the packaging process. What if you want to package and verify TS error syntax? This is to use another loader:

Solution 2: TS-Loader

yarn add ts-loader -D
Copy the code

Remove @babel/preset-typescript and add TS-loader to the configuration file after packing:

Ts-loader can verify the TS syntax that does not conform to the rules during the packaging process.

The troubleshooting procedures are as follows

  1. Tsconfig. json config “JSX “: “react”

  2. yarn add @types/react @types/react-dom

  3. Resolve specific syntax errors

8. Optimize the development experience of Webpack-dev-server

While it’s not “automated” to manually view HTML file changes after each repackage, WebPack allows us to turn on a local service that listens to the packaging process to automatically update pages, and also to hot-update them.

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

To enable the packaging service, run the webpack-dev-server command in the 4.x version. In 5.x just: WebPack Serve

// package.json{..."scripts": {
    "build": "webpack"."dev": "webpack serve"},... }Copy the code

In this case, run yarn dev to observe that package listening is enabled. The configuration items of devSer are as follows:

// webpack.config.js

module.exports = {
    ...
    devServer: {
      contentBase: path.join(__dirname, "dist"), // * Service startup root (static service directory except main.js)
      compress: true.// * Enable Gzip Compression for each static file
      open: true.// * Whether to open the browser automatically. The default value is false
      port: 8081.// * User-defined service port. The default value is 8080
      hot: true.// * Whether to enable module hot update. The default value is false
      proxy: { // * Local forward proxy (often used for non-same-origin requests)
        "/api": {
          target: "http://localhost:3000".pathRewrite: {
            "^/api": "",},},},},... }Copy the code

Now, a TS version of react development environment is set up, and the rest of the custom configuration is based on the needs of the company project. For example, our project used to use the SASS Module development mode.

9. Support sASS Module development mode

Install sass-Loader and Node-sass

yarn add sass-loader node-sass -D
Copy the code
// src/comp.module.scss

.wrap {
  .head {
    font-size: 20px;
    color: blueviolet;
  }
  .body {
    font-size: 14px;
    color: yellowgreen; }}Copy the code
// src/Comp.tsx

import React from 'react'
import styles from './comp.module.scss'

const Comp = () = > {
  const list: string[] = ['1'.'abc']
  let peekValue: string
  peekValue = list.pop() as string
  return (<div className={styles.wrap}>
    <div className={styles.head}>This is the COMP component</div>
    <div className={styles.body}>Test using</div>
  </div>)}export default Comp
Copy the code

Add a resolution rule to the configuration file:

To match TS, we need to create a new type declaration file

// typed-css.d.ts

// SCSS module declaration
declare module '*.scss' {
  const content: {[key: string] :any}
  export = content
}
// Less module declaration
declare module '*.less' {
  const content: { [key: string] :any }
  export default content
}
Copy the code

React hot replacement (HMR)

yarn add @pmmmwh/react-refresh-webpack-plugin react-refresh -D
Copy the code
// webpack.config.js

const ReactRefreshPlugin = require('@pmmmwh/react-refresh-webpack-plugin')...plugins: [
    new HtmlWebpackPlugin({
      template: "./src/index.html",}).new CleanWebpackPlugin(),
+    new ReactRefreshPlugin()
  ],
...
Copy the code

11. Configure the path alias

Be sure to follow the instructions below

// webpack.config.js
module.exports = {
  ...
  resolve: {
    extensions: [".js".".json".".ts".".tsx"].alias: {
      The '@': path.resolve(__dirname, './src')}... }Copy the code
// tsconfig.json

{
  "compilerOptions": {

    "baseUrl": "./src"."paths": {
      "@compoents": ["./components/*"]."@ / *": [". / *",}}Copy the code

conclusion

A working TS React development environment is now in place

Available functions:

  • Typescript grammar
  • sass module
  • Module hot replacement
  • Path alias
  • Parse images and CSS
  • source-map

Later supported items:

  • Third party package optimization, Treeshaking, CDN, etc
  • Production environment configuration file separation
  • Production environment package volume and Chunkname optimization

Webpack-TS-React-Lead