创建项目流程:
创建脚手架:
执行npx create-react-app [项目名称]创建项目
整理文件
删除public文件夹中相关的无效文件和代码,
其中:
public保留index.html,favicon.ico。
src文件中保留index.js。
改写App为jsx的memo包裹项目。
配置目录文件:
创建文件夹并同上命名,用以管理资源
配置开发引导文件
根目录中配置jsconfig.json文件,实现更好的代码提示和组件索引
a) 配置内容如下:
{
"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 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/test的react-scripts启动更改为craco启动
配置less:
执行命令yarn add craco-less@2.1.0-alpha.0
由于之前已经在craco中配置过less的相关插件,建议在css中创建一个less并引用之后进行测试一下,保证less配置的正确运行(webpack相关的配置修改之后记得重启项目)
Css样式重置
b) 执行命令yarn add normalize.css,并在src下index.js中添加
import “normalize.css” 引用.
c) 在assets/css中创建reset.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 {
text-decoration: none;
}
img {
vertical-align: top;
}
ul, li {
list-style: none;
}
并在assets/css/index.less中引用该文件。
d) 对于各个组件,使用styled-components库实现css-in-js的编写方式
i. 执行命令yarn add styled-components
ii. 模块入口文件创建文件style.js代码如下
import styled from ‘styled-components’
export const TestAWrapper = styled.div`
//css code
`
iii. 模块入口文件引用之后,建议最外层根标签使用TestAWrapper来进行包裹,可以提高代码阅读性
配置路由:
e) 执行命令yarn add install react-router-dom
f) Src/index.js下添加
import { HashRouter } from “react-router-dom”
并且使用HashRouter将App包裹
g) 在src/router中创建index.js并添加代码
import { lazy } from "react";
import { Navigate } from "react-router-dom";
const TestA = lazy(() => import("@/views/test-a"))
const routes = [
{
path: '/',
element: <Navigate to="/testa"/>
},
{
path: '/testa',
element: <TestA />
},
]
export default routes;
h) A中添加代码
import { useRoutes } from 'react-router-dom'
import routes from '@/router'
//并且在路由展示区域添加代码
{ useRoutes(routes) }
//来引入自定义路由配置文件
配置redux:
i) 执行命令yarn add @reduxjs/toolkit react-redux
j) 在store/index.js中整合所有的redux,作为统一入口文件
import { configureStore } from '@reduxjs/toolkit'
import testaReducer from './test-a'
const store = configureStore({
reducer: {
testa: testaReducer
}
})
export default store
k) 在store建立和views中相同的文件夹树结构,并且在对应的树结点文件夹中创建入口文件来做redux片段代码的编写,例如,在store/testa/index.js中创建代码:
import { createSlice } from '@reduxjs/toolkit'
const TestASlice = createSlice({
name: 'testa',
initialState: {
count: 0,
},
reducers: {
},
extraReducers: builder => {
builder.addCase(
//todo
)
}
})
export default TestASlice.reducer
配置axios:
l) 执行命令:yarn add axios
m) 在service中创建入口文件index.js,模块请求方法文件夹modules,请求二次封装文件夹request,具体代码根据业务逻辑会发生变化
n) 以易查内部系统为例,二次封装请求命名为ycrequest
service/index.js文件代码
import ycrequest from “./request”;
export default ycrequest
service/request中包含config.js和index.js两个文件
其中config.js文件用来配置网络请求的常量参数
export const BASE_URL = “http://192.168.1.215:6100”
export const TIMEOUT = 10000
index.js文件中代码为
import axios from 'axios';
import { BASE_URL, TIMEOUT } from './config';
class YCRequest {
constructor(baseURL, timeout) {
this.instance = axios.create({
baseURL,
timeout
})
this.instance.interceptors.response(res => {
// 请求成功判断
if (res.code === "200")
return res.data
else
throw new Error(res.message)
}, err => {
//error todo
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 ycrequest = new YCRequest( BASE_URL, TIMEOUT )
export default ycrequest
该网络请求使用类实例对三方库调动的方法进行二次封装,以防止三方库出现异常后替换需要过大代价的情况。
Modules文件夹中用于存放不同模块需要使用的网络请求,只存放网络请求发起方法,网络请求处理的方法应封装在reduxSlice中。
o) 代理劫持配置
详情查看src/setupProxy文件
异步网络编写:
p) 网络请求方法编写在services/modules中对应的结点文件夹中,请求方法举例:
import { stringify } from "qs";
import ycrequest from "../request";
export function getUserInfo(payload) {
return ycrequest.get({
url: `/auth/user/login?${stringify(payload)}`
})
}
q) 组件中派发redux中的action来发起网络请求代码举例:
const dispatch = useDispatch()
useEffect(() => {
const requestParams = {
name: 'sg',
password: '123456'
}
dispatch(fetchUserInfo(requestParams))
}, [dispatch])
r) 将网络请求结果保存到redux中的代码写在store中对应的结点文件夹中,保存方法举例:
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'
import { getUserInfo } from '@/services'
export const fetchUserInfo = createAsyncThunk("fetchUserInfo", async payload => {
const res = await getUserInfo(payload)
return res.user
})
const TestASlice = 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 } = TestASlice.actions
export default TestASlice.reducer
组件中从Redux数据获取:
代码示例:
const { nickName, sex, phonenumber } = useSelector(state => ({
nickName: state.testa.userInfo.nickName,
sex: state.testa.userInfo.sex,
phonenumber: state.testa.userInfo.phonenumber,
}), shallowEqual)
整个项目开发搭建流程大致如此,该版本项目搭建时间为2023年1月上旬,记录一下,未来变更会另行记录