React 面试题总结
目录
相关网址
Redux
准备工作
文件规范
model.js
.jsx 引入 model.js 相关
一、核心概念
1. React 的核心思想是什么?
2. JSX 的本质是什么?
3. Virtual DOM 和 Diff 算法
4. React 事件系统的特点?
5. 受控组件 vs 非受控组件?
二、Hooks
6. useState 的工作原理?
7. useEffect 的使用规则?
8. useEffect vs useLayoutEffect?
9. useMemo 和 useCallback 的区别?
10. useRef 的用途?
11. useContext 的使用与注意事项?
12. 自定义 Hook 的规则?
三、组件设计
13. 类组件 vs 函数组件?
14. React.memo / PureComponent 的作用?
15. 高阶组件(HOC)的概念与问题?
16. children 和组合模式
四、状态管理
17. React 状态管理方案对比
18. useReducer 的使用场景?
19. Redux 核心三原则?
五、性能优化
20. React 有哪些常见的性能优化手段?
21. React 18 的并发特性?
22. key 的作用?为什么不能用 index?
六、路由
23. React Router v6 的主要变化?
七、其他高频题
24. React 的 Fiber 架构是什么?
25. React 的错误边界(Error Boundary)?
26. React Server Components(RSC)是什么?
27. React 中如何处理表单?
28. useImperativeHandle 的作用?
29. StrictMode 的作用?
30. React 19 的主要新特性?
相关网址
react : 快速入门 – React 中文文档
Ant Design : 介绍 - Ant Design
Redux
redux 是 react 开发时,一种状态管理工具,再开发时,可以将一些方法和数据包括请求之类的封装,再整个项目都可以调用
准备工作
对应依赖
dependencies:{ "umi": "^3.5.0", "umi-request": "^1.4.0", "umi-serve": "^1.9.10", "@umijs/route-utils": "^2.0.0", "dva-model-extend": "^0.1.2", "ahooks": "^3.7.2", "react": "^17.0.0", "react-dev-inspector": "^1.7.0", "react-dom": "^17.0.0", "react-fittext": "^1.0.0", "react-helmet-async": "^1.2.0", "react-router": "^4.3.1", } devDependencies:{ "@umijs/fabric": "^2.8.0", "@umijs/plugin-blocks": "^2.2.0", "@umijs/plugin-esbuild": "^1.4.0", "@umijs/plugin-openapi": "^1.3.0", "@umijs/preset-ant-design-pro": "^1.3.0", "@umijs/preset-dumi": "^1.1.0", "@umijs/preset-react": "^2.1.0", "umi-serve": "^1.9.10" }@/utils/model.js
import dvaModelExtend from 'dva-model-extend' export const createResetStoreModel = (namespace, initState, path) => { let isClear = true return { namespace, reducers: { resetStore(state, { payload = {} }) { return { ...JSON.parse(JSON.stringify(initState)), ...payload, } }, }, subscriptions: { setup({ dispatch, history }) { history.listen(() => { if (history.location.pathname !== path) { isClear = false } if (isClear) { return } if (!isClear) { dispatch({ type: 'resetStore' }) isClear = true } }) }, }, } } export const createRouteLevelModel = (namespace, initState, path, model) => { const resetModel = createResetStoreModel(namespace, initState, path) return dvaModelExtend(model, resetModel, { namespace, reducers: { changeStore(state, { payload }) { return { ...state, ...payload, } }, changeStoreDeeply(state, { payload }) { return Object.assign({}, state, payload) }, }, }) }文件规范
const.js数据存放 例如:字段富化对象,表格对应数据等
index.jsx首页 默认进入的页面
model.jsredux 相关,公共字段,接口请求和处理等
components子组件,用于放置 index.jsx 中各个组件
model.js
必须有四个变量
namespace当前路由对应的 name type = String
const namespace = 'home'resetPath当前路由对应的 path type = String
const resetPath = '/app/home'initState公共数据存储 type = Object
const initState = { dataAll :[] }model处理模块 type = Object
const model = { namespace, state: JSON.parse(JSON.stringify(initState)), effects: { *fetchList({ payload = {} }, { call, put, select }) { try { // put 调用 model 方法 yield put({ // 对应方法 默认有个 changeStore 用于更新 initState 数据 type: 'changeStoreDeeply', payload: { ...payload, isLoading: true, }, }) // select 用于获取 initState 数据 写法如下 const { searchForm } = yield select((_) => _[namespace]) const body = { ...formatParams(searchForm), ...pageParams } // call 调用对应接口请求 const { data, code, msg } = yield call(getAuthorizationList, body) if (code === 200) { yield put({ type: 'changeStore', payload: { dataAll: data.data || [], }, }) } else { throw new Error(msg) } } catch (e) { const errorMsg = e.message || e message.error(`获取审核信息列表失败${errorMsg ? `:${errorMsg}` : ''}`) } }, }, reducers: { // 可以再这对数据进一步梳理 }, }将 redux 返出
import { createRouteLevelModel } from '@/utils/model' * * * export default createRouteLevelModel(namespace, initState, resetPath, model).jsx 引入model.js 相关
import { connect } from 'umi' function Main({ inclusionConfig, dispatch }) { const { isLoading, isEditable, configList = [], resetConfigList = [], } = inclusionConfig // dispatch({ // type: 'inclusionConfig/changeStore', // payload: { // isEditable: true, // }, // }) return <div>home</div> } // inclusionConfig 就是引入的对应的 redux 可以引入多个 // Main 当前页面的组件 export default connect(({ inclusionConfig,XXX }) => ({ inclusionConfig,XXX }))(Main)一、核心概念
1. React 的核心思想是什么?
声明式 UI:描述"是什么"而非"怎么做",状态驱动视图
组件化:UI 拆分为独立、可复用的组件
单向数据流:数据自上而下(props),事件自下而上(回调)
Virtual DOM:通过 diff 算法最小化真实 DOM 操作
2. JSX 的本质是什么?
JSX 是React.createElement的语法糖,经 Babel 编译后转换为函数调用:
// JSX <div className="box"><span>{text}</span></div> // 编译后 React.createElement('div', { className: 'box' }, React.createElement('span', null, text) )JSX 不是模板语言,而是 JavaScript 表达式——可以赋值、作为参数传递、在条件/循环中使用。
3. Virtual DOM 和 Diff 算法
Virtual DOM:用 JS 对象描述 DOM 结构,更新时对比新旧虚拟树,计算最小变更再操作真实 DOM。
Diff 策略(O(n)):
同层比较,不跨层级
不同类型的元素 → 销毁重建
同类型元素 → 更新属性
列表通过
key识别节点身份
4. React 事件系统的特点?
合成事件(SyntheticEvent):跨浏览器封装,统一接口
事件委托:React 17+ 事件绑定在 root 容器(而非 document)
事件池(React 16):事件对象会被复用,异步访问需
e.persist()(React 17+ 已移除事件池)e.stopPropagation()阻止的是 React 合成事件冒泡,不影响原生事件
5. 受控组件 vs 非受控组件?
特性 | 受控组件 | 非受控组件 |
|---|---|---|
数据源 | React state | DOM 自身 |
获取值 | state 变量 | ref.current.value |
表单验证 | 实时验证 | 提交时验证 |
动态控制 | 方便 | 困难 |
推荐使用受控组件;文件输入等场景用非受控组件。
二、Hooks
6. useState 的工作原理?
const [count, setCount] = useState(0)每次渲染时,React 按调用顺序维护 hooks 链表
setCount触发重新渲染,新渲染中通过顺序获取最新值更新是异步批处理的(React 18 默认所有场景自动批处理)
函数式更新
setCount(prev => prev + 1)可获取最新值
7. useEffect 的使用规则?
useEffect(() => { // 副作用逻辑 return () => { /* 清理函数 */ } }, [dependencies])依赖数组 | 执行时机 |
|---|---|
不传 | 每次渲染后 |
| 仅挂载/卸载 |
| a 或 b 变化时 |
常见陷阱:
闭包陷值(capture stale value):依赖数组遗漏变量
无限循环:依赖中包含每次渲染都变化的引用
8. useEffect vs useLayoutEffect?
特性 | useEffect | useLayoutEffect |
|---|---|---|
执行时机 | DOM 绘制后(异步) | DOM 变更后、绘制前(同步) |
使用场景 | 数据请求、订阅、日志 | DOM 测量、防闪烁布局 |
性能影响 | 不阻塞渲染 | 阻塞渲染 |
9. useMemo 和 useCallback 的区别?
const memoizedValue = useMemo(() => expensiveCalc(a, b), [a, b]) const memoizedFn = useCallback(() => doSomething(a), [a])useMemo:缓存计算结果useCallback:缓存函数引用(等价于useMemo(() => fn, deps))
何时使用:
传递给
React.memo子组件的回调 →useCallback昂贵计算 →
useMemo不要过度优化:简单计算不需要 memo
10. useRef 的用途?
获取 DOM 元素引用:
<input ref={inputRef} />存储跨渲染的可变值(不触发重新渲染):定时器 ID、前一次的值
组件实例引用(类组件 / forwardRef)
const renderCount = useRef(0) useEffect(() => { renderCount.current++ }) // 不会触发重渲染11. useContext 的使用与注意事项?
const ThemeContext = createContext('light') // Provider <ThemeContext.Provider value={theme}> <App /> </ThemeContext.Provider> // Consumer const theme = useContext(ThemeContext)注意:Context 值变化会导致所有消费者重新渲染,即使它们只用了 context 中的部分字段。优化方式:
拆分 Context
useMemo 包裹 value
使用状态管理库(Zustand/Jotai)替代
12. 自定义 Hook 的规则?
命名必须以
use开头只在组件顶层或其他 Hook 中调用(不能在条件/循环中)
每次调用产生独立的状态(不同组件间不共享状态)
function useDebounce(value, delay) { const [debouncedValue, setDebouncedValue] = useState(value) useEffect(() => { const timer = setTimeout(() => setDebouncedValue(value), delay) return () => clearTimeout(timer) }, [value, delay]) return debouncedValue }三、组件设计
13. 类组件 vs 函数组件?
对比项 | 类组件 | 函数组件 + Hooks |
|---|---|---|
语法 | class + render | function + return |
状态 | this.state | useState |
生命周期 | 完整钩子 | useEffect 统一处理 |
this 问题 | 存在 | 不存在 |
逻辑复用 | HOC / render props | 自定义 Hook |
性能 | 实例化开销 | 更轻量 |
趋势 | 维护旧代码 | 官方推荐 |
14. React.memo / PureComponent 的作用?
React.memo:高阶组件,对函数组件的 props 做浅比较,相同则跳过渲染PureComponent:类组件版本,对 props + state 做浅比较
const Child = React.memo(({ data, onClick }) => { return <div onClick={onClick}>{data.name}</div> })注意:如果 props 中包含每次渲染都新建的对象/函数,memo 无效——需配合 useMemo/useCallback。
15. 高阶组件(HOC)的概念与问题?
HOC 是一个接收组件、返回新组件的函数,用于逻辑复用:
function withAuth(WrappedComponent) { return function(props) { const isAuth = useAuth() if (!isAuth) return <Redirect to="/login" /> return <WrappedComponent {...props} /> } }问题:props 命名冲突、嵌套地狱、静态方法丢失、ref 无法透传(需 forwardRef)。现代项目用自定义 Hook 替代。
16. children 和组合模式
// 插槽式组合(推荐) <Card> <Card.Header>Title</Card.Header> <Card.Body>{content}</Card.Body> </Card> // render props <DataFetcher url="/api/users"> {({ data, loading }) => loading ? <Spin /> : <UserList data={data} />} </DataFetcher>组合优于继承是 React 的核心设计哲学。
四、状态管理
17. React 状态管理方案对比
方案 | 特点 | 适用场景 |
|---|---|---|
useState/useReducer | 内置,局部状态 | 组件内部 |
Context | 内置,跨层级传递 | 主题、用户信息等低频更新 |
Redux/RTK | 单一 store,可预测 | 大型应用、复杂状态流 |
Zustand | 极简,无 Provider | 中小型应用 |
Jotai | 原子化 | 细粒度状态 |
React Query/SWR | 服务端状态管理 | 接口缓存、请求去重 |
18. useReducer 的使用场景?
当状态逻辑复杂(多个子值、依赖前一个状态)或需要传递 dispatch 给深层组件时:
function reducer(state, action) { switch (action.type) { case 'increment': return { count: state.count + 1 } case 'decrement': return { count: state.count - 1 } default: throw new Error() } } const [state, dispatch] = useReducer(reducer, { count: 0 })19. Redux 核心三原则?
单一数据源:整个应用状态存储在一棵对象树中
状态只读:唯一改变方式是 dispatch action
纯函数修改:reducer 是纯函数(state + action → newState)
五、性能优化
20. React 有哪些常见的性能优化手段?
避免不必要渲染:
React.memo + useCallback/useMemo
状态下沉(将状态移到真正需要的组件)
组件拆分(不变的部分抽成独立组件)
减少渲染开销:
虚拟列表(react-window / react-virtuoso)
懒加载(React.lazy + Suspense)
代码分割(动态 import)
批处理与调度:
React 18 自动批处理(减少渲染次数)
useTransition / useDeferredValue(区分优先级)
startTransition 标记非紧急更新
21. React 18 的并发特性?
Automatic Batching:所有场景(包括 setTimeout、Promise)自动批处理
useTransition:将更新标记为非紧急,可被打断
useDeferredValue:延迟更新,类似防抖但由 React 调度
Suspense for Data Fetching:配合流式 SSR
const [isPending, startTransition] = useTransition() function handleSearch(e) { // 紧急:更新输入框 setInput(e.target.value) // 非紧急:更新搜索结果 startTransition(() => { setSearchResults(filterData(e.target.value)) }) }22. key 的作用?为什么不能用 index?
key 帮助 React 识别列表元素的身份
用 index 作 key:元素增删时,index 对应关系变化 → 状态错乱、不必要的 DOM 操作
正确做法:使用数据中唯一稳定的标识符(id)
六、路由
23. React Router v6 的主要变化?
<Switch>→<Routes><Route>的component/render→element={<Component />}嵌套路由使用相对路径 +
<Outlet />useNavigate替代useHistory新增 loader/action(数据路由模式)
<Routes> <Route path="/" element={<Layout />}> <Route index element={<Home />} /> <Route path="users/:id" element={<UserDetail />} /> <Route path="*" element={<NotFound />} /> </Route> </Routes>七、其他高频题
24. React 的 Fiber 架构是什么?
Fiber 是 React 16 引入的新协调引擎:
将渲染工作拆分为小的工作单元(Fiber 节点)
支持中断/恢复:浏览器空闲时执行,有高优先级任务时暂停
实现了增量渲染,避免长时间阻塞主线程
为并发模式奠定基础
25. React 的错误边界(Error Boundary)?
用类组件的componentDidCatch或static getDerivedStateFromError捕获子组件树的渲染错误:
class ErrorBoundary extends React.Component { state = { hasError: false } static getDerivedStateFromError(error) { return { hasError: true } } componentDidCatch(error, info) { logError(error, info.componentStack) } render() { if (this.state.hasError) return <FallbackUI /> return this.props.children } }注意:不能捕获事件处理、异步代码、服务端渲染、自身的错误。函数组件目前无法直接实现错误边界。
26. React Server Components(RSC)是什么?
在服务端执行的组件,不发送 JS 到客户端
可以直接访问数据库/文件系统
与 Client Components 通过
"use client"指令区分减少客户端 JS bundle 体积
Next.js App Router 默认使用 RSC
27. React 中如何处理表单?
// 受控方式 function Form() { const [form, setForm] = useState({ name: '', email: '' }) const handleChange = (e) => { setForm(prev => ({ ...prev, [e.target.name]: e.target.value })) } return ( <form onSubmit={handleSubmit}> <input name="name" value={form.name} onChange={handleChange} /> <input name="email" value={form.email} onChange={handleChange} /> </form> ) }复杂表单推荐使用 React Hook Form 或 Formik。
28. useImperativeHandle 的作用?
配合forwardRef自定义暴露给父组件的实例方法:
const FancyInput = forwardRef((props, ref) => { const inputRef = useRef() useImperativeHandle(ref, () => ({ focus: () => inputRef.current.focus(), getValue: () => inputRef.current.value, })) return <input ref={inputRef} /> })29. StrictMode 的作用?
开发模式下帮助发现潜在问题:
双重调用渲染/Effect(检测副作用是否纯净)
警告使用废弃 API
检测意外的副作用
仅在开发环境生效,不影响生产构建。
30. React 19 的主要新特性?
Actions:表单处理新范式(useActionState、useFormStatus)
use():在组件/Hook 中读取 Promise 和 Context 的新 API
React Compiler:自动 memoization,减少手动 useMemo/useCallback
ref 作为 prop:函数组件可直接接收 ref,无需 forwardRef
Document Metadata:组件中直接渲染
<title>、<meta>等
