目录
一、钩子函数
1.创建React项目
npx create-react-app react-basic名
npm i -D sass
npm i antd//组件库(查看文档,根据具体需求修改)
配置别名路径@=src:配置craco.config.js 修改命令
样式初始化 npm i normalize.css
图标渲染 echarts
2.结构一样时:map方法遍历渲染列表,遍历谁给谁key

3.JS代码中编写HTML 多行时可用括号包起来(标签)
4.{} 识别 JavaScript中的表达式( 字符串、javaScript变量 、函数调用、JavaScript对象)
5.条件渲染:
- 逻辑与

- 三元

- 自定义函数 + if判断语句
5.事件绑定
on 事件名称 = { 事件处理程序 },遵循驼峰命名法


传出数据用箭头函数


6.组件
一个组件就是首字母大写的函数
7.useState() 渲染、修改、收集、记录数据时使用
向组件添加一个状态变量, 从而控制影响组件的渲染结果


不能直接修改原数据

8.样式控制
- 类名

- 动态控制类名 ('静态类名',{动态类名:条件})

9.受控表单绑定(数据由state管理)


10.获取DOM(数据由dom管理)在渲染后

11.组件通信 - 父传子:在子组件标签上绑定属性 ,子组件通过props参数接收数据,props是只读对象,props.children表示子标签的内容
- 子传父:父声明含参函数,子标签绑定;子组件中传递解构参数,调用父组件中的箭头函数并传递数据实参

- 兄弟通信:状态提升(先子传父后父传子)
- 跨层通信:
1.使用createContext方法创建一个上下文对象Ctx
-
在顶层组件(App)中通过 Ctx.Provider 组件提供数据value (标签)
-
在底层组件(B)中通过 useContext(Ctx) 钩子函数获取 数据
-
value更新,所有消费组件都重渲染
12.useEffect
组件渲染完毕之后就需要和服务器要数据,整个过程属于"只由渲染引起的操作",渲染后执行

当依赖项为函数时,使用usecallback()存函数,防止无效重渲染

13.自定义Hook函数
use命名, 返回仍需使用的状态、方法。
可以通过解构来使用。
只能在组件顶层或者其他自定义Hook函数中调用。
14.接口获取数据
- 定义接口文件 放数据
- 定义命令
- 请求数据并渲染
useEffect(()=>{async function- (){await axios.get(''),set-() }, -()}, )
15.封装和复用
子:封装负责渲染的组件 UI组件
父:当作标签使用
过程涉及通信
状态上提,视图下沉(上层管理状态,下层纯UI复用)
16.优化
useMemo:缓存计算结果
js
//只在依赖数组发生变化时,才重新执行函数计算新值;依赖不变直接复用缓存值。
const cachedValue = useMemo(() => {
// 纯计算逻辑
return 计算结果
}, [依赖1, 依赖2])
useCallback:缓存函数本身
js
const handleClick = useCallback(() => {}, [])
17.常用
删除页面数据时,可以使用_.filter(item=>{条件})过滤
tab切换: 1.setstate点击记录 2.遍历,逻辑与匹配,添加类名
点击切换:1.useState 2.绑定onClick
排序:.orderBy(数据,值,方式)
分组: .groupby(数据,条件)
发布评论:即增加数据并渲染,set-(...,{增加的数据})
随机数:uuidV4()
日期格式:dayjs().format('YYYY-MM-DD')
求和:_.reduce((a,c)=>a+c,0)
二、路由
1.框架
- npm i react-router-dom
- 子模块Pages文件夹里创建子文件在index里写组件
- Router/index.js 中导入组件并配置
js
import { createBrowserRouter } from 'react-router-dom'
import Home from './pages/Home'
import About from './pages/About'
import NotFound from './pages/NotFound'
// 配置路由表
const router = createBrowserRouter([
{
path: '/',
element: <Home />
},
{
path: '/about',
element: <About />
},
// 404兜底
{
path: '*',
element: <NotFound />
}
])
export default router
- 入口文件 index.js
js
import React from 'react'
import ReactDOM from 'react-dom/client'
import { RouterProvider } from 'react-router-dom'
import router from './router'
ReactDOM.createRoot(document.getElementById('root')).render(
<RouterProvider router={router}/>
)
2.二级路由
先创建组件,再配置
js
//在配置中写children,加上index:true
import { Outlet } from 'react-router-dom'
import Layout from './pages/Layout'
import User from './pages/User'
import UserInfo from './pages/UserInfo'
const router = createBrowserRouter([
{
path: '/user',
element: <Layout/>,
children: [
// 默认子路由 index:true
{ index: true, element: <User/> },
{ path: 'info', element: <UserInfo/> }
]
}
])
在一级组件的index里放 <Outlet /> 渲染子路由
如Layout 组件内部放<Outlet />
先创建,再配置,最后渲染
3.跳转
js
//跳转链接
import { Link } from 'react-router-dom'
// 替换历史记录(不可回退)
<Link to="/about" replace>关于</Link>
//NavLink,自带路由激活状态,导航菜单
import { NavLink } from 'react-router-dom'
<NavLink to="/user">
{({ isActive }) => (
<span style={{ color: isActive ? 'red' : '#333' }}>用户中心</span>
)}
</NavLink>
//编程式跳转
import { useNavigate } from 'react-router-dom'
const navigate = useNavigate()
navigate('/about')
//编程式组件
import { Navigate } from 'react-router-dom'
<Navigate to="/login" replace />//无登录跳登录页
// 取state参数
const location = useLocation()
const info = location.state
//获取路径 location.pathname
三、redux
1.安装 npm install @reduxjs/toolkit react-redux
2.编写子模块
store/module/userSlice.js
js
import { createSlice,createAsyncThunk } from '@reduxjs/toolkit'
import axios from 'axios'
const initialState = {
name: '',
age: 18,
info: null,
loading: false,
error: null
}
const userSlice = createSlice({
name: 'user',
initialState,
reducers: {
// 同步action
setName: (state, action) => {
state.name = action.payload
},
setAge: (state, action) => {
state.age = action.payload
},
addAge: (state) => {
state.age += 1
}
// 同步清空用户信息
clearUserInfo: (state) => {
state.info = null
state.error = null
},
// 同步修改用户名
updateName: (state, action) => {
if (state.info) state.info.name = action.payload
}
}
// 处理异步状态
extraReducers: (builder) => {
builder
.addCase(getUserInfo.pending, (state) => {
state.loading = true
})
.addCase(getUserInfo.fulfilled, (state, action) => {
state.loading = false
state.info = action.payload
})
.addCase(getUserInfo.rejected, (state, action) => {
state.loading = false
state.error = action.payload
})
}
})
// 异步请求
export const getUserInfo = createAsyncThunk(
'user/getUserInfo',
async (id, { rejectWithValue }) => {
try {
const res = await axios.get(`/api/user/${id}`)
return res.data
} catch (err) {
return rejectWithValue(err.response.data)
}
}
)
// 导出action方法
export const { setName, setAge, addAge } = userSlice.actions
export default userSlice.reducer
3.组合store/index.js
js
import { configureStore } from '@reduxjs/toolkit'
import userReducer from './slice/userSlice'
export const store = configureStore({
reducer: {
user: userReducer
}
})
4.注入store
js
import React from 'react'
import ReactDOM from 'react-dom/client'
import { Provider } from 'react-redux'
import { store } from './store'
import App from './App'
ReactDOM.createRoot(document.getElementById('root')).render(
<Provider store={store}>
<App />
</Provider>
)
5.组件中使用数据 + 派发 action
js
import { useSelector, useDispatch } from 'react-redux'
import { setName, setAge, addAge } from './store/slice/userSlice'
export default function User() {
// 获取state
const { name, age } = useSelector(state => state.user)
//使用函数
const dispatch = useDispatch()
//调用异步
const dispatch = useDispatch()
useEffect(() => {
dispatch(getUserInfo(1))
}, [dispatch])
return (
<div>
<p>姓名:{name}</p>
<p>年龄:{age}</p>
<button onClick={() => dispatch(setName('张三'))}>修改姓名</button>
<button onClick={() => dispatch(setAge(25))}>修改年龄</button>
<button onClick={() => dispatch(addAge())}>年龄+1</button>
</div>
)
}
可用devtools 调试
在很多组件中共享使用,放到redux中维护,比如token、个人信息
四、优化
1.打包
默认打包:npm run build
本地预览:npm i serve -g
serve -s ./build
2.vite
js
{
"scripts": {
"dev": "vite",
"build": "tsc && vite build", // 生产打包
"build:test": "vite build --mode test", // 测试环境打包
}
}
//包体积分析(可视化看打包大小)
npm i vite-bundle-visualizer -D
import { defineConfig } from 'vite'
import { visualizer } from 'vite-bundle-visualizer'
export default defineConfig({
plugins: [visualizer()]
})
//执行打包自动打开分析页面
//打包
npm run build
//预览
npm run preview
3.webpack
js
{
"scripts": {
"dev": "webpack serve",
"build": "webpack --mode production"
}
}
//包体积分析(可视化看包大小)
npm i webpack-bundle-analyzer -D
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
module.exports = {
plugins: [new BundleAnalyzerPlugin()]
}
//打包后自动弹出可视化图表,看各依赖占用体积。
//打包
npm run build
//预览
npx http-server dist
4.CDN 引入第三方库(减少打包体积)
非业务js放入cdn服务器,不参与打包
Webpack CDN 外部化 externals
js
module.exports = {
externals: {
'react': 'React',
'react-dom': 'ReactDOM'
}
}
html
//public/index.html 手动引入 CDN:
<script src="https://cdn.jsdelivr.net/npm/react@18/umd/react.production.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/react-dom@18/umd/react-dom.production.min.js"></script>
5.路由懒加载
js
//router/index.js中
import { lazy, Suspense } from 'react'
//lazy动态导入
const Home = lazy(() => import('./pages/Home'))
const About = lazy(() => import('./pages/About'))
const Loading = () => <div>页面加载中...</div>
//包裹组件
<Suspense fallback={<Loading />}>
<Home />
</Suspense>
6.git
js
// 拉取远仓到本地(首次)
git clone 远程仓库地址
git add .
git commit -m "备注" 提交到本仓
//提交到远仓
git push
//查看提交记录
git log
- 性能优化
资源优化:静态资源压缩、图片懒加载、CDN 分发、合理使用浏览器缓存;
渲染优化:减少回流重绘、长列表虚拟滚动、动画使用 transform 硬件加速;
请求优化:接口防抖节流、接口缓存、合并请求;
打包优化:代码分包、路由组件懒加载、第三方依赖 CDN 外置、Tree-Shaking 剔除无用代码;
React 渲染优化:memo、useMemo、useCallback 减少无效重渲染。
五、React 高频
- React 基础理念
声明式编程、组件化开发、单向数据流、数据驱动视图、虚拟 DOM+Diff 算法,组合优于继承。
声明式:只描述页面最终状态,无需手动操作 DOM,框架自动完成视图更新;
组件化:页面拆分成独立可复用组件,降低维护成本;
单向数据流:数据自上而下通过 props 传递,数据变更可追溯;
数据驱动视图:状态改变自动更新页面,不用手动操作 DOM;
虚拟 DOM+Diff:用 JS 对象描述 DOM,差异化更新真实 DOM;
组合优于继承:使用组件组合、Hooks 复用逻辑,摒弃复杂类继承。 - 虚拟 DOM & Diff 算法
虚拟 DOM 是用纯 JS 对象映射真实 DOM 结构,脱离浏览器 API,规避频繁 DOM 操作带来的性能损耗。
Diff 算法用于新旧虚拟 DOM 对比,高效找出差异更新真实 DOM,遵循三条规则:
- 仅同层级节点比较,不跨层级移动 DOM;
- 节点标签不同,直接销毁旧节点重建新节点;
- 标签相同时对比属性、内容;列表渲染依靠 key 建立节点唯一映射,大幅提升对比效率。
- key 的作用
key 作为列表节点唯一标识,Diff 时快速匹配新旧节点,精准复用 DOM,减少删除重建操作。
不推荐用 index 下标做 key:数组增删、排序时下标错乱,会导致组件状态错乱、额外性能消耗。 - 为什么不直接操作 DOM
真实 DOM 对象属性极多,创建、修改、查询开销大;频繁操作会持续触发回流重绘。
虚拟 DOM 会批量收集变更,通过 Diff 算出最小改动,一次性局部更新 DOM,减少页面重绘重排,性能更好。 - 组件通信方式
父子组件:父传子 props,子传父回调函数;
兄弟组件:状态提升至共同父组件、Context、全局状态库;
跨多层级组件:Context、Redux/Zustand 等状态管理;
任意无关联组件:全局事件订阅、全局状态。 - hooks 规则
仅能在函数组件或自定义 Hook 内部调用;
禁止在循环、条件判断、嵌套函数中使用;
保证每次渲染 Hook 调用顺序固定,依赖校验正常工作。 - useState /useEffect 用法
useState:管理组件局部状态,setState 更新是异步批量合并,多次更新会合并执行。
useEffect:统一处理所有副作用,如接口请求、定时器、事件订阅、DOM 操作;通过依赖数组控制执行时机;
注意:useEffect 回调不能直接加 async,需在内部单独定义异步函数执行。 - useRef 作用
- 获取真实 DOM 元素实例;
- 保存跨渲染周期不变的数据、定时器 ID;
- 存储组件实例变量,修改不会触发组件重渲染。
- useMemo /useCallback 区别
二者均为性能优化 Hook,都依赖依赖数组控制更新:
useMemo:缓存函数计算后的结果,避免每次渲染重复执行昂贵计算;
useCallback:缓存函数引用,防止函数地址频繁变化,配合 memo 避免子组件无效重渲染。 - 受控组件、非受控组件
受控组件:表单 value 绑定组件 state,表单值完全由 React 状态管控,变更通过 onChange 同步;
非受控组件:不绑定 state,通过 useRef 获取 DOM 原生 value,表单数据由 DOM 自身维护。 - 高阶组件 HOC
高阶组件是接收组件、返回全新包装组件的函数,基于装饰器模式复用公共逻辑,常用于统一处理权限校验、页面 loading、埋点、请求封装。 - Context 作用
实现跨层级组件数据透传,避免多层组件逐层传递 props 的 "props drilling" 问题,适合全局主题、用户信息、配置等全局数据。 - React 严格模式
仅开发环境生效,用于代码检测:识别废弃生命周期 API、不安全副作用;会重复执行部分副作用,暴露定时器、订阅未销毁等内存泄漏问题。 - props.children 和 React.Children
props.children 是组件插槽传入的子元素,类型不固定(文本、数组、组件、undefined 都有可能);
React.Children 是内置工具集,提供 map/forEach 等安全遍历方法,兼容所有 children 类型,避免类型报错。 - React 为什么要用 JSX
JSX 是 React.createElement 的语法糖,把 HTML 结构写进 JS 代码;实现 UI 与业务逻辑内聚,拥有完整 JS 语法能力,符合声明式开发,可读性更强。 - 状态提升
当多个兄弟组件需要共享同一份数据时,将共享状态提升到它们最近的共同父组件统一管理,通过 props 分发给子组件,实现兄弟通信。 - React 渲染流程
初始化挂载:执行 render 生成虚拟 DOM,通过 Diff 生成真实 DOM 渲染到页面;
更新流程:state/props 发生变化,生成新虚拟 DOM,Diff 对比新旧树,仅将差异部分更新到真实 DOM。 - Redux 核心
三大核心原则:单一数据源、状态只读、纯函数修改状态;
核心四要素:Store(仓库)、Action(描述行为)、Reducer(纯函数计算新状态)、Dispatch(派发动作);
单向数据流:dispatch→action→reducer→store→视图更新。 - 打包工具区别
Webpack:打包全量编译,插件生态完善,适配各类复杂项目;冷启动速度慢,适合大型复杂业务;
Vite:基于 ESModule 原生模块,开发环境预构建第三方依赖,按需编译,冷启动、热更新极快;打包底层基于 Rollup,新项目主流选择。