概述

本文旨在通过从0到1搭建一套完整的React开发框架来掌握如​​Webpack​​​、​​React​​​、​​Typescript​​​、​​ CSS preprocessor​​​等各部分是如何协同进行编译开发的,进而能去了解如​​creat-react-app​​之类的框架都做了哪些事情。

同时本文还提供了业务层面的如路由​​react-router​​​、状态管理器​​redux-toolkit​​的配置和使用方式。


TIPS: 本文中所有包版本均采用当前最新版本,注意区别于如webpack、react、react-router等模块的旧版本使用方式。


一、初始化项目

yarn init


yarn init或npm init都是OK的


二、构建核心打包环境

  1. 添加依赖
yarn add webpack webpack-cli webpack-dev-server -D
  1. 依赖模块说明

模块名

描述

版本

webpack

模块化打包工具,打包代码时的核心依赖

^5.72.1

webpack-cli

支持在命令行中执行webpack的工具

^4.9.2

webpack-dev-server

本地开发服务器,便于开发

^4.9.0

webpack-merge

用于合并webpack-config文件

^5.8.0

  1. webpack.config.js 配置文件
    webpack.config.js是webpack的默认配置文件,也可以在命令行中通过--config指定配置文件。
    一般在项目中会区分production、development环境的配置文件,这里我们设计配置文件为:公共配置文件,开发环境配置文件,产品环境配置文件,目录结构如下
config
-- webpack.config.base.js
-- webpack.config.dev.js
-- webpack.config.prod.js

webpack.config.base.js 公共配置文件

const path = require("path");

/**
* @method resolve
* @description 从根路径开始查找文件
*/
const resolve = (targetPath) => {
return path.resolve(__dirname, "..", targetPath);
};

module.exports = {
target: "web",
// 入口文件
entry: {
main: resolve("./src/index.js"),
},
// 输出
output: {
// 文件名称
filename: "[name].[contenthash].js",
// 输出目录
path: resolve("./dist"),
// 每次编译输出的时候,清空dist目录 - 这里就不需要clean-webpack-plugin了
clean: true,
// 所有URL访问的前缀路径
publicPath: "/",
},
resolve: {
// 定义了扩展名之后,在import文件时就可以不用写后缀名了,会按循序依次查找
extensions: [".js", ".jsx", ".ts", ".tsx", ".json", ".css", ".less"],
// 设置链接
alias: {
// 注意resolve方法开始的查找的路径是/
"@": resolve("./src"),
},
},
};

webpack.config.dev.js 开发环境配置文件

// merge,合并两个或多个webpack配置文件
const { merge } = require("webpack-merge");

// 导入公共配置文件
const webpackConfigBase = require("./webpack.config.base");

// dev环境下相关配置
module.exports = merge(webpackConfigBase, {
// 指定环境
mode: "development",
// 输出source-map的方式,增加调试。eval是默认推荐的选择,build fast and rebuild fast!
devtool: "eval",
// 本地服务器配置
devServer: {
// 启动GZIP压缩
compress: true,
// 设置端口号
port: 3000,
// 代理请求设置
proxy: {
"/api": {
// 目标域名
target: "http://xxxx.com:8080",
// 允许跨域了
changeOrigin: true,
// 重写路径 - 根据自己的实际需要处理,不需要直接忽略该项设置即可
pathRewrite: {
// 该处理是代码中使用/api开头的请求,如/api/userinfo,实际转发对应服务器的路径是/userinfo
"^/api": "",
},
// https服务的地址,忽略证书相关
secure: false,
},
},
},
});

weback.config.build.js 线上产品环境配置文件

const { merge } = require("webpack-merge");
const webpackConfigBase = require("./webpack.config.base");

module.exports = merge(webpackConfigBase, {
// 指定打包环境
mode: "production",
});
  1. package.json命令行
    在package.json的scripts中添加一下命令:
scripts: {
"dev": "webpack serve --config config/webpack.config.dev",
"build": "webpack build --config config/webpack.config.prod"
}

开发环境运行:​​yarn dev​​​ 生产环境运行:​​​yarn build​


三、添加HTML模板文件

  1. 添加依赖
yarn add html-webpack-plugin -D
  1. 创建模板文件
    目录结构如下
public
-- index.html
-- logo.ico
  1. 更新webpack配置
    在webpack.config.dev.js中添加如下配置:
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
// ...
plugins: [
new HtmlWebpackPlugin({
// HTML模板文件
template: resolve("./public/index.html"),
// 收藏夹图标
favicon: resolve("./public/logo.ico"),
}),
]
// ...
}

四、解析React

  1. 添加依赖
# 添加react、react-dom
yarn add react react-dom
# 添加babel等loader
yarn add babel-loader @babel/core @babel/preset-env @babel/plugin-transform-runtime @babel/preset-react core-js@3 -D
  1. 依赖模块说明

模块名

描述

版本

react

还用说是啥吗? 核心代码

^18.1.0

react-dom

还用说是啥吗? 浏览器端实现

^18.1.0

babel-loader

识别ES6语法,编译js

^8.2.5

@babel/core

babel处理的核心逻辑

^7.18.2

@babel/preset-env

根据一些预设的目标值转换js语法,会打包一些polyfill

^7.18.2

@babel/preset-react

帮助识别jsx语法,解析react

^7.17.12

@babel/plugin-transform-runtime

优化解决preset-env打包的polyfill会污染全局的问题

^7.18.2

core-js

polyfill的核心实现,选择3版本

3

  1. 添加webpack的loader配置
    loader是webpack的文件处理器,让webpack能够处理其他类型的文件,并转为有效的模块,以供程序使用,以及被添加到依赖图中。
    webpack.config.base.js文件
module.exports = {
// ...
module: {
rules: [
{
// 匹配js/jsx
test: /\.jsx?$/,
// 排除node_modules
exclude: /node_modules/,
use: {
// 确定使用的loader
loader: "babel-loader",
// 参数配置
options: {
presets: [
[
// 预设polyfill
"@babel/preset-env",
{
// polyfill 只加载使用的部分
useBuiltIns: "usage",
// 使用corejs解析,模块化
corejs: "3",
},
],
// 解析react
"@babel/preset-react",
],
// 使用transform-runtime,避免全局污染,注入helper
plugins: ["@babel/plugin-transform-runtime"],
},
},
}
]
}
// ...
}
  1. 创建React组件
    创建App.jsx文件
import React, { useState } from "react";

export default function App () {

return <div className="app">
<h1>Hello Webpack-React</h1>
</div>;
}

入口文件src/index.js

import React from "react";
// 注意这里最新版的ReactDOM是从client中导出的
import ReactDOM from "react-dom/client";
// 因为设置了extensions,所以可以不加扩展名
import App from './App';

// 创建app根节点
const appEl = document.createElement("div");
// 设置id
appEl.id = "app";
// 追加节点到body中
document.body.appendChild(appEl);

// 最新版本使用的是ReactDOM.createRoot
// 如果使用ReactDOM.render()控制台会报warnning错误
const root = ReactDOM.createRoot(appEl);

// 渲染
root.render(<App />);

页面效果图:从0到1搭建完整React开发框架:webpack@5.x,react@18.x_css

效果都不用说,肯定是 从0到1搭建完整React开发框架:webpack@5.x,react@18.x_css_02

五、解析CSS以及CSS预处理器

CSS在webpack中也是作为一个资源被识别的,需要配置相关的loader。CSS预处理器如less/sass/stylus/postcss都可以被相关的loader识别。

  1. 添加依赖
yarn add css-loader less less-loader style-loader postcss postcss-loader mini-css-extract-plugin cross-env autoprefixer css-minimizer-webpack-plugin -D
  1. 依赖模块说明

模块名

描述

版本

css-loader

解析css

^6.7.1

less

支持less语法

^4.1.2

less-loader

解析less

^11.0.0

style-loader

将解析的css内容追加到head中

^3.3.1

postcss

好用好玩的css插件,压缩、自动补全

^8.4.14

postcss-loader

解析postcss设置

^7.0.0

mini-css-extract-plugin

分离CSS

^2.6.0

cross-env

好用的环境配置

^7.0.3

autoprefixer

自动补全css属性前缀

^10.4.7

css-minimizer-webpack-plugin

生产环境,压缩css

^4.0.0

  1. webpack相关配置
    webpack.config.dev.js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

const isProd = process.env.NODE_ENV === "prod";

module.exports = {
// ...
plugins: [
new MiniCssExtractPlugin({
// 输出的每个css文件名称
filename: isProd ? "[name].[contenthash].css" : "[name].css",
// 非入口的chunk文件名 - 通过import()加载异步组件中样式
chunkFilename: isProd ? "[id].[contenthash].css" : "[id].css",
}),
],

module: {
rules: [
{
test: /\.(css|less)$/,
use: [
// 生产环境下直接分离打包css
isProd ? MiniCssExtractPlugin.loader : "style-loader",
{
loader: "css-loader",
},
"less-loader",
{
loader: "postcss-loader",
options: {
postcssOptions: {
// 浏览器前缀自动补全
plugins: ["autoprefixer"],
},
},
},
],
},
]
}

// ...
}


在webpack的loader中,加载顺序是从右向左依次处理,css/less文件的处理顺序是:postcss-loader -> less-loader -> css-loader -> (style-loader | MiniCssExtractPlugin.loader)


webpack.config.prod.js

在生产环境中,将打包的css文件进行压缩

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

module.exports = merge(webpackConfigBase, {
// ...
optimization: {
minimizer: [
new CssMinimizerPlugin()
]
},
// ...
})
  1. package.json更新scripts,增加环境变量
    通过cross-env配置环境变量参数,跨平台好用!
"scripts": {
"dev": "cross-env NODE_ENV=dev webpack serve --config config/webpack.config.dev",
"build": "cross-env NODE_ENV=prod webpack build --config config/webpack.config.prod"
},

传递参数NODE_ENV,在webpack的配置文件中可以使用​​process.env.NODE_ENV​​获取值。

判断是否是生产环境

const isProd = process.env.NODE_ENV === 'prod';