项目环境搭建:React 18 + Antd 5 + Router 6 + Redux + Toolkit + Axios

1,157 阅读4分钟

写在前面

最近在开发新项目,有机会从0开始配置环境,现把流程记录下来。

工程地址:gitee.com/kagebousii/…

本文涵盖从创建 React 项目、配置依赖包、配置调试环境等内容,重在快速搭建标准 React 项目工程,希望能帮到大家。

本项目各依赖包版本:

"@reduxjs/toolkit": "^1.9.2",
"antd": "^5.1.7",
"axios": "^1.3.0",
"craco-less": "2.1.0-alpha.0",
"normalize.css": "^8.0.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-redux": "^8.0.5",
"react-router-dom": "^6.8.0",
"styled-components": "^5.3.6",
...

初始化项目以及项目目录结构设计

执行命令npx create-react-app [项目名称]来创建一个新项目,执行yarn start (npm start)启动该项目,之后删除项目中用不到的文件或者代码。

在 public 文件夹中保留index.htmlfavicon.ico。在 src 文件夹中保留index.js。改写 App.js 为 memo 包裹项目。

目前精简后的项目结构:

新建文件夹并如图命名,用以管理资源,顺手创建两个页面 Login 和 Home,用以后续测试:

配置 开发引导文件

在根目录中创建jsconfig.json文件,实现更好的代码提示和组件索引:

{
    "compilerOptions": {
        "target": "es5",
        "module": "esnext",
        "baseUrl": "./",
        "moduleResolution": "node",
        "paths": {
            "@/*": [
                "src/*"
            ]
        },
        "jsx": "preserve",
        "lib": [
            "esnext",
            "dom",
            "dom.iterable",
            "scripthost"
        ]
    }
}

配置 Craco

执行命令yarn add @craco/craco@alpha -D,在开发依赖环境下安装 craco ,用以覆写 config 文件内容。

在根目录中创建craco.config.js文件并如下配置:

const path = require("path")
const CracoLessPlugin = require("craco-less")

const resolve = pathname => path.resolve(__dirname, pathname)

module.exports = {
    //less
    plugins: [
        {
            plugin: CracoLessPlugin,
        }
    ],
    //webpack
    webpack: {
        alias: {
            "@": resolve("src"),
            "components": resolve("src/components"),
            "utils": resolve("src/utils")
        }
    }
}

package.json文件中的 script 里start/build/testreact-scripts启动更改为 craco 启动:

配置 Less

执行命令yarn add craco-less@2.1.0-alpha.0安装 Less。

由于之前已经在 craco 中配置过 Less 的相关插件,建议在 css 中创建一个 Less 并引用之后进行测试一下,保证 Less配置的正确运行(webpack 相关的配置修改之后记得重启项目)。

配置 全局css

执行命令yarn add normalize.css,并在 src/index.js 中添加import "normalize.css"

在 assets/css 中创建variables.less文件:

@textColor: #222;
@textColorSecondary: #333;

在 assets/css 中创建reset.less文件:

@import "./variables.less";

body, button, dd, dl, dt, form, h1, h2, h3, h4, h5, h6, hr, input, li, ol, p, pre, td, textarea, th, ul {
    margin: 0;
    padding: 0;
}

a {
    color: @textColor;
    text-decoration: none;
}

img {
    vertical-align: top;
}

ul, li {
    list-style: none;
}

在 assets/css 中创建index.less文件:

@import './reset.less';

body {
    font-size: 20px;
}

index.js中引入全局样式:

对于各个组件的样式,使用styled-components库实现css-in-js的编写方式。

执行命令yarn add styled-components安装styled-components

未来会在各模块入口文件夹中创建文件style.js,代码如下:

import styled from "styled-components";

export const TestAWrapper = styled.div`
    height: 300px;
`

配置 Ant Design

执行命令yarn add antd

其他配置等项目要用了再写,比如语言国际化,修改默认主题等。

配置 Router

执行命令yarn add react-router-dom

在 src/index.js 中添加import { HashRouter } from "react-router-dom"并包裹APP组件

在 src/router 中创建index.js并添加代码:

import { Navigate } from "react-router-dom";
import { lazy } from "react";

const Home = lazy(() => import("@/views/Home"));
const Login = lazy(() => import("@/views/Login"));

const routes = [
	{
		path: "/",
		element: <Navigate to="/login" />,
	},
	{
		path: "/home",
		element: <Home />,
	},
	{
		path: "/login",
		element: <Login />,
	},
];

export default routes;

在 App 中添加代码:

执行yarn start启动项目,输入对应的路由地址,可以正常显示对应的页面了。

login页: http://localhost:3000/#/login
home页: http://localhost:3000/#/home

配置 Redux 和 Redux toolkit

执行命令yarn add @reduxjs/toolkit react-redux

在 store 中创建 login.js:

import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'

import { getUserInfo } from '@/services'

export const fetchUserInfo = createAsyncThunk("fetchUserInfo", async payload => {
    const res = await getUserInfo(payload)
    return res.user
})

const LoginSlice = createSlice({
    name: 'testa',
    initialState: {
        userInfo: {}
    },
    reducers: {
        changeUserInfo(state, { payload }) {
            state.userInfo = payload
        }
    },
    extraReducers: builder => {
        builder.addCase(fetchUserInfo.fulfilled, (state, { payload }) => {
                    //request success todo
                    state.userInfo = payload
                })
                .addCase(fetchUserInfo.rejected, () => {
                    //request failed todo
                    console.log("request rejected")
                })
    }
})

export const { changeUserInfo } = LoginSlice.actions;
export default LoginSlice.reducer;

在 store 中创建 index.js:

import LoginReducer from "./login";
import { configureStore } from "@reduxjs/toolkit";

const store = configureStore({
  reducer: {
    login: LoginReducer,
  },
});

export default store;

配置 Axios

执行命令yarn add axios

在 src/services 中创建 request 文件,配置拦截器

import axios from "axios";

const BASE_URL = "xxxx"
const TIMEOUT = 10000

class Request {
  constructor(baseURL, timeout) {
    this.instance = axios.create({
      baseURL,
      timeout,
    });
    // TODO:请求拦截器

    // 响应拦截器
    this.instance.interceptors.response.use(
      (res) => {
        //  请求成功判断
        if (res.data.code === 200) return res.data.data;
        else {
          console.log("request error");
          throw new Error(res.message);
        }
      },
      (err) => {
        //error todo
        console.log("network error");
        throw new Error(err);
      }
    );
  }

  request(config) {
    return this.instance.request(config);
  }

  get(config) {
    return this.request({
      ...config,
      method: "get",
    });
  }

  post(config) {
    return this.request({
      ...config,
      method: "post",
    });
  }
}

const request = new Request(BASE_URL, TIMEOUT);
export default request;

在 src/services 中创建 login 模块

import request from "./request";
import { stringify } from "qs";


export function getUserInfo(payload) {
    return request.get({
        url: `/auth/user/login?${stringify(payload)}`
    })
}

页面开发测试

改写 login

import { Button, Input } from "antd";
import React, { memo } from "react";

const Login = memo(() => {
    return (
        <div>
            <Input placeholder="账号" />
            <br />
            <Input.Password placeholder="密码" />
            <br />
            <Button type="primary">登录</Button>
        </div>
    );
});

export default Login;

改写home

import React, { memo } from "react";


import { Button } from "antd";


const Home = memo(() => {
  return (
    <div>
      <h1>Home Page</h1>
      <Button type="primary">返回</Button>
    </div>
  );
});


export default Home;

在React组件中实现页面路由跳转