一、axios配置
1.utils文件夹下面的:request.ts
import axios from "axios";
import type { AxiosInstance,AxiosError, AxiosRequestConfig } from 'axios'
import {message} from 'antd';
import { Console } from "console";
// 基础URL,axios将会自动拼接在url前
let baseURL = process.env.REACT_APP_API_URL;
const getAuthToken = (token: string) => `Bearer ${token}`;
// 默认请求超时时间
const timeout = 30000;
// 创建axios实例
const service:AxiosInstance = axios.create({
timeout,
baseURL,
headers: {
Accept: 'application/json',
'X-Requested-With': 'XMLHttpRequest',
},
// 如需要携带cookie 该值需设为true
// withCredentials:true
});
// 统一请求拦截 可配置自定义headers 例如 languange、token等
service.interceptors.request.use(
(config) => {
// 配置自定义请求头
// let customHeaders: AxiosRequestHeaders = {
// language:'zh-cn',
// };
// config.headers = customHeaders;
let token = localStorage.getItem('token')!
if(!token){
config.headers['authorization'] = getAuthToken(token);
}
return config
},
(error:AxiosError)=>{
console.log(error);
Promise.reject(error)
}
)
// axios 返回格式
interface axiosTypes<T>{
data:T;
status:number;
statusText:string;
}
//核心处理代码 将返回一个promise 调用then将可获取响应的业务数据
const requestHandler = <T>(method: 'get' | 'post' | 'put' | 'delete', url: string, params: object = {}, config: AxiosRequestConfig = {}): Promise<T> => {
let response: Promise<axiosTypes<any>>;
switch(method){
case 'get':
response = service.get(url, {params: { ...params }, ...config});
break;
case 'post':
response = service.post(url, {...params}, {...config});
break;
case 'put':
response = service.put(url, {...params}, {...config});
break;
case 'delete':
response = service.delete(url, {params: { ...params }, ...config});
break;
}
return new Promise<T>((resolve, reject) => {
response.then(res => {
//业务代码 可根据需求自行处理
const data = res.data;
console.log('res:',res)
if(data.errCode !== 0){
//特定状态码 处理特定的需求
// if(data.errCode == 401){
// message.warning('您的账号已登出或超时,即将登出...');
// console.log('登录异常,执行登出...');
// }
// let e = JSON.stringify(data);
// message.warning(`请求错误:${data}`);
console.log(`请求错误:${data}`)
//数据请求错误 使用reject将错误返回
reject(data);
}else{
//数据请求正确 使用resolve将结果返回
resolve(data);
}
}).catch(error => {
// let e = JSON.stringify(error);
message.warning(`网络错误:${error}`);
// console.log(`网络错误:${e}`)
console.log(`网络错误:${error}`)
reject(error);
})
})
}
// 使用 request 统一调用,包括封装的get、post、put、delete等方法
const request = {
get: <T>(url: string, params?: object, config?: AxiosRequestConfig) => requestHandler<T>('get', url, params, config),
post: <T>(url: string, params?: object, config?: AxiosRequestConfig) => requestHandler<T>('post', url, params, config),
put: <T>(url: string, params?: object, config?: AxiosRequestConfig) => requestHandler<T>('put', url, params, config),
delete: <T>(url: string, params?: object, config?: AxiosRequestConfig) => requestHandler<T>('delete', url, params, config)
};
// 导出至外层,方便统一使用
export { request };
2.api文件夹下面的list.ts接口封装例子:
import { request } from "../utils/request";
import { baseUrls } from "../utils/baseUrlConfig";
//登录
export const LoginApi = (params: any) => request.post<any>(`${baseUrls.baseUrl1}/auth/login`, params, {timeout: 15000});
export const getArticleList =(params: any) => request.post<any>(`${baseUrls.baseUrl1}/cms_admin/get_article_list`, params, {timeout: 15000});
3.将api文件夹下面封装的接口导出,index.ts
import * as list from './list';
const api = {
list,
};
export default api;
4.组件中对接口的使用
import {FC,useState} from 'react';
import { useNavigate } from 'react-router-dom';
import api from '@/api';
import md5 from "md5";
export interface LoginProps{}
export const Login:FC<LoginProps> = ()=>{
const navigate = useNavigate();
const [useName,setUseName] = useState<string>('');
const [password,setPassword] = useState<string>('');
const Login = ()=>{
api.list.LoginApi({
mobile:useName,
password:md5(password),
platform:5,
operationID:Date.now()+''
}).then(res=>{
console.log(res)
const token = res?.data?.token
localStorage.setItem('token',token)
navigate('/')
}).catch(err=>{
console.log(err)
})
}
return (
<div style={{width:'500px',height:'500px',border:'1px solid red',margin: '20px auto'}}>
<div style={{width:'300px',margin: '20px auto'}}>
<span style={{ height:'30px', width:'100px',}}>用户名:</span>
<input type="text" style={{ height:'30px', width:'200px',}} value={useName} onChange={e=>setUseName(e.target.value)}/>
</div>
<div style={{width:'300px',margin: '20px auto'}}>
<span style={{ height:'30px', width:'100px',}}>密码:</span>
<input type="password" style={{ height:'30px', width:'200px',}} value={password} onChange={e=>setPassword(e.target.value)}/>
</div>
<div style={{width:'200px',margin: '20px auto'}}><button style={{ height:'30px', width:'200px',}} onClick={Login}>登录</button></div>
</div>
)
}
二、redux配置
1.store文件夹下面的homeReducer.ts,userReducer.ts片
//homeReducer.ts
import { createSlice,PayloadAction } from "@reduxjs/toolkit";
// import { RootState } from "../Store";
interface HomeState{
count:number;
}
const initialState:HomeState={
count:0,
}
export const HomeReducer = createSlice({
name:'Home',
initialState,
reducers:{
addCount:state=>{
state.count+=1
},
changeCount:(state,action:PayloadAction<number>)=>{
state.count = state.count*action.payload
},
reduceCount:state=>{
state.count-=1;
}
}
})
export const {addCount,changeCount,reduceCount} = HomeReducer.actions;
export default HomeReducer.reducer;
//userReducer.ts
import { createSlice,PayloadAction } from "@reduxjs/toolkit";
// import { RootState } from "../Store";
interface UserState{
count:number;
}
const initialState:UserState={
count:0,
}
export const userReducer = createSlice({
name:'user',
initialState,
reducers:{
addCount:state=>{
state.count+=1
},
changeCount:(state,action:PayloadAction<number>)=>{
state.count = state.count*action.payload
},
reduceCount:state=>{
state.count-=1;
}
}
})
export const {addCount,changeCount,reduceCount} = userReducer.actions;
export default userReducer.reducer;
2.store文件夹下面的store.ts
import { configureStore } from '@reduxjs/toolkit';
import userReducer from './userReducer/userReducer';
import homeReducer from './homeReducer/homeReducer';
const store = configureStore({
reducer: {
user: userReducer,
home:homeReducer,
},
})
export default store;
// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>;
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch;
3.使用例子
//APP.tsx
function App() {
return (
<Provider store={store}>
<Router>
<Suspense>
<TestAppRouter/>
</Suspense>
</Router>
</Provider>
);
}
//组件中使用
import { useSelector,useDispatch } from "react-redux";
import { RootState } from "@/store/Store";
import {addCount,changeCount} from '@/store/userReducer/userReducer';
const dispatch = useDispatch();
const count = useSelector((state:RootState)=>state.user.count);
dispatch(addCount());dispatch(changeCount(5))
三、react-router-dom的配置
1.Router文件夹下面的TestAppRouter.ts
import {
Route,
Routes,
RoutesProps
} from "react-router-dom";
import {FC,lazy} from 'react';
const Home = lazy(()=>import('@/pages/Home'));
const Login = lazy(()=>import('@/pages/Login'));
const Regist = lazy(()=>import('@/pages/Regist'));
const Editor = lazy(()=>import('@/components/Editor'));
const NewEditor = lazy(()=>import('@/components/NewEditor'));
export interface TalkRouterProps extends RoutesProps {}
export const TestAppRouter:FC<TalkRouterProps> = (props)=>{
return (
<Routes {...props}>
<Route path="/" element={<Home/>}>
<Route path='editor' element={<Editor/>}/>
<Route path ='neweditor' element={<NewEditor/>}/>
</Route>
<Route path='/login' element={<Login/>} />
<Route path ='/register' element={<Regist/>} />
</Routes>
)
}
2.在APP.tsx中的使用
import React,{Suspense} from 'react';
import './App.css';
import { BrowserRouter as Router } from "react-router-dom";
import {TestAppRouter} from '@/Router/TestAppRouter';
import { Provider } from 'react-redux';
import store from './store/Store';
function App() {
return (
<Provider store={store}>
<Router>
<Suspense>
<TestAppRouter/>
</Suspense>
</Router>
</Provider>
);
}
export default App;
3.Outlet的使用,home.tsx
import { FC, useEffect } from "react";
import { useNavigate, Outlet } from "react-router-dom";
import { useIsPath } from "@/Func/Func";
import { useSelector,useDispatch } from "react-redux";
import { RootState } from "@/store/Store";
import {addCount,changeCount} from '@/store/userReducer/userReducer';
import api from "@/api";
export interface HomeProps {}
export const Home: FC<HomeProps> = () => {
const navigate = useNavigate();
const [, isPath] = useIsPath("/editor");
const [, isPath2] = useIsPath("/neweditor");
const dispatch = useDispatch();
const count = useSelector((state:RootState)=>state.user.count);
const getarticle = ()=>{
api.list.getArticleList({
areaCode:'CA',
operationID:"219396618",
pageNumber:1,
showNumber:10000,
status:0
}).then(res=>{
console.log(res)
}).catch(err=>{
console.log(err)
})
}
useEffect(() => {
console.log(isPath);
}, [isPath]);
return (
<div>
<div className="editor">
<div style={{ margin: "10px" }}>Home</div>
<div style={{ margin: "10px" }}>
<button
onClick={() => (!isPath ? navigate("/editor") : navigate("/"))}
>
{!isPath ? "go ediitor" : "go home"}
</button>
<button
onClick={() => (!isPath2 ? navigate("/neweditor") : navigate("/"))}
>
{!isPath2 ? "go newediitor" : "go home"}
</button>
<span style={{marginLeft:'10px',marginRight:'10px'}}>{count}</span>
<button onClick={()=>dispatch(addCount())}>add</button>
<button onClick={()=>dispatch(changeCount(5))}>change</button>
<button onClick={()=>getarticle()}>获取</button>
<button onClick={()=>navigate('/login')}>去登录页</button>
</div>
</div>
<div>
<Outlet />
</div>
</div>
);
};
四、@配置
1.yarn add @craco/craco 或者 npm install @craco/craco --save
2.修改 package.json 文件(改变启动)
"scripts": {
//"start": "react-scripts start",
// "build": "react-scripts build",
//"test": "react-scripts test",
"start": "craco start",
"build": "craco build",
"test": "craco test",
}
3.在根目录下创建craco.config.js文件(用于修改默认配置)
const path = require('path')
module.exports = {
// webpack 配置
webpack: {
// 配置别名
alias: {
// 约定:使用 @ 表示 src 文件所在路径
'@': path.resolve(__dirname, 'src'),
},
},
}
4.在tsconfig.json中配置
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"baseUrl": "src",
"paths": {
"@/*": ["*"]
}
},
"include": ["src", "craco.config.js"]
}
五、扩展redux-persist(状态永久存储,防刷新数据消失)
<1>安装:npm i @reduxjs/toolkit react-redux redux-persist;
<2>store.ts代码
import { configureStore, combineReducers } from "@reduxjs/toolkit";
import ListSlice from "./reducer/ListSlice";
import GoodSlice from "./reducer/GoodSlice";
import {
persistStore,
persistReducer,
FLUSH,
REHYDRATE,
PAUSE,
PERSIST,
PURGE,
REGISTER,
} from "redux-persist";
import storage from "redux-persist/lib/storage";
//所有的的状态片
const reducer = combineReducers({
ListSlice,
GoodSlice,
});
const persistConfig = {
key: "redux",
storage: storage,
whitelist: ["ListSlice"], //白名单--需要永久存储的片数据,其他的片段则不永久存储页面刷新则数据消失
// blacklist:['CollapsedSlice'],//黑名单仅不保存CollapsedSlice
};
const persistedRedcer = persistReducer(persistConfig, reducer);
const store = configureStore({
reducer: persistedRedcer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: {
//忽略了 Redux Persist 调度的所有操作类型。这样做是为了在浏览器控制台读取a non-serializable value was detected in the state时不会出现错误。
ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
},
}),
});
export const persistor = persistStore(store);
export default store;
export type RootState = ReturnType<typeof store.getState>;
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch;
<3>ListSlice.ts代码
import { createSlice ,PayloadAction} from "@reduxjs/toolkit";
type initialStateType={
price:number
}
const initialState:initialStateType = {
price:15
}
export const ListSlice = createSlice({
name:'ListSlice',
initialState:initialState,
reducers:{
addPrice:(state,action:PayloadAction<number>)=>{state.price=state.price+action.payload},
changePrice:(state,action:PayloadAction<number>)=>{state.price=state.price-action.payload}
}
})
// 导出actions
export const {addPrice,changePrice} = ListSlice.actions;
// 导出reducer,在创建store中使用
export default ListSlice.reducer;
<4>GoodSlice.ts代码
import {createSlice,PayloadAction} from '@reduxjs/toolkit';
type initialStateType={
name:string
}
const initialState:initialStateType={
name:'小明'
}
export const GoodSlice = createSlice({
name:'GoodSlice',
initialState,
reducers:{
changeName:(state,action:PayloadAction<string>)=>{state.name=action.payload}
}
})
// 导出actions
export const {changeName} = GoodSlice.actions;
// 导出reducer
export default GoodSlice.reducer
<5>如何使用:
import { changeName } from "@/store/reducer/GoodSlice";
import { addPrice, changePrice } from "@/store/reducer/ListSlice";
import { FC,useRef,useState,useEffect } from "react";
import { useDispatch,useSelector } from "react-redux";
import { RootState } from "@/store/store";
// import { CSSTransition } from 'react-transition-group';
interface LoginProps {}
export const Login: FC<LoginProps> = () => {
const dispatch = useDispatch();
const price = useSelector((state:RootState)=>state.ListSlice.price);
const name = useSelector((state:RootState)=>state.GoodSlice.name);
const [flag,setFlag] = useState<boolean>(true);
const ref = useRef<HTMLDivElement>(null);
const ToCheck = ()=>{setFlag(flag?false:true);dispatch(addPrice(2))};
useEffect(()=>{
if(!flag){
ref.current&&ref.current.setAttribute('class','switchs-change2');
}else{
ref.current&&ref.current.setAttribute('class','switchs-change')
}
},[flag])
useEffect(()=>{
console.log(JSON.parse(localStorage.getItem('persist:redux')!))
},[])
return (
<div>
<div onClick={()=>dispatch(changePrice(1))}>登录页面---={price}===={name}</div>
<button onClick={()=>dispatch(changeName('小李'))}>修改name</button>
<div className="switchs" onClick={ToCheck}>
<div className="switchs-change" ref={ref}>
<div className="switchs-on">ON</div>
<div className="switchs-nbsp"> </div>
<div className="switchs-off">OFF</div>
</div>
</div>
</div>
);
};