使用 webpack 从 0 搭建 React 项目

Use Webpack to build your own React project

Posted by Reckless on August 18, 2023

项目初始化

yarn init -y 个人较为喜欢 yarn 作为包管理工具,可自行选择为 npm 或其他包管理器

安装 React 相关依赖

yarn add react react-dom

react: 类组件、函数组件、hooks、contexts、refs…这些都是 React 特性,即 React API。
react-dom:渲染器,负责在不同的宿主载体上实现特性,达到与描述相对应的真实效果。比如在浏览器上,渲染出DOM树、响应点击事件等。

安装 webpack 相关依赖

yarn add -D webpack webpack-cli webpack-dev-server

webpack:构建工具
webpack-cli:命令行运行 webpack 的工具
webpack-dev-server:开发服务器

安装 babel 相关依赖

yarn add babel-loader @babel/core @babel/preset-react

babel-loader:webpack 的加载器(loader),用于将 JavaScript 代码通过 Babel 进行转换。
@babel/core:Babel 的核心模块,它是 babel 编译器的主要部分。
@babel/preset-react:Babel 的一个预设(preset),用于转换 React 中的 JSX 语法。

注:React 17 之前需要引入 React,如:

1
import React from "react";

React 17 之后不需要引入 React,可在运行时自动注入,但需要在 webpack.config.js 下进行相应配置:

Before React v17

1
2
3
4
5
6
7
8
9
10
11
module: {
    rules: [
        {
            test: /\.js$/,
            loader: "babel-loader",
            options: {
                presets: ["@babel/preset-react"]
            }
        },
    ]
}

After React v17

1
2
3
4
5
6
7
8
9
10
11
12
13
module: {
    rules: [
        {
            test: /\.js$/,
            loader: "babel-loader",
            options: {
                presets: [["@babel/preset-react", {
                    runtime: "automatic" 
                }]] // 注意这里是嵌套数组
            }
        },
    ]
}

创建 webpack 配置文件

在项目根目录下创建 webpack.config.js ,并在此文件中配置入口、出口、模块等。示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const path = require("path")

module.exports = {
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js',
        'publicPath': '/'
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                loader: "babel-loader",
                options: {
                    presets: ["@babel/preset-react"]
                }
            }
        ]
    },
    devServer: {
        historyApiFallback: true,
        hot: true,
        port: 3456
    }
}

创建项目文件结构

  1. 在项目根目录下创建一个名为 src 的文件夹。
  2. src 文件夹中创建一个名为 index.js 的文件,作为项目的入口文件。

创建 HTML 模板

  1. 在项目根目录下创建一个名为 public 的文件夹。
  2. 在 public 文件夹中创建一个名为 index.html 的文件,并在 body 中添加以下内容:
1
2
<div id="root"></div>
<script src="/bundle.js"></script>

编写基础代码

打开 src/index.js 文件并添加以下代码:

1
2
3
4
5
6
import React from 'react';
import { createRoot } from 'react-dom/client';

const container = document.getElementById('root');
const root = createRoot(container); // createRoot(container!) if you use TypeScript
root.render(<h1>Hello, React!</h1>);

React 18 引入了一个新的根 API,它为管理根提供了更好的人体工程学。新的根 API 还启用了新的并发渲染器,它允许您选择并发功能。

如果安装 v18 的 React,则需要以上创建根节点的方式,v18 之前使用 ReactDOM.render 即可。

运行开发服务器

  1. 打开终端或命令行界面,确保当前目录位于项目根目录。
  2. 执行此命令以启动开发服务器 npx webpack serve --mode development

可以在 package.json 中设置 scripts,以使用更简洁的命令行运行开发服务器

1
2
3
4
5
{
  "scripts": {
    "start": "npx webpack serve --mode development"
  }
}

使用 loader 和 plugin 在不同环境下处理样式

yarn add css-loader style-loader mini-css-extract-plugin sass sass-loader

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
module.exports = {
    module: {
        rules: [
            {
                test: /\.s[ac]ss$/,
                use: [ // 一定要注意在 webpack 中配置 loaders 的顺序,因为 loaders 总是从下到上执行
                    (
                        process.env.NODE_ENV === 'development'
                            ? "style-loader" // 开发环境将 css-loader 转译出的 JS 字符串插入到页面的 style 标签
                            : MiniCssExtractPlugin.loader // 将样式代码抽离到单独产物文件,并以 <link> 标签方式引入到页面中
                    ),
                    "css-loader", // 将 css 解析为 webpack 可理解的 js
                    "sass-loader"  // 将 scss 解析为 css
                ]
            }
        ]
    },
    plugins: [
        new MiniCssExtractPlugin(),
        new HtmlWebpackPlugin({ //
            template: "public/index.html" // 需要指定模板 HTML 路径
        })
    ]
}

这里需要注意几个点:

  • mini-css-extract-plugin 库同时提供 Loader、Plugin 组件,需要同时使用
  • mini-css-extract-plugin 不能与 style-loader 混用,否则报错,所以上述示例中第 9 行需要判断 process.env.NODE_ENV 环境变量决定使用那个 Loader
  • mini-css-extract-plugin 需要与 html-webpack-plugin 同时使用,才能将产物路径以 link 标签方式插入到 html 中

使用 file-loader@svgr/webpack 处理 react 中的静态资源

使用 file-loader 来处理各种图片静态资源

使用 @svgr/webpack 来处理 svg 文件,这样我们可以直接把 svg 当作组件方式来引入,并且可以设置相应的类名,示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import Svg from './assets/react.svg';
import styles from './styles.module.scss';
import masiwei from './assets/masiwei.png';

const App = () => {
    return (
        <div>
            <div className={ styles.title }>
                <Svg className={ styles.react }/>
                <h1>Hello, React!</h1>
            </div>
            <div className={ styles.info }>
                <img src={ masiwei } alt=""/>
            </div>
        </div>
    )
}

export default App

两者的 webpack 配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
module.exports = {
    performance: {
        maxAssetSize: 1000000, // 单个文件大小限制(字节)
        maxEntrypointSize: 1000000, // 入口文件大小限制(字节)
    },
    module: {
        rules: [
            {
                test: /\.(png|jpg|gif|webp)$/, // 匹配图片文件
                use: ['file-loader'] // 使用 file-loader
            },
            {
                test: /\.svg$/, // 单独配置 svg 文件 这样可直接作为组件使用
                use: ['@svgr/webpack'],
            }
        ]
    }
}

其中加入了一个新属性 performance,用于限制文件大小,单位:字节



App ready for offline use.