命令:
npx create-react-app react-ts-app --template typescript
npm i axios sass antd @ant-design/icons react-router-dom redux react-redux redux-persist @reduxjs/toolkit
创建模块
- Views->
-
Home-> -
Home.tsx -
Home.modules.scss
配置路由
使用BrowserRouter
注: ** react-router-dom 与 react router区别?**
route-> index.ts
import { lazy } from "react";
import { createBrowserRouter } from "react-router-dom";
import type { RouteObject } from "react-router-dom";
import React from "react";
import { CalendarOutlined, CopyOutlined ,FileAddOutlined,ScheduleOutlined,WarningOutlined} from "@ant-design/icons";
const Home = lazy(() => import('../views/Home/Home'))
const Sign = lazy(() => import('../views/Sign/Sign'))
const Exception = lazy(() => import('../views/Exception/Exception'))
const Apply = lazy(() => import('../views/Apply/Apply'))
const Check = lazy(() => import('../views/Check/Check'))
const Login = lazy(() => import('../views/Login/Login'))
// 导航守卫
const BeforeEach = lazy(() => import("../components/BeforeEach/BeforeEach"))
// 扩展meta元信息接口
declare module 'react-router' {
interface IndexRouteObject {
meta?: {
menu?: boolean,
title?: string,
icon?: React.ReactNode,
auth?: boolean
}
}
interface NonIndexRouteObject {
meta?: {
menu?: boolean,
title?: string,
icon?: React.ReactNode,
auth?: boolean
}
}
}
export const routes: RouteObject[]= [
{
path: '/',
element: React.createElement(BeforeEach, null, React.createElement(Home)),
meta: {
menu: true,
title: '考勤管理',
icon: React.createElement(CopyOutlined),
auth: true
},
children: [
{
path: 'sign',
element: React.createElement(Sign),
meta: {
menu: true,
title: '在线打卡签到',
icon: React.createElement(CalendarOutlined),
auth: true
},
},
]
},
{
path: '/login',
element: React.createElement(BeforeEach, null, React.createElement(Login)),
}
]
const router = createBrowserRouter(routes);
export default router;
导航守卫
注: matchRoutes: ?
import React from "react";
import {useLocation, matchRoutes, Navigate} from 'react-router-dom'
import {routes} from '../../router'
interface BeforeEachProps {
children?:React.ReactNode
}
export default function BeforeEach(props: BeforeEachProps) {
// 对路由做导航守卫
const location = useLocation();
const matchs = matchRoutes(routes, location)
console.log('location',location);
console.log('matchs', matchs);
// 由于matchs中的最后一项类型不确定 所以加个类型保护(isArray)
if( Array.isArray(matchs)) {
const meta = matchs[matchs.length - 1].route.meta
// if(meta?.auth) {
// return <Navigate to="/login" />
// }
}
return (
<>
{props.children}
</>
)
}
封装Axios
import axios from 'axios'
import type { AxiosRequestConfig, AxiosResponse } from 'axios'
// 分装axios
const instance = axios.create({
baseURL: 'http://localhost:8080',
timeout: 5000
})
instance.interceptors.request.use(function (config) {
return config;
}, function (error) {
return Promise.reject(error)
});
// 添加响应拦截器
instance.interceptors.response.use(function (response) {
return response
}, function (error) {
return Promise.reject(error)
})
interface Data {
[index: string]: unknown
}
interface Http {
get: (url: string, data?: Data, config?: AxiosRequestConfig) => Promise<AxiosResponse>
post: (url: string, data?: Data, config?: AxiosRequestConfig) => Promise<AxiosResponse>
put: (url: string, data?: Data, config?: AxiosRequestConfig) => Promise<AxiosResponse>
patch: (url: string, data?: Data, config?: AxiosRequestConfig) => Promise<AxiosResponse>
delete: (url: string, data?: Data, config?: AxiosRequestConfig) => Promise<AxiosResponse>
}
const http: Http = {
get(url, data, config) {
return instance.get(url, {
params: data,
...config
})
},
post(url, data, config) {
return instance.post(url, data, config)
},
put(url, data, config) {
return instance.put(url, data, config)
},
patch(url, data, config) {
return instance.patch(url, data, config)
},
delete(url, data, config) {
return instance.delete(url, {
data,
...config
})
}
}
export default http
配置Redux
- store->
-
index.tsx -
modules-> -
users.tsx
注: createSlice: ?
// users.tsx
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import {PayloadAction} from '@reduxjs/toolkit';
import http from "../../utils/http";
type Token = string
type Infos = {
[index: string]: unknown
}
type UsersState = {
token: Token
infos: Infos
}
type Login = {
email: string
pass: string
}
// 异步方法
export const loginAction = createAsyncThunk('users/loginAction', async (payload: Login) => {
const ret = await http.post('/users/login', payload)
return ret
})
export const infosAction = createAsyncThunk('users/infosAction', async () => {
const ret = await http.get('/users/infos')
return ret
})
const usersSlice = createSlice({
name:'users',
initialState: {
token: '',
infos: {}
} as UsersState,
reducers: {
// 同步方法
updateToken(state, action: PayloadAction<Token>) {
state.token = action.payload;
},
updateInfos(state, action: PayloadAction<Infos>) {
state.infos = action.payload;
},
clearToken(state) {
state.token = '';
}
}
})
export const {updateToken,updateInfos,clearToken} = usersSlice.actions
export default usersSlice.reducer;
// index.tsx
import { configureStore } from "@reduxjs/toolkit";
// 实现数据持久化的配置操作
import {
persistStore,
persistReducer,
FLUSH,
REHYDRATE,
PAUSE,
PERSIST,
PURGE,
REGISTER,
} from 'redux-persist'
import storage from "redux-persist/lib/storage";
import usersReducer from '../store/modules/users'
const persistConfig = {
key: 'root',
version: 1,
storage,
whitelist: ['token']
}
// blacklist 黑名单数组,可以忽略一些 reducers 中的 key。
// whitelist 白名单数组,一旦设置,其他的 key 都会被忽略。
const store = configureStore({
reducer: {
users: persistReducer(persistConfig,usersReducer)
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: {
ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
},
}),
})
// 持久化
persistStore(store)
export default store;