# react-practice **Repository Path**: pggcj/react-practice ## Basic Information - **Project Name**: react-practice - **Description**: react相关练习和插件实现练习 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2023-05-16 - **Last Updated**: 2025-04-27 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # React-Practice React 练习 ## axios 安装 axios:`npm i axios` ### 简单使用: ```js import axios from "axios"; axios.get("/userInfo").then((res) => { // ... }); ``` ### 进阶使用: ```js // 初始化 axios.js import axios from "axios"; const instance = axios.create({ baseUrl: "/", // 初始路径 timeout: 3000, // 请求超时 }); // 请求拦截 instance.interceptors.request.use( (config) => { // 请求前统一处理请求参数 /** * 在这里一般会携带前台的参数发送给后台,比如下面这段代码: * const token = getToken() * if (token) { * config.headers.token = token * } */ return config; }, (error) => { const errorMsg = error.message || "Request Error"; // ... 这里插入全局弹窗 return errorMsg; } ); // 响应拦截 instance.interceptors.response.use( (response) => { const { data } = response; // ... 可以在这里做一些状态码判断或数据处理 return data; }, (error) => { const errorMsg = error.message || "Request Error"; // ... 这里插入全局弹窗 return errorMsg; } ); ``` 以上的实例化 axios 实现了请求拦截和响应拦截,在拦截中允许对请求数据和响应数据进行处理以适配更复杂的开发环境 ### axios 多实例 #### 文件目录介绍 ```shell Project ├── index.ts 入口文件 ├── create.ts axios实例化函数封装 ├── common.ts 拦截器逻辑定义 ├── modules │ │ │ └── *** 模块目录,如 `login,goods....` │ ├── index.ts ``` #### 初始化:axios 实例化入口 ```js import createInstance from "./create"; import { baseInstanceInterceptors, shopInstanceInterceptors } from "./common"; // 默认axios实例 export const baseInstance = createInstance( { baseURL: "https://api.oioweb.cn", timeout: 3000, }, baseInstanceInterceptors.request, baseInstanceInterceptors.response ); // 商品实例 export const shopInstance = createInstance( { baseURL: "/shop", timeout: 3000, }, shopInstanceInterceptors.request, shopInstanceInterceptors.response ); // ... ``` 可以导出多个 axios 实例,根据需求修改不同实例的拦截器逻辑 #### 创建 axios 实例函数封装: ```js import axios, { AxiosRequestConfig } from "axios"; import type { AxiosResponse, InternalAxiosRequestConfig } from "axios"; type OnFulfilled = ((value: T) => T | Promise) | null; type OnRejected = (error: any) => any | null; type InterceptorsRequest = { onFulfilled: OnFulfilled, onRejected?: OnRejected, }; type InterceptorsRespone = { onFulfilled: OnFulfilled, onRejected?: OnRejected, }; type Params = { [x: string]: any, }; function createInstance( config: AxiosRequestConfig, commonInterceptorsRequest: InterceptorsRequest, commonInterceptorsResponse: InterceptorsRespone ) { const instance = axios.create(config); instance.interceptors.request.use( commonInterceptorsRequest.onFulfilled, commonInterceptorsRequest.onRejected ); instance.interceptors.response.use( commonInterceptorsResponse.onFulfilled, commonInterceptorsResponse.onRejected ); function request(method: string, url: string, params: Params = {}) { return instance({ method, url, params, }); } return { instance, request, }; } export default createInstance; ``` #### 拦截器逻辑定义: ```js // 在此定义 实例规则 import { message } from "antd"; import { InternalAxiosRequestConfig, AxiosResponse } from "axios"; export const baseInstanceInterceptors = { request: { onFulfilled: (config: InternalAxiosRequestConfig) => { return config; }, onRejected: (error: any) => { const errorMsg = error.message || "Request Error"; message.error(errorMsg); return Promise.reject(errorMsg); }, }, response: { onFulfilled: (config: AxiosResponse) => { const { data } = config; return data; }, onRejected: (error: any) => { const { response } = error; const status = response.status; let errorMsg = ""; switch (status) { case 401: errorMsg = "token 过期,请重新登录"; break; case 404: errorMsg = "请求链接错误"; break; } message.error(errorMsg); return Promise.reject(errorMsg); }, }, }; export const shopInstanceInterceptors = { request: { onFulfilled: (config: InternalAxiosRequestConfig) => { return config; }, onRejected: (error: any) => { const errorMsg = error.message || "Request Error"; message.error(errorMsg); return Promise.reject(errorMsg); }, }, response: { onFulfilled: (config: AxiosResponse) => { const { data } = config; return data; }, onRejected: (error: any) => { const { response } = error; const status = response.status; let errorMsg = ""; switch (status) { case 401: errorMsg = "token 过期,请重新登录"; break; case 404: errorMsg = "请求链接错误"; break; } message.error(errorMsg); return Promise.reject(errorMsg); }, }, }; ``` #### 模块化请求: ```js import { baseInstance } from "../../index"; const { request } = baseInstance; function OneDayEnglish() { return request("GET", "/api/common/OneDayEnglish"); } export { OneDayEnglish }; ``` #### 使用: ```js async function index() { let data = await OneDayEnglish(); //... } ``` axios 的使用和通用实例化使用到此结束,对实例化的封装的意义在于处理不同接口可能存在的不同需求 ## react-router-dom ### 简单使用: ```js import {BorwserRouter,Routes,Route,Link,Outlet} from 'react-router-dom'; // 标签形式 import { useNavigate } from "react-router-dom"; // 函数形式 const App = () =>{ const navigato = useNavigate() // 函数跳转 const gonext = () =>{ navigato("/**",{state:{name:"zhangsan"},replace:true}) // 携带参数,删除跳转前的页面 } // useParams 获取标签跳转所携带的参数 // useLocation 获取函数跳转所携带的参数 return (
index
} /> /** 等同 index} /> */ home /** 同时展示父子路由需添加Outlet组件 */}> info}> /** 嵌套路由 url = /home/info */ detail}> /** 无参数路由 */ detail?id}> /** 参数匹配路由 */ 404} /> /** 上列路由都无法匹配,则认为是非法路由,path=* 来接收非法路由 */ /** 嵌套路由 */ /** 动态路由 */ /** 会被跳转到404页面 */ ) } ``` ### 函数式创建路由 函数式需要将 BorwserRouter 绑定为项目入口文件 index.tsx 中的 App 组件的父组件 ```js import ReactDOM from 'react-dom/client'; import {useRoutes,BorwserRouter,Outlet} from "react-router-dom" const router = useRoutes([ { path:"/", element:
index
}, { path:"/home", element:
home
, children:[ { path:"info", element:
info
} ] }, { path:"/detail", element:
detail
, }, { path:"/detail/:id", element:
detail?id
, }, { path:"*", element:
404
, } ]) const root = ReactDOM.createRoot( document.getElementById('root') as HTMLElement ); root.render( {/* */} {router} ); ``` ### 路由优化 如果需要对路由进行优化,如路由守卫、KeepAlive 路由缓存,则需要对 router 数组进行处理 #### 路由守卫 ##### react 更新机制 因为更新,函数组件都会被初始化,即: ```js const [num, setNum] = useState(0); setNum(1); /** 触发更新 => const [num,setNum] = useState(1) */ ``` 如果在函数组件中使用 `const location = useLocation()` 那么每次路由跳转更新页面时都会触发这条表达式,再将`location`绑定到 useEffect 依赖中 ```js // App.tsx const location = useLocation(); const navigate = useNavigate(); useEffect(() => { routerRaurd(location, navigate, router); }, [ location, ]); /** 可以直接绑定location中的path,否则因为location是引用数据类型的关系每次的location值都不同,useEffect会一直触发 */ ``` 路由守卫逻辑 ```js // routerRaurd.ts function routerRaurd(location, navigate, router) { const { pathname } = location; const routerDetail = query(pathname, router); if (routerDetail) { // 在这里根据需求对router进行鉴权和重定向 如:if(!localStorage.get("Login-Token")){ navigate("/login",{replace:true}) } } } // 查找路由 function query(pathname, router) { for (let i = 0; i < router.length; i++) { if (pathname === router.path) { // 如果嵌套路由的子级path里带有 `/` 那么这里会匹配不到导致最终跳转404页面 return router[i]; } if (router[i].children) { return query(pathname, router[i].children); } } } ``` #### KeepAlive 路由缓存 原理:将同一个嵌套路由中的父级的属性`element`绑定为`KeepAlive`组件,那么所有被渲染的子级都会被`KeepAlive`组件处理,此时路由匹配时只需要将子级渲染到 dom 中并隐藏,如果切换路由再将对应的组件显示则可以完成一个最简单的路由缓存效果 ```js import { useEffect, useRef, useReducer } from "react"; import { useLocation, useOutlet } from "react-router-dom"; function KeepAlive() { const componentList = useRef(new Map()); // 路由映射(伪数组) const outLet = useOutlet(); // 返回子路由所匹配的组件 const { pathname } = useLocation(); const [, update] = useReducer((num) => num + 1, 0); useEffect(() => { if (!componentList.current.has(pathname)) { componentList.current.set(pathname, outLet); } update(); }, [pathname]); return (
{/* Array.form 转为数组才能使用map */} {Array.from(componentList.current).map(([key, component]) => (
{component}
))}
); } export default KeepAlive; ``` 如果对 KeepAlive 还有别的需求,可以在组件中进行定义逻辑 ## redux ### 安装 `npm i redux --save` 如果不是小型简单的 redux 项目,需要在许多页面中使用 redux,为了减少导入 那么则需要安装`react-redux` `npm i react-redux` ### 初始化 创建 store 状态存储对象和 reducer 状态处理函数 ```js // create store.js import { createStore } from "redux"; import rootReducer from "./reducers.js"; const store = createStore(rootReducer); export default store; ``` ```js // create reducers.js const initialState = { // 在redux中定义的状态 count: 0, isLoading: false, // ... }; // reducer 处理函数 function reducer(state = initialState, action) { // 根据action中的type来处理 switch (action.type) { case "ADD": return { ...state, count: state.count + 1, }; case "REMOVE": return { ...state, count: state.count - 1, }; case "UPDATE_LOADING": // 更新isLoading的状态 return { ...state, isLoading: action.payload, // 根据payload属性更新 }; } } ``` ### 使用 简单使用(不使用 react-redux) ```js import store from "./store.js"; // 查询 store.getState().count; store.getState().isLoading; // 更新 store.dispatch({ type: "ADD" }) || store.dispatch({ type: "REMOVE" }); store.dispatch({ type: "UPDATE_LOADING", payload: true }); // payload参数指定更新结果 // 监听 store.subscribe(function () {}); // 当store更新时会执行该回调函数 ``` 简单使用(使用 react-redux) 需要在 react 项目的入口文件中使用 react-redux 中的 Provider 包裹住根组件 ```JS // react入口文件index.js import { Provider } from 'react-redux'; import store from './store'; render( ) // 在页面中使用 import { useDispatch,useSelector } from "react-redux" const dispatch = useDispatch() // 查询 const count = useSelector(state=>state.count) const isLoading = useSelector(state=>state.isLoading) // 更新 dispatch({type:"ADD"}) dispatch({type:"UPDATE_LOADING",payload:true}) // 监听,监听还是使用redux中的store对象,react-redux中没有导出监听函数 store.subscribe(()=>{ console.log(1) }) ``` | Type | Description | CODEC | | :-----: | -------------- | :--------: | | AACLC | AAC | mp4a.40.2 | | HEAAC | AAC + SBR | mp4a.40.5 | | HEAACv2 | AAC + SBR + PS | mp4a.40.29 | readme 表格 https://blog.csdn.net/MacwinWin/article/details/108405415?ops_request_misc=&request_id=&biz_id=102&utm_term=readme%E8%A1%A8%E6%A0%BC&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-4-108405415.142^v87^control_2,239^v2^insert_chatgpt&spm=1018.2226.3001.4187