# 2503 **Repository Path**: flyird/2503 ## Basic Information - **Project Name**: 2503 - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 6 - **Created**: 2025-10-27 - **Last Updated**: 2025-11-20 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## 概览 - 第1周 - React 类组件 + Router@5.3.4 - 考试内容记账本(移动端项目) - 第2周 - React 函数组件、hooks - TS喜马拉雅 - 考试内容基本统一(移动端项目) - 第3周 - redux - 蜜雪冰城后台管理(PC端) - 第4周 - TS,蜜雪冰城后台管理(PC端) ## 相关网站 所有学习资料(上课的代码、文档): - https://gitee.com/flyird/2503 react官网: - [https://zh-hans.react.dev/](https://gitee.com/link?target=https%3A%2F%2Fzh-hans.react.dev%2F) vant组件库网站: - [https://react-vant.3lang.dev/](https://gitee.com/link?target=https%3A%2F%2Freact-vant.3lang.dev%2F) antd组件库网站: - 最新网站:[https://ant.design/index-cn](https://gitee.com/link?target=https%3A%2F%2Fant.design%2Findex-cn) - 备用网站:https://4x-ant-design.antgroup.com/index-cn redux官网: - [https://cn.redux.js.org/index.html](https://gitee.com/link?target=https%3A%2F%2Fcn.redux.js.org%2Findex.html) ## 安装react项目 > 最新版是19(2024年3月25日发布),我们学习的是react18版本。 安装命令: ``` pnpm create vite 回车 * Project name: | vite-project(这里填写项目名称,就是文件夹名称) — 回车 * Select a framework: | Vanilla | Vue | > React | Preact | Lit | Svelte | Solid | Qwik | Angular | Marko | Others — 回车 * Select a variant: | TypeScript | TypeScript + React Compiler | TypeScript + SWC | JavaScript | JavaScript + React Compiler | > JavaScript + SWC | React Router v7 ↗ | TanStack Router ↗ | RedwoodSDK ↗ | RSC ↗ — 回车 (选择No) * Use rolldown-vite (Experimental)?: | Yes | > No — 回车 (选择No) * Install with pnpm and start now? | Yes / > No — ``` **安装完项目,先不启动**。 **VScode打开项目。找到package.json文件,修改里面的 react和 react-dom为18.3.1版本,然后再 `pnpm i` 安装依赖**。 ## JSX语法 **写法** 1. 只能有一个根元素 2. 标签中嵌入JS表达式时要用 `{}` 3. 循环绑定的元素需要设置key值 4. 给jsx设置类 `class`要写成`className` 行内样式 `style={{}}` 5. `label` 标签中的`for`要写成`htmlFor` 6. 所有标签必须形成闭合,成对闭合或者自闭合都可以 7. JSX支持多行(换行),如果需要换行,需使用 () 包裹,防止bug出现 **循环**必须加key,key的值不能重复。 **定义状态**,必须放到 state 里面。只有放到state里面的数据,在数据变化后,页面才会更新。 **更新数据**: 必须使用 this.setState({ 数据名: 新值, 数据名: 新值, ......... }) 只能完整的、更新整个数据。如果是对象或数组,不能直接修改里面的某个属性。 **更新数据的步骤**: 1. 拷贝一份新的数据 `let newList = JSON.parse(JSON.stringify(this.state.list))` 2. 修改更新后的这份数据 `按照实际情况去填加、删除、修改....,去改newList` 3. 最后,使用 setState 整体改回去 `this.setState({ list: newList })` 受控组件: - 表单元素,受到数据的控制。数据改变后,表单的状态才能改变(数据没有变化,表单元素不会变) - 输入框 `value={}` - 复选框 `checked={}` - 单选框 `value={}` - 下拉框 `` - 一个表单项,加了上面的`value`或`checked`,必须配合一个 `onChange={() => {}}` ## 组件通信 ### 父传子 **父组件**:给组件填加属性即可 ```jsx ``` **子组件**:this.props.xxx 接收 ```jsx // Header.jsx 组件:

{this.props.title}

``` ### 所谓的子传父 > 父组件写好方法,将方法传递给子组件。 **父组件**: ```jsx del = (id) => { // 完成删除功能 } ``` **子组件**:接收方法,点击后调用即可 ```jsx ``` ### 上下文传参 准备Ctx对象: ```jsx import React from 'react' const Ctx = React.createContext() export default Ctx ``` 父组件: ```jsx import Ctx from '....' render() { return (
) } ``` 子组件: ```jsx import Ctx from '...' export default class List { static contextType = Ctx // 关键行。加入这行,你的 render() { return (

{this.context.title}

) } } ``` ## 路由@6 步骤1:main.jsx 中,导入 BrowserRouter,然后将App包裹起来 ```jsx import { BrowserRouter } from 'react-router-dom' createRoot(document.getElementById('root')).render( , ) ``` 步骤2:固定写到 App.jsx 中,定义基本的路由(嵌套格式是固定的:**Routes 里面 只能放 Route**) ```jsx } /> } /> } /> } /> } /> ``` 步骤3:使用NavLink 或 Link 或 **组件库**进行跳转 步骤4:如果需要编程导航跳转以及获取参数,需要使用高阶组件套起来。高阶组件写法: ```jsx import { useNavigate, useParams, useLocation } from 'react-router-dom' function withRouter(Com) { // 必须返回函数(这个函数就是下周要讲的函数组件) return function NewComponent() { // 必须在这里,这个位置。调用useXxx const navigate = useNavigate() // 相当于是Vue中的 useRouter const params = useParams() // 获取动态路由参数 const location = useLocation() // 相当于是Vue中的 useRoute return ( <> ) } } export default withRouter ``` 步骤5:使用自己的withRouter将组件包裹即可使用 `this.props.navigate('/xxx')` 和 `this.props.params.id` ```jsx import withRouter from '../hoc/withRouter' class List extends Component { render() { return ( ) } } export default withRouter(List) ``` ## 考题-案例 **可以提前安装项目,并且把需要的包安装好**。 ``` 安装项目 修改版本为 18.3.1 pnpm i pnpm i react-vant pnpm i @react-vant/icons pnpm i react-router-dom@6 pnpm i @types/react-router-dom -D ``` 准备好两个工具: - addRouter 或 withRouter,每一个子组件,都套用他 - Ctx.jsx 文件 main.jsx里面: - 导入 BrowserRouter ,将 `` 包裹起来 App.jsx中写路由、设计数据、使用上下文传递数据、增删改查方法都写到App,然后使用上下文向后传递。 后代页,在render函数的第一行输出 this ,查看一下数据、方法、路由信息,都在哪里???? - 上下文传递过来的:`this.context.list this.context.del() this.context.add()` - 路由信息 `this.props.navigate('/xxx') this.props.params.id` - 父传子,比如封装的Btns里面,获取父传子的数据 `this.props.arr this.props.active this.props.fn()` ## 第二周(ts) hook 就是 useXxxx() 这样的一堆**函数**。因为有很多hook,所以叫做 hooks 比如vue学过 useRouter() useRoute() 从第二周开始,学习函数组件: ```jsx // 类组件 class App extends Component { render () { return
ssss
} } // 函数组件 function App () { // 所有的hook,必须写到函数组件内部 const [a, setA] = useState(0) return
ssss
} ``` 统一管理数据思想。增删改查所有代码放到一个文件中。 页面使用的时候,触发这些方法即可。(和Vuex非常像)(唯一的难点) ## useState 这个hook的作用用于定义状态(数据),并且提供一个专门用于更新状态的函数。 ```jsx const [age, setAge] = useState(18) const [name, setName] = useState('zs') const [height] = useState(180) return
setAge(20)}>{age}
``` ## useMemo 作用:缓存属性。其实就是计算属性 ```jsx const total = useMemo(() => { /*return计算的结果*/ }, [依懒项, 依懒项, ....]) // 只有依懒项改变后,计算属性才会重新计算 const total = useMemo(() => { return age + height }, [age]) // 这个计算属性,依赖age的变化;只有age改变后,计算属性才会重新计算;height改变后,它不会重新计算 ``` ## 函数组件中的组件通信 ### 父传子 父组件: ```jsx ``` 子组件: ```jsx function Home (props) { // props ====== { 属性: xxx, 属性: yyy, 属性: 180 } } ``` ### 上下文传参 Ctx.jsx和之前一样 ```jsx import React from "react"; const Ctx = React.createContext() export default Ctx ``` 父组件:和之前一样 ```jsx import Ctx from '....' ``` 子组件: ```jsx import Ctx from '....' const obj = useContext(Ctx) // obj ==== { xx: 111, yy: 222 } ``` ## useReducer使用逻辑 ![image-20251105135849427](assets/image-20251105135849427-17624982790411.png) ## 第二周需要安装的包 ``` pnpm i react-vant pnpm i react-router-dom@6 pnpm i @types/react-router-dom pnpm i @react-vant/icons ``` ## 第三周 ### 安装的包: ``` 安装 ts 版本项目: pnpm i axios pnpm i @reduxjs/toolkit pnpm i react-redux pnpm i react-router-dom@6 pnpm i @types/react-router-dom -D pnpm install antd pnpm install @ant-design/icons@5.x ``` 先搭建页面+路由也可以。先把api写好,json-server启动也可以。 ### utils/http.ts ```ts import axios from "axios"; const instance = axios.create({ baseURL: 'http://localhost:3000' }) instance.interceptors.request.use((config) => { return config }, (error) => { return Promise.reject(error) }) instance.interceptors.response.use(response => { return response.data // 响应拦截器,一定要返回response.data }, (error) => { return Promise.reject(error) }) export default instance ``` ### 数据去考题中复制 略 ### 根据数据写API(api/index.ts) ```tsx import instance from "../utils/http"; // 两份数据;没份数据,需要查询、填加、修改、删除。所以一共需要8个API // 4个商品接口 // 获取商品 export const getShopAPI = (params: {}) => { return instance.get('/shoplist', { params }) } // 填加商品 export const addShopAPI = (data) => { return instance.post('/shoplist', data) } // 修改商品 export const updateShopAPI = (data) => { return instance.put(`/shoplist/${data.id}`, data) } // 删除商品 export const deleteShopAPI = (id) => { return instance.delete(`/shoplist/${id}`) } // 获取分类 export const getTypeAPI = (params: {}) => { return instance.get('/typelist', { params }) } // 填加分类 export const addTypeAPI = (data) => { return instance.post('/typelist', data) } // 修改分类 export const updateTypeAPI = (data) => { return instance.put(`/typelist/${data.id}`, data) } // 删除分类 export const deleteTypeAPI = (id) => { return instance.delete(`/typelist/${id}`) } ``` ### 路由配置 ![image-20251111090923251](assets/image-20251111090923251.png) ### 菜单调整 ```tsx import { Outlet, useNavigate, useLocation } from 'react-router-dom'; const Layout1: React.FC = () => { // 路由跳转方法 const navigate = useNavigate() // 获取地址栏的路由信息 const location = useLocation() ...... } { navigate(obj.key) // obj.key 就是下面菜单的key值,也就是跳转地址 }} items={[ { key: '/layout', icon: , label: '首页', }, { key: '2', icon: , label: '商品管理', children: [ { key: '/layout/goods', icon: , label: '商品列表', }, { key: '/layout/category', icon: , label: '分类列表', }, ] }, ]} /> ``` ## 第四周 ### 自定义hook hook,是use开头的一堆函数,因为有很多,所以叫做 hooks hook 分类的话: - 有React自带的hook(useState 、useMemo 、useEffect、useContext... ) - 路由中提供的hook(useLocation 、useNavigate 、useParams 、useRoutes) - 第三方插件中的 hook - 自定义hook (自己编写的) 使用时注意事项: - 必须用到函数组件**内部** - 必须写到顶层,不能放到if、for...里面 自定义hook: - use开头 - 导出一个函数 useXxxx - 函数里面,必须返回值 - 自定义的hook里面,能不能使用已有的hook,比如能不能使用 useState等hook呢?**随便用**。 ### useRef & useForwardRef & useImperativeHandle 获取DOM元素、获取组件实例 ```tsx import { useRef } from 'react' function App () { const pRef = useRef() const goodsRef = useRef() return (

hello

) } ``` Goods组件: - 父组件给Goods加了ref,子组件必须使用 React.forwardRef包裹起来(这是语法要求,和暴露方法不暴露方法没关系) - 要想将子组件的方法暴露 - 子组件的第2个参数,是ref - 然后使用 useImperativeHandle来暴露方法 ```jsx import React, { useImperativeHandle } from 'react' function Goods(props, ref) { const fn1 = () => { console.log('fn1') } const fn2 = () => { console.log('fn2') } // 暴露方法给父组件 useImperativeHandle(ref, () => { // return 暴露的内容 return { fn1, fn2 } }) return (
子组件
) } export default React.forwardRef(Goods) ``` ### useCallback 和 memo - useCallback用于缓存方法,第1个参数是缓存的方法,第2参数是依赖项。返回值就是缓存的方法 - 子组件使用 React.memo()将子组件包裹 父组件: ```jsx // 函数是引用类型 test = 0x11 // 父组件的变量改变了,比如value改变了,父组件就会重新渲染 // 重新渲染,就会重新创建一个test函数,虽然函数的内容没有变,但是地址变了 // 原来test=0x11 重新创建了test,test=0x22 // 下面使用useCallback 将函数缓存。React会检查这个函数“真的改变了吗” // 如果函数一样的内容,React就会认为他没有变量,就不会重新生成了 const test = useCallback(() => { console.log(123123) }, []) // 依懒项为空,表示它不依赖任何数据。所以其他数据变化,和他没有关系 ``` 子组件: ```jsx import React from 'react' // 子组件,除了初始渲染之外 // 如果React认为传递过来的props改变了,也就是传递过来的数据变了 // 他就会重新渲染 function Type(props) { console.log('Type渲染了') return (
) } // useMemo 是计算属性,缓存属性的。它和 memo 完全是两回事 export default React.memo(Type) ``` ## TS类型总结 定义两份数据的类型: - 把db.json中的数据,复制到类型中,改一下即可 回顾数组类型的定义: - `元素的类型[]` 表示一个数组的类型 API接口的类型 & 仓库的类型: - 获取数据,得到是一个**数组**。 - 删除数据,参数是 id,那么类型一定是 number - 填加、修改、参数是**一条数据**