一、钩子函数

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 多行时可用括号包起来(标签)

  • <> </> 简写空标签
    简洁,日常开发最常用
    不支持任何属性,不能传 key
return (
  <>
    <h1>标题</h1>
    <p>内容</p>
  </>
)
  • <Fragment> 完整组件
    可以接收 key 属性
    循环渲染列表、需要给分组节点加 key 时必须用它
{list.map(item => (
  <Fragment key={item.id}>
    <span>{item.title}</span>
    <span>{item.time}</span>
  </Fragment>
))}

Fragment 用于包裹多个并列节点,不生成真实 DOM,简化dom结构;
<> </> 是 Fragment 语法糖,无法传递 key;

4.{} 识别 JavaScript中的表达式( 字符串、javaScript变量 、函数调用、JavaScript对象)

React 为什么要用 JSX?
JSX 是 React.createElement 的语法糖,把 HTML 结构写进 JS 代码,浏览器不能直接识别
能将页面 UI 结构和 JS 业务逻辑写在同一处,可读性高
贴合 React 声明式、组件化的开发理念,大幅提升开发效率
支持 JS 表达式插值,无需记忆额外模板指令

5.条件渲染:

  • 逻辑与
    在这里插入图片描述
  • 三元
    在这里插入图片描述
  • 自定义函数 + if判断语句

5.事件绑定
on 事件名称 = { 事件处理程序 },遵循驼峰命名法

在这里插入图片描述
在这里插入图片描述
传出数据用箭头函数
在这里插入图片描述
在这里插入图片描述
6.组件
一个组件就是首字母大写的函数
7.useState() 渲染、修改、收集、记录数据时使用
向组件添加一个状态变量, 从而控制影响组件的渲染结果
在这里插入图片描述
在这里插入图片描述
不能直接修改原数据
在这里插入图片描述

useState用法
管理组件局部状态,setState 更新是异步批量合并,多次更新会合并执行。
1.存入更新队列
2.判断是否开启批量更新(批量合并规则)
合成事件、生命周期、useEffect 内部:批量更新,多次 setState 合并为一次,只走一次渲染;
setTimeout、原生 DOM 事件、异步请求回调:同步执行,不批量,每次 setState 立即更新。
3.批量合并更新,计算最新 state(浅层合并)
4.生成新虚拟 DOM
5.新旧 vdom 走 Diff 算法对比,标记 DOM 差异。
6.真实 DOM 渲染
7.执行后续生命周期

8.useReducer类似于useState

// 1. 定义reducer纯函数
function countReducer(state, action) {
  switch (action.type) {
  //传参payload
    case 'add':
      return { ...state, num: state.num + action.payload }
    case 'sub':
      return { ...state, num: state.num - action.payload }
    default:
      return state
  }
}
//const [state, dispatch] = useReducer(reducer, initialState)
function Demo() {
  const [state, dispatch] = useReducer(countReducer, { num: 0 })
//2.调用dispatch({ type: '' })或dispatch({ type: '', payload:  })
  return (
    <>
      <p>{state.num}</p>
      <button onClick={() => dispatch({ type: 'add', payload: 1 })}>+1</button>
      <button onClick={() => dispatch({ type: 'sub', payload: 1 })}>-1</button>
    </>
  )
}

9.样式控制

  • 类名
    在这里插入图片描述
  • 动态控制类名 (’静态类名‘,{动态类名:条件})
    在这里插入图片描述

10.受控表单绑定(数据由state管理)
表单 value 绑定组件 state,表单值完全由 React 状态管控,变更通过 onChange 同步;
在这里插入图片描述
在这里插入图片描述

11.非受控组件(数据由dom管理)在渲染后
不绑定 state,通过 useRef 获取 DOM 原生 value,表单数据由 DOM 自身维护。
在这里插入图片描述

useRef 作用:
获取真实 DOM 元素实例;
保存跨渲染周期不变的数据、定时器 ID;
存储组件实例变量,修改不会触发组件重渲染。

维度propsstate
归属来自父组件,外部数据组件内部私有数据
读写只读,子不可修改可读可内部更新
修改方式父组件更新自身 state 重新传递setState /useState 更新
作用父子通信传值、传方法管理组件动态业务数据
可传递可以继续向下传给后代组件仅当前组件内部使用

12.组件通信

  • 父传子:在子组件标签上绑定属性 ,子组件通过props参数接收数据,props是只读对象,props.children表示子标签的内容
  • 子传父:父声明含参函数,子标签绑定;子组件中传递解构参数,调用父组件中的箭头函数并传递数据实参
    在这里插入图片描述
  • 兄弟通信:状态提升(先子传父后父传子)、Context、全局状态库
  • 跨层通信:Context、Redux/Zustand

Context:
1.使用createContext方法创建一个上下文对象Ctx
2. 在顶层组件(App)中通过 Ctx.Provider 组件提供数据value (标签)
3. 在底层组件(B)中通过 useContext(Ctx) 钩子函数获取 数据
4. value更新,所有消费组件都重渲染

  • 任意无关联组件:全局事件订阅、全局状态。

Context 作用
解决多层组件嵌套时多层透传 props 的问题,实现跨层级组件直接共享全局数据,无需每层手动传递参数。适合主题、用户信息这类全局轻量数据,不适合频繁更新、复杂业务状态。

状态提升
当多个兄弟组件需要共享同一份数据时,将共享状态提升到它们最近的共同父组件统一管理,通过 props 分发给子组件,实现兄弟通信。

复杂全局:Redux/Zustand
低频全局:Context
父子、简单跨层:props/状态提升

13.useEffect
组件渲染完毕之后就需要和服务器要数据,整个过程属于“只由渲染引起的操作”,渲染后执行
统一处理所有副作用,如接口请求、定时器、事件订阅、DOM 操作;通过依赖数组控制执行时机;
注意:useEffect 回调不能直接加 async,需在内部单独定义异步函数执行。
在这里插入图片描述
当依赖项为函数时,使用usecallback()存函数,防止无效重渲染
在这里插入图片描述

14.自定义Hook函数
use命名, 返回仍需使用的状态、方法。
可以通过解构来使用。
规则:
仅能在函数组件顶层或自定义 Hook 内部调用;
禁止在循环、条件判断、嵌套函数中使用;
保证每次渲染 Hook 调用顺序固定。(React 内部用链表存每个 Hook 的状态。按顺序存、按顺序取,所以不能在 if/for 里用,否则顺序乱了,顺序错乱会导致状态映射错误,数据就错了)
自定义 Hooks 必须以 use 开头

15.接口获取数据

  • 定义接口文件 放数据
  • 定义命令
  • 请求数据并渲染
    useEffect(()=>{async function- (){await axios.get(‘’),set-() }, -()},[ ])

16.封装和复用
子:封装负责渲染的组件 UI组件
父:当作标签使用
过程涉及通信
状态上提,视图下沉(上层管理状态,下层纯UI复用)

17.性能优化
useMemo:缓存计算结果

//只在依赖数组发生变化时,才重新执行函数计算新值;依赖不变直接复用缓存值。
const cachedValue = useMemo(() => {
  // 纯计算逻辑
  return 计算结果
}, [依赖1, 依赖2])

useCallback:缓存函数本身

const handleClick = useCallback(() => {}, [])

区别
useMemo:缓存函数计算后的结果,避免每次渲染重复执行昂贵计算;
useCallback:缓存函数引用,防止函数地址频繁变化,配合 memo 避免子组件无效重渲染。
useMemo、useCallback依赖项变化,才会执行

React.memo 是高阶组件,用于缓存函数组件,只有props发生变化时才重渲染组件,减少无效渲染;默认采用浅比较对比新旧 props。父组件传递字面量对象、匿名函数会让浅比较失效,需要 useMemo、useCallback 稳定引用。memo 只能拦截 props 变更,无法拦截自身 state、context 更新带来的重渲染。
浅比较:简单类型比较值;复杂类型比较地址

资源优化:静态资源压缩、图片懒加载、CDN 分发、合理使用浏览器缓存;
渲染优化:减少回流重绘、长列表虚拟滚动、动画使用 transform 硬件加速;
请求优化:接口防抖节流、接口缓存、合并请求;
打包优化:代码分包、路由组件懒加载、第三方依赖 CDN 外置、Tree-Shaking 剔除无用代码;
React 渲染优化:memo、useMemo、useCallback 减少无效重渲染。

18.常用
删除页面数据时,可以使用_.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 中导入组件并配置
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
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.二级路由
先创建组件,再配置

//在配置中写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.跳转

//跳转链接
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

4.路由模式

  • Hash 模式
    URL 带有 # 哈希锚点,# 后面内容不会发给后端
    无需后端配置,#后的内容不会参与 HTTP 请求,服务器只识别 #前面路径,永远匹配根目录资源。
    不利于 SEO
    静态页面用hash
  • History 模式
    必须后端配置兜底,防止刷新404
    SEO 友好

5.Switch / Routes 作用

//Switch只会渲染第一个匹配成功的路由,不会多个路由同时渲染。
//搭配 exact 实现精确匹配,避免前缀误匹配

import { Switch, Route } from 'react-router-dom'
<Switch>
  <Route exact path="/" component={Home}/>
  <Route path="/about" component={About}/>
  {/* 兜底404 */}
  <Route path="*" component={NotFound}/>
</Switch>
//<Routes>同样仅渲染匹配的单个路由
//默认精确匹配,不需要写 exact
import { Routes, Route } from 'react-router-dom'
<Routes>
  <Route path="/" element={<Home/>}/>
  <Route path="/about" element={<About/>}/>
  <Route path="*" element={<NotFound/>}/>
</Routes>

不加 Switch/Routes 会怎样?
多个 Route 满足前缀规则时同时渲染多个页面,出现页面重叠。
Switch 模糊匹配;Routes 默认精确匹配,支持嵌套路由,配合 children + <Outlet>

三、redux

1.安装 npm install @reduxjs/toolkit react-redux
2.编写子模块
store/module/userSlice.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

import { configureStore } from '@reduxjs/toolkit'
import userReducer from './slice/userSlice'

export const store = configureStore({
  reducer: {
    user: userReducer
  }
})

4.注入store

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

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、个人信息
redux平替:zustand

Redux 核心
三大核心原则:单一数据源、状态只读、纯函数修改状态;
核心四要素:Store(仓库)、Action(描述行为)、Reducer(纯函数计算新状态)、Dispatch(派发动作);
单向数据流:dispatch→action→reducer→store→视图更新。

四、打包

1.打包
默认打包:npm run build
本地预览:npm i serve -g
serve -s ./build
2.vite

{
  "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

{
  "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

Webpack 全能适合传统应用项目;打包全量编译,插件生态完善,适配各类复杂项目;冷启动速度慢,适合大型复杂业务
Vite 开发体验强、新项目首选,打包底层基于 Rollup;按需编译,冷启动、热更新极快
Rollup 专注库打包,Tree-Shaking 最优、产物干净;
esbuild 编译速度极快,多用于预构建辅助加速。
编译速度:esbuild >> Vite (dev) > Webpack
打包产物纯净度:Rollup > Vite > Webpack

4.CDN 引入第三方库(减少打包体积)
非业务js放入cdn服务器,不参与打包
Webpack CDN 外部化 externals

module.exports = {
  externals: {
    'react': 'React',
    'react-dom': 'ReactDOM'
  }
}
//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.路由懒加载

//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

// 拉取远仓到本地(首次)
git clone 远程仓库地址

git add . 
git commit -m "备注" 提交到本仓
//提交到远仓
git push 
//查看提交记录
git log

五、底层

  1. React 基础理念

声明式编程、组件化开发、单向数据流、数据驱动视图、虚拟 DOM+Diff 算法,组合优于继承。
声明式:只描述页面最终状态,无需手动操作 DOM,框架自动完成视图更新;
组件化:页面拆分成独立可复用组件,降低维护成本;
单向数据流:数据自上而下通过 props 传递,数据变更可追溯;
数据驱动视图:状态改变自动更新页面,不用手动操作 DOM;
虚拟 DOM+Diff:用 JS 对象描述 DOM,差异化更新真实 DOM;
组合优于继承:使用组件组合、Hooks 复用逻辑,摒弃复杂类继承。

  1. 虚拟 DOM & Diff 算法

虚拟 DOM 是用纯 JS 对象映射真实 DOM 结构,规避频繁 DOM 操作带来的性能损耗。

为什么不直接操作 DOM?
真实 DOM 对象属性极多,创建、修改、查询开销大;频繁操作会持续触发回流重绘。
虚拟 DOM 会批量收集变更,通过 Diff 算出最小改动,一次性局部更新 DOM,减少页面重绘重排,性能更好。

Diff 算法用于新旧虚拟 DOM 对比,高效找出差异更新真实 DOM,遵循三条规则:

  • 仅同层级节点比较,不跨层级移动 DOM;
  • 节点标签不同,直接销毁旧节点重建新节点;
  • 标签相同时对比属性、内容;列表渲染依靠 key 建立节点唯一映射,大幅提升对比效率。

React 与 Vue 的 diff 算法有何不同?
React Diff(简单双端遍历,无移动预判)
新旧列表首尾两端指针同时对比:头头、尾尾、头尾、尾头;
能复用的直接移动,无法匹配则标记为新增 / 删除;
没有计算最优移动方案,只会简单移动节点,乱序列表会产生多余 DOM 操作。
Vue3 Diff(最长递增子序列 LIS 优化)
先建立 key 与索引映射表,快速找到可复用节点;
找出不需要移动的稳定节点(最长递增子序列);
不在序列里的节点统一移动到末尾,最大化减少 DOM 移动操作;
乱序列表性能远优于 React。

  1. 触发组件重渲染
    组件自身调用 setState、useState 更新、useReducer 的 dispatch 派发动作修改内部状态;
    父组件重渲染,传递给子组件的 props 发生改变;
    组件依赖的 Context 上下文 value 更新;
    手动执行强制更新函数。
    仅修改 ref、原地修改 state 不改变引用,不会触发重新渲染。

  2. React 更新

React 更新分为 Render 渲染阶段和 Commit 执行阶段。Render 阶段可中断分片执行,只遍历 Fiber 树、执行 render 与 hooks、做 Diff 并标记 DOM 副作用,不操作真实 DOM;Commit 阶段不可中断,分三步同步执行 DOM 增删改、生命周期和 useLayoutEffect;页面真正渲染完成后才异步执行 useEffect。

React 渲染流程
初始化挂载:执行 render 生成虚拟 DOM,通过 Diff 生成真实 DOM 渲染到页面;
更新流程:state/props 发生变化,生成新虚拟 DOM,Diff 对比新旧树,仅将差异部分更新到真实 DOM。

  1. React 的 Fiber 架构
    Fiber 是 React16 之后推出的全新调度架构,简单说就是把原来递归渲染的大任务,拆分成一个个很小的执行单元,它出现的原因是老版本的渲染是同步递归的,一旦任务量大就会阻塞主线程,导致页面卡顿、用户交互没响应。Fiber 可以在处理完一个小单元后,判断有没有用户输入、动画这类高优先级任务,如果有就暂停当前渲染,先处理高优先级任务,等空闲了再恢复。它能中断的核心改造是把虚拟 DOM 从树结构改成了链表结构,每个节点都记录父节点、子节点、兄弟节点,还能保存执行进度,所以可以随时暂停、恢复和继续执行。

React 事件机制
原理:底层通过事件委托将所有事件挂载到根容器,所有子组件事件统一由顶层捕获,减少 DOM 绑定数量;节省内存。
封装统一事件对象抹平浏览器差异,内置事件对象池复用优化内存;
合成事件内部 setState 会批量异步更新;异步中使用事件对象需要调用 e.persist ()。
顺序:触发->冒泡->合成->派发->回收

  1. key 的作用
    key 作为列表节点唯一标识,Diff 时快速匹配新旧节点,精准复用 DOM,减少删除重建操作。
    不推荐用 index 下标做 key:数组增删、排序时下标错乱,会导致组件状态错乱、额外性能消耗。

  2. 高阶组件 HOC
    高阶组件是接收组件、返回全新包装组件的函数,基于装饰器模式复用公共逻辑,常用于统一处理权限校验、页面 loading、埋点、请求封装。
    装饰器模式:不修改原组件源码,通过包装一层实现功能叠加

// 装饰器:增加loading状态
function withLoading(Comp) {
  return function Wrapper(props) {
    if (props.loading) return <div>加载中...</div>
    return <Comp {...props} />
  }
}

// 原始组件,代码无改动
const Table = () => <div>表格数据</div>
// 装饰增强
const TableWithLoading = withLoading(Table)
  1. React 严格模式
    仅开发环境生效,用于代码检测:识别废弃生命周期 API、不安全副作用;会重复执行部分副作用,暴露定时器、订阅未销毁等内存泄漏问题。

  2. react-intl
    react-intl 是 React 官方国际化库,通过顶层IntlProvider注入语言包与语言标识;
    组件内可用useIntl钩子在 JS 逻辑格式化文案,或<FormattedMessage>标签直接渲染;
    支持动态插值、日期 / 数字货币格式化;
    修改 locale 即可全局切换语言,大型项目配合defineMessages统一管理文案 ID

const [locale, setLocale] = useState('zh')
  const messages = locale === 'zh' ? zh : en

  return (
    <IntlProvider locale={locale} messages={messages}>
      <App setLocale={setLocale} />
    </IntlProvider>
  )

切换语言原理:locale 状态变更,IntlProvider 下发新 messages,子组件全部更新

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐