React 高频面试题(2026版):基础+进阶+手写+项目实战全覆盖
结合最新面试趋势(React 18、Hooks 最佳实践、服务端渲染),整理了分梯度、带答题思路、附手写代码的核心面试题,覆盖入门到资深级考点,帮你精准抓住重点、高效备考。
一、基础必懂(入门级,80%面试会问)
1. React 的核心特性有哪些?
核心答案:
- 声明式编程:关注"做什么"而非"怎么做",只需描述UI状态,React 自动处理DOM更新;
- 组件化:UI拆分为独立可复用的组件,降低耦合、提升维护性;
- 单向数据流:数据从父组件流向子组件,子组件不直接修改父组件数据,保证数据可追溯;
- 虚拟DOM(VDOM):用JS对象描述DOM结构,通过diff算法最小化DOM操作;
- JSX :语法糖(
React.createElement的简写),允许在JS中写HTML结构,提升开发效率。
2. JSX 是什么?为什么不能直接运行,需要编译?
核心答案:
- JSX 是 React 的语法扩展,本质是
React.createElement(component, props, ...children)的语法糖; - 示例:
<div className="box">Hello</div>编译后 →React.createElement('div', { className: 'box' }, 'Hello'); - 不能直接运行的原因:浏览器无法识别JSX语法,需通过Babel编译为普通JS代码后才能执行。
3. React 组件的两种定义方式及区别?
| 维度 | 函数组件(Function Component) | 类组件(Class Component) |
|---|---|---|
| 写法 | 纯函数,返回JSX | 继承React.Component,通过render返回JSX |
| 状态管理 | 需用Hooks(useState/useReducer) | 用this.state/this.setState |
| 生命周期 | 需用Hooks(useEffect) | 内置生命周期方法(componentDidMount等) |
| 性能 | 更轻量,无实例开销 | 有实例开销,略重 |
| TS支持 | 更简洁,类型推断友好 | 需绑定this,类型配置复杂 |
| 适用场景 | 大部分场景(React16.8+推荐) | 老项目/需复杂生命周期场景 |
4. 简述 React 的单向数据流及优势
核心答案:
- 单向数据流:数据只能从父组件通过props传递给子组件,子组件若要修改数据,需调用父组件传递的回调函数,由父组件更新数据后再回传;
- 优势:
- 数据流向清晰,便于调试(可快速定位数据修改位置);
- 避免子组件随意修改数据导致的状态混乱;
- 组件解耦,父组件控制数据,子组件仅负责展示/交互。
5. props 和 state 的区别?
| 维度 | props | state |
|---|---|---|
| 来源 | 父组件传递 | 组件内部定义 |
| 可变性 | 只读(子组件不能修改) | 可变(通过setState/useState更新) |
| 触发更新 | 父组件修改props,子组件重新渲染 | 组件内部更新state,自身重新渲染 |
| 用途 | 组件间通信 | 组件内部状态管理 |
二、Hooks 核心(中级,高频区分度考点)
1. React Hooks 出现的背景?解决了什么问题?
核心答案:
- 背景:类组件存在的痛点------
- 逻辑复用困难(HOC/Render Props 导致嵌套地狱);
- 复杂组件逻辑分散(生命周期方法中混杂不同逻辑,如componentDidMount同时请求数据+初始化定时器);
- this指向问题(类组件方法需绑定this,易出错);
- 解决的问题:
- 用Hooks将分散的逻辑聚合,按功能组织代码;
- 函数组件支持状态管理和生命周期逻辑;
- 逻辑复用更简洁(自定义Hooks),无嵌套地狱;
- 无需处理this指向问题。
2. 常用 Hooks 及使用场景(useState/useEffect/useRef/useMemo/useCallback)
| Hook | 核心作用 | 典型使用场景 |
|---|---|---|
| useState | 函数组件声明状态 | 管理组件内部简单状态(如输入框值、弹窗显隐) |
| useEffect | 处理副作用(数据请求/定时器/事件绑定) | 组件挂载请求数据、卸载清理定时器/事件监听 |
| useRef | 保存可变值(不触发渲染) | 获取DOM元素、保存定时器ID、缓存上一次状态 |
| useMemo | 缓存计算结果(避免重复计算) | 复杂数据推导(如列表过滤排序)、减少不必要渲染 |
| useCallback | 缓存函数引用(避免函数重新创建) | 传递给子组件的回调函数(配合React.memo优化) |
关键示例:
jsx
// useEffect 副作用处理(挂载请求数据,卸载清理定时器)
useEffect(() => {
// 挂载时请求数据
const fetchData = async () => {
const res = await api.getUserList()
setList(res.data)
}
fetchData()
// 挂载时启动定时器
const timer = setInterval(() => {
setCount(prev => prev + 1)
}, 1000)
// 卸载时清理副作用
return () => {
clearInterval(timer)
}
}, []) // 空依赖:仅挂载/卸载执行
// useMemo 缓存计算结果
const filteredList = useMemo(() => {
return list.filter(item => item.age > 18)
}, [list]) // 仅list变化时重新计算
// useCallback 缓存函数
const handleClick = useCallback(() => {
setCount(prev => prev + 1)
}, []) // 依赖为空,函数引用不变
3. useEffect 的依赖项及执行规则?常见坑?
核心执行规则:
- 依赖项为空数组
[]:仅在组件挂载时执行一次,卸载时执行返回的清理函数; - 依赖项为特定值
[a, b]:组件挂载时执行,且a/b变化时重新执行,同时先执行上一次的清理函数; - 无依赖项:组件每次渲染后都执行;
常见坑:
- 遗漏依赖项:如useEffect中使用了state/props但未加入依赖,导致逻辑不生效;
- 无限循环:在useEffect中更新state,且state加入依赖,未做条件判断;
- 清理副作用不及时:如未清理定时器/事件监听,导致内存泄漏。
4. 自定义 Hooks 的定义及使用场景?
核心答案:
- 定义:以
use开头的自定义函数,封装可复用的逻辑,内部可调用内置Hooks; - 核心规则:只能在函数组件/自定义Hooks中调用,不能在普通函数/条件语句中调用;
- 典型场景:请求数据、表单校验、防抖节流、监听窗口大小等。
自定义Hook示例(useRequest.js):
jsx
// 封装通用请求逻辑
import { useState, useEffect } from 'react'
export function useRequest(api, params = []) {
const [data, setData] = useState(null)
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
const fetchData = async () => {
try {
setLoading(true)
const res = await api(...params)
setData(res.data)
} catch (e) {
setError(e)
} finally {
setLoading(false)
}
}
useEffect(() => {
fetchData()
}, params) // 参数变化重新请求
return { data, loading, error, refetch: fetchData }
}
// 组件中使用
import { useRequest } from '@/hooks/useRequest'
const { data, loading, refetch } = useRequest(api.getUserList, [1, 10])
5. React.memo、useMemo、useCallback 的区别及使用场景?
| 优化手段 | 核心作用 | 适用场景 |
|---|---|---|
| React.memo | 缓存组件,仅props变化时重新渲染 | 纯展示型子组件,props不频繁变化 |
| useMemo | 缓存计算结果,避免重复计算 | 复杂计算(如列表过滤排序) |
| useCallback | 缓存函数引用,避免函数重新创建 | 传递给子组件的回调函数(配合React.memo) |
优化示例:
jsx
// 子组件:用React.memo缓存
const Child = React.memo(({ onClick, data }) => {
return <button onClick={onClick}>{data}</button>
})
// 父组件:用useCallback缓存函数,useMemo缓存数据
const Parent = () => {
const [count, setCount] = useState(0)
// 缓存函数,避免每次渲染重新创建
const handleClick = useCallback(() => {
console.log('click')
}, [])
// 缓存数据,避免每次渲染重新计算
const data = useMemo(() => `count: ${count}`, [count])
return <Child onClick={handleClick} data={data} />
}
三、进阶核心(资深级,原理+实战考点)
1. React 虚拟DOM及diff算法原理?
虚拟DOM :用JS对象描述DOM节点的结构和属性(如 { type: 'div', props: { className: 'box' }, children: [] }),避免直接操作真实DOM(DOM操作比JS运算慢100+倍)。
diff算法核心规则(React 18):
- 同层比较:只对比同一层级的节点,不跨层级比较(如根节点div不会和子节点p比较);
- 类型优先:节点类型不同(如div vs p),直接销毁旧节点、创建新节点;类型相同则对比props;
- 列表对比 :
- 依赖key唯一标识节点,避免就地复用导致数据错乱;
- 采用"最长递增子序列"算法,最小化列表节点的移动次数;
- React 18优化:支持并发渲染,diff过程可中断,优先处理高优先级任务(如用户输入)。
关键考点:列表渲染为什么必须加key?
- 不加key:React会"就地复用"节点(列表排序/过滤后,仅更新内容不移动DOM),导致表单值错乱、动画异常;
- 禁止用index做key:列表顺序变化时index也会变,失去唯一标识作用,推荐用后端返回的唯一ID。
2. React 18 的核心新特性?
核心答案:
- 并发渲染(Concurrent Rendering):渲染过程可中断、恢复,优先处理高优先级任务(如用户输入、动画),提升交互流畅度;
- 自动批处理(Automatic Batching):将多个state更新合并为一次渲染,减少渲染次数(如Promise/定时器中的setState也会批处理);
- useTransition:标记低优先级更新(如列表筛选),避免阻塞高优先级操作(如输入框输入);
- Suspense:支持数据请求 Suspense(配合React Query/SWR),实现"加载中"占位,简化异步渲染逻辑;
- createRoot:替代ReactDOM.render,启用React 18新特性。
useTransition示例:
jsx
import { useState, useTransition } from 'react'
const Search = () => {
const [input, setInput] = useState('')
const [list, setList] = useState([])
const [isPending, startTransition] = useTransition()
const handleInput = (e) => {
// 高优先级:立即更新输入框
setInput(e.target.value)
// 低优先级:标记为过渡更新,不阻塞输入
startTransition(() => {
// 模拟复杂列表筛选
const filteredList = bigList.filter(item => item.includes(e.target.value))
setList(filteredList)
})
}
return (
<div>
<input value={input} onChange={handleInput} />
{isPending && <div>加载中...</div>}
<ul>{list.map(item => <li key={item}>{item}</li>)}</ul>
</div>
)
}
3. React 状态管理方案对比(useState/useReducer/Redux/Zustand/Jotai)
| 方案 | 核心特点 | 适用场景 |
|---|---|---|
| useState | 轻量,管理组件内部简单状态 | 单个组件/小型组件状态 |
| useReducer | 基于Redux思想,管理复杂状态逻辑 | 组件内部复杂状态(如多状态联动) |
| Redux | 全局状态,中间件丰富(Thunk/Saga) | 大型应用,需严格状态规范 |
| Zustand | 轻量全局状态,无Provider嵌套 | 中小型应用,追求简洁 |
| Jotai | 原子化状态,细粒度更新 | 需精准控制渲染的场景 |
Zustand示例(轻量全局状态):
jsx
// store.js
import { create } from 'zustand'
const useUserStore = create((set) => ({
name: '张三',
age: 20,
updateName: (newName) => set({ name: newName }),
incrementAge: () => set((state) => ({ age: state.age + 1 }))
}))
// 组件中使用
const User = () => {
const { name, age, updateName } = useUserStore()
return (
<div>
<p>{name}({age}岁)</p>
<button onClick={() => updateName('李四')}>修改姓名</button>
</div>
)
}
4. React 性能优化的核心手段?
(1)渲染优化
- 避免不必要的渲染:
- 用React.memo缓存纯组件;
- 用useCallback缓存传递给子组件的函数;
- 用useMemo缓存复杂计算结果;
- 拆分组件:将大组件拆分为小型纯组件,减少渲染范围;
- 懒加载组件:
React.lazy + Suspense按需加载组件。
(2)数据优化
- 列表虚拟滚动:用react-window/react-virtualized渲染长列表,只渲染可视区域;
- 防抖节流:搜索框输入防抖、滚动/resize事件节流;
- 缓存请求数据:用SWR/React Query缓存接口数据,避免重复请求。
(3)打包优化
- 代码分割:
React.lazy拆分代码包,减少首屏加载体积; - Tree Shaking:移除未使用的代码(需ES模块);
- CDN引入第三方库:如React、ReactDOM通过CDN引入,减少打包体积。
(4)运行时优化
- 减少DOM操作:用React状态驱动视图,避免手动操作DOM;
- 清理副作用:useEffect中及时清理定时器/事件监听,避免内存泄漏;
- 避免在渲染中创建函数/对象:如
<Child onClick={() => {}} />会导致子组件每次重新渲染。
5. React 项目如何处理跨域?
和Vue跨域方案一致,核心分两类:
(1)开发环境(本地调试)
-
配置代理(Create React App示例):
jsx// src/setupProxy.js const { createProxyMiddleware } = require('http-proxy-middleware') module.exports = function(app) { app.use( '/api', createProxyMiddleware({ target: 'http://localhost:3000', // 后端接口地址 changeOrigin: true, pathRewrite: { '^/api': '' } // 去掉/api前缀 }) ) }
(2)生产环境
- 后端配置CORS:设置
Access-Control-Allow-Origin为前端域名; - Nginx反向代理:将前端请求转发到后端,避免跨域。
四、手写题(高频压轴)
1. 实现自定义 Hook useDebounce(防抖)
jsx
import { useState, useEffect } from 'react'
// 防抖Hook:延迟执行函数,避免频繁触发
export function useDebounce(value, delay = 300) {
const [debouncedValue, setDebouncedValue] = useState(value)
useEffect(() => {
// 设置定时器,延迟更新
const timer = setTimeout(() => {
setDebouncedValue(value)
}, delay)
// 每次value变化时,清除上一次的定时器
return () => clearTimeout(timer)
}, [value, delay])
return debouncedValue
}
// 组件中使用
const Search = () => {
const [input, setInput] = useState('')
// 防抖后的输入值
const debouncedInput = useDebounce(input, 500)
// 防抖后请求接口
useEffect(() => {
if (debouncedInput) {
api.search(debouncedInput).then(res => {
setList(res.data)
})
}
}, [debouncedInput])
return <input value={input} onChange={(e) => setInput(e.target.value)} />
}
2. 实现简易版 useState Hook
jsx
// 模拟React的useState实现(简化版)
let state = [] // 存储状态
let index = 0 // 标记当前Hook的索引
function useState(initialValue) {
const currentIndex = index // 保存当前索引
// 初始化状态
state[currentIndex] = state[currentIndex] || initialValue
// 更新状态函数
const setState = (newValue) => {
// 支持函数式更新
if (typeof newValue === 'function') {
state[currentIndex] = newValue(state[currentIndex])
} else {
state[currentIndex] = newValue
}
// 模拟重新渲染(实际React会触发组件重渲染)
render()
}
index++ // 索引递增,保证多个useState顺序一致
return [state[currentIndex], setState]
}
// 模拟渲染函数
function render() {
index = 0 // 每次渲染重置索引
// 实际React会重新执行组件函数
console.log('组件重新渲染,当前state:', state)
}
// 测试
const [count, setCount] = useState(0)
setCount(1) // 输出:组件重新渲染,当前state: [1]
setCount(prev => prev + 1) // 输出:组件重新渲染,当前state: [2]
总结
核心考点回顾
- 基础层:React核心特性、JSX原理、props/state区别、组件定义方式;
- Hooks层:useState/useEffect规则、自定义Hooks、性能优化(React.memo/useMemo/useCallback);
- 进阶层:虚拟DOM/diff算法、React 18新特性、状态管理方案、性能优化;
- 实战层:跨域处理、组件懒加载、自定义Hook封装、手写核心逻辑。
答题技巧
- 基础题:先答核心定义,再补示例/使用场景;
- Hooks题:结合"副作用""缓存""复用"三个关键词展开;
- 原理题:用"通俗语言+简化代码"解释(如虚拟DOM="JS对象描述DOM");
- 项目题:围绕"性能、复用、可维护"展开,体现实战经验。