React 面试题
前端面试题 - React篇
本文档系统整理了React面试的高频题目,涵盖React核心概念、Hooks详解、React 18/19新特性、状态管理、性能优化、路由、服务端渲染、测试、源码原理等核心知识点。
文档概览
React在前端面试中的重要性
React作为目前最流行的前端框架之一,在各大公司的前端面试中占据重要地位。掌握React不仅是面试的必备技能,也是现代前端开发的核心能力。本文档整理的题目来源于2024-2025年真实面试场景,具有极高的参考价值。
题目统计
| 分类 | 题目数量 | 难度分布 |
|---|---|---|
| React核心概念 | 15题 | 基础10题 / 中级3题 / 高级2题 |
| Hooks详解 | 20题 | 基础6题 / 中级10题 / 高级4题 |
| React 18/19新特性 | 18题 | 基础4题 / 中级11题 / 高级3题 |
| 状态管理 | 15题 | 基础3题 / 中级11题 / 高级1题 |
| 性能优化 | 15题 | 基础2题 / 中级9题 / 高级4题 |
| React Router v6 | 12题 | 基础5题 / 中级6题 / 高级1题 |
| 服务端渲染(Next.js) | 15题 | 基础2题 / 中级9题 / 高级4题 |
| 测试 | 10题 | 基础4题 / 中级6题 |
| 源码原理 | 15题 | 高级15题 |
| 高级特性与其他 | 10题 | 基础2题 / 中级5题 / 高级3题 |
| 总计 | 145题 | 基础38题 / 中级70题 / 高级37题 |
React版本覆盖说明
本文档涵盖React 16.8+至React 19的最新特性:
- React 16.8: Hooks首次引入
- React 17: 事件委托机制变更、JSX转换优化
- React 18: 并发渲染、自动批处理、新Hooks(useId/useTransition/useDeferredValue等)
- React 19: Actions、新Hooks(useOptimistic/useActionState)、React Compiler
目录
- React核心概念
- Hooks详解
- React 18/19新特性
- 状态管理
- 性能优化
- React Router v6
- 服务端渲染(Next.js)
- 测试
- 源码原理
- 高级特性与其他
- 手写代码题精选
- 复习建议与重点题目
一、React核心概念
基础概念
1. 什么是React?它的核心特性有哪些?
难度:⭐(基础) 标签:#React基础 #核心概念
问题描述: 请介绍React是什么,以及它相比其他前端框架的核心优势。
参考答案要点:
- React是用于构建用户界面的JavaScript库,由Facebook开发和维护
- 核心特性:
- 虚拟DOM:通过JavaScript对象模拟真实DOM,减少直接操作DOM的性能开销
- 组件化:将UI拆分为独立、可复用的组件,提高代码可维护性
- 单向数据流:数据从父组件流向子组件,便于追踪数据变化
- 声明式编程:描述UI应该是什么样子,而非如何操作DOM
- JSX语法:在JavaScript中编写类似HTML的代码,提高开发效率
2. 什么是JSX?它与普通JavaScript有什么区别?
难度:⭐(基础) 标签:#JSX #语法
问题描述: 解释JSX的概念,以及它在React中的作用和编译过程。
参考答案要点:
- JSX是JavaScript的语法扩展,允许在JS中编写类似HTML的代码
- 编译过程:JSX会被Babel等工具编译为
React.createElement()调用 - 重要规则:
- 组件名必须大写开头,否则会被认为是原生DOM标签
- 必须有一个根元素包裹
- 使用
{}嵌入JavaScript表达式 class要写成className,for要写成htmlFor
代码示例:
// JSX写法
const element = <h1 className="title">Hello, React!</h1>
// 编译后
const element = React.createElement(
"h1",
{ className: "title" },
"Hello, React!"
)3. 什么是虚拟DOM?它的工作原理是什么?
难度:⭐(基础) 标签:#虚拟DOM #Diff算法
问题描述: 请解释虚拟DOM的概念、工作原理以及它带来的优势。
参考答案要点:
- 虚拟DOM是真实DOM的JavaScript对象表示,是轻量级的副本
- 工作原理:
- 数据变化时创建新的虚拟DOM树
- 通过Diff算法比较新旧虚拟DOM的差异
- 只更新变化的部分到真实DOM(Patch操作)
- 优势:
- 减少DOM操作次数,提高性能
- 跨平台能力(React Native)
- 声明式编程,开发者无需手动操作DOM
4. 类组件和函数组件有什么区别?
难度:⭐(基础) 标签:#组件 #类组件 #函数组件
问题描述: 比较React中的类组件和函数组件,说明各自的优缺点。
参考答案要点:
| 特性 | 类组件 | 函数组件 |
|---|---|---|
| 定义方式 | ES6 Class | 普通函数 |
| 状态管理 | this.state | useState Hook |
| 生命周期 | 有完整生命周期方法 | useEffect模拟 |
| this指向 | 需要处理this绑定 | 无this,更简洁 |
| 代码量 | 相对较多 | 更简洁 |
| 测试难度 | 较难 | 更容易测试 |
| 逻辑复用 | HOC/Render Props | 自定义Hooks |
推荐:新项目优先使用函数组件 + Hooks
5. 什么是Props和State?它们有什么区别?
难度:⭐(基础) 标签:#Props #State #数据流
问题描述: 解释Props和State的概念,以及它们在React组件中的作用和区别。
参考答案要点:
| 特性 | Props | State |
|---|---|---|
| 数据来源 | 父组件传递 | 组件内部管理 |
| 可变性 | 只读(Read-only) | 可变(通过setState) |
| 用途 | 组件间通信 | 组件内部状态管理 |
| 更新影响 | 触发子组件重新渲染 | 触发组件重新渲染 |
| 默认值 | 可设置defaultProps | 可在构造函数/useState中设置 |
核心原则:Props向下传递,State内部管理
6. 什么是受控组件和非受控组件?
难度:⭐(基础) 标签:#表单 #受控组件 #非受控组件
问题描述: 解释受控组件和非受控组件的概念,以及各自的使用场景。
参考答案要点:
- 受控组件:表单数据由React组件的state管理,通过onChange事件更新
- 优点:便于验证、测试、与其他UI状态同步
- 缺点:代码量稍多
- 非受控组件:表单数据由DOM自身管理,通过ref获取值
- 优点:代码简洁,类似传统HTML表单
- 缺点:难以进行实时验证和状态同步
代码示例:
// 受控组件
function ControlledInput() {
const [value, setValue] = useState("")
return <input value={value} onChange={(e) => setValue(e.target.value)} />
}
// 非受控组件
function UncontrolledInput() {
const inputRef = useRef(null)
const handleSubmit = () => {
console.log(inputRef.current.value)
}
return <input ref={inputRef} />
}7. React组件间通信的方式有哪些?
难度:⭐(基础) 标签:#组件通信 #Props #Context
问题描述: 列举React中组件间通信的各种方式,并说明各自的适用场景。
参考答案要点:
- 父子组件通信:Props传递(父→子)、回调函数(子→父)
- 兄弟组件通信:状态提升到共同父组件
- 跨层级通信:Context API
- 全局状态管理:Redux、Zustand、Jotai等
- 发布订阅模式:Event Bus
- URL参数:路由传参
8. React中key的作用是什么?
难度:⭐(基础) 标签:#Key #列表渲染 #Diff算法
问题描述: 解释React中key属性的作用,以及为什么不应该使用数组索引作为key。
参考答案要点:
- 作用:
- 帮助React识别哪些元素改变了、被添加或被删除
- 在Diff算法中用于判断元素是否可以复用
- 提高列表渲染性能
- 为什么不建议用索引作为key:
- 列表项顺序变化时会导致不必要的重新渲染
- 可能产生状态错乱问题
- 删除/插入元素时性能差
- 最佳实践:使用唯一稳定的ID作为key
组件与生命周期
9. React生命周期方法有哪些?
难度:⭐⭐(中级) 标签:#生命周期 #类组件
问题描述: 详细介绍React类组件的生命周期方法及其执行时机。
参考答案要点:
挂载阶段:
constructor:初始化state和绑定方法static getDerivedStateFromProps:根据props更新staterender:渲染UI(必须实现)componentDidMount:组件挂载后,适合发起网络请求、订阅
更新阶段:
static getDerivedStateFromPropsshouldComponentUpdate:控制是否重新渲染,用于性能优化rendergetSnapshotBeforeUpdate:获取更新前DOM快照componentDidUpdate:组件更新后
卸载阶段:
componentWillUnmount:清理工作(定时器、订阅、事件监听等)
错误处理:
static getDerivedStateFromError:渲染备用UIcomponentDidCatch:记录错误信息
10. setState是同步还是异步的?
难度:⭐⭐(中级) 标签:#setState #异步更新 #批处理
问题描述: 解释setState的同步/异步行为,以及React 18的自动批处理特性。
参考答案要点:
- React 17及之前:
- React事件处理函数中:异步执行,批量更新
- setTimeout/Promise中:同步执行
- React 18:
- 自动批处理(Automatic Batching):所有更新都批量处理
- 包括Promise、setTimeout、原生事件中的更新
- 强制同步更新:使用
flushSync
代码示例:
import { flushSync } from "react-dom"
// React 18自动批处理
setTimeout(() => {
setCount((c) => c + 1) // 不会立即更新
setFlag((f) => !f) // 合并为一次更新
}, 1000)
// 强制同步
flushSync(() => {
setCount((c) => c + 1) // 立即更新
})11. 什么是React的错误边界(Error Boundary)?
难度:⭐⭐(中级) 标签:#错误边界 #异常处理
问题描述: 解释错误边界的概念、实现方式以及局限性。
参考答案要点:
- 实现方式:使用
static getDerivedStateFromError或componentDidCatch - 作用:捕获子组件树的JavaScript错误,显示降级UI
- 能捕获的错误:
- 渲染期错误
- 生命周期方法中的错误
- 构造函数中的错误
- 不能捕获的错误:
- 事件处理函数中的错误
- 异步代码中的错误
- 服务端渲染的错误
- 错误边界自身抛出的错误
代码示例:
class ErrorBoundary extends React.Component {
state = { hasError: false }
static getDerivedStateFromError(error) {
return { hasError: true }
}
componentDidCatch(error, errorInfo) {
console.error("Error:", error, errorInfo)
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong.</h1>
}
return this.props.children
}
}12. 什么是React Portal?
难度:⭐⭐(中级) 标签:#Portal #DOM操作
问题描述: 解释React Portal的概念和使用场景。
参考答案要点:
- Portal可以将子节点渲染到父组件以外的DOM节点
- 使用
ReactDOM.createPortal(child, container) - 事件冒泡:仍然按照React组件树进行,而非DOM树
- 常见用途:模态框、提示框、下拉菜单等需要脱离父组件布局的场景
代码示例:
function Modal({ children, onClose }) {
return ReactDOM.createPortal(
<div className="modal-overlay" onClick={onClose}>
<div className="modal-content" onClick={(e) => e.stopPropagation()}>
{children}
</div>
</div>,
document.getElementById("modal-root") // 渲染到body下的modal-root
)
}事件与Diff算法
13. 什么是React的合成事件系统?
难度:⭐⭐(中级) 标签:#合成事件 #事件委托
问题描述: 解释React合成事件系统的实现原理和特点。
参考答案要点:
- React使用
SyntheticEvent封装原生事件,提供跨浏览器一致性 - 事件委托机制:事件监听器挂载在root容器(React 17+),而非document
- 事件池化(React 17前):合成事件对象会被复用,异步访问需要使用
e.persist() - 执行顺序:原生事件先执行,合成事件后执行
- 优势:减少内存占用、跨浏览器兼容、便于统一管理
14. 为什么React元素有$$typeof属性?
难度:⭐⭐⭐(高级) 标签:#安全 #XSS #React元素
问题描述: 解释React元素中$$typeof属性的作用和意义。
参考答案要点:
- 用于防止XSS攻击
$$typeof是一个Symbol类型(Symbol.for('react.element'))- 安全机制:
- Symbol类型无法被JSON序列化
- 阻止服务器JSON注入攻击
- 确保元素是通过
React.createElement创建的合法元素
- 如果服务器返回的JSON中包含恶意
{ type: 'script', props: { src: 'evil.js' } },由于缺少$$typeof,React会拒绝渲染
15. 什么是React的Diff算法?
难度:⭐⭐⭐(高级) 标签:#Diff算法 #虚拟DOM #Fiber
问题描述: 详细解释React Diff算法的实现原理和优化策略。
参考答案要点:
- 时间复杂度优化:从O(n³)优化到O(n)
- 核心策略:
- 同级比较:只比较同一层级的节点
- 类型不同则销毁重建:type不同直接替换整个子树
- 使用key优化列表:通过key识别元素身份
- Diff过程:
- 单节点Diff:比较type和key
- 多节点Diff:两轮遍历
- 第一轮:复用相同位置的节点
- 第二轮:处理移动、新增、删除
二、Hooks详解
基础Hooks
1. 什么是React Hooks?为什么要引入Hooks?
难度:⭐(基础) 标签:#Hooks #函数组件
问题描述: 介绍React Hooks的概念,以及它解决了类组件的哪些问题。
参考答案要点:
- Hooks是React 16.8引入的特性,让函数组件可以使用状态和生命周期
- 解决的问题:
- 类组件的this指向问题
- 逻辑复用困难(HOC嵌套地狱)
- 生命周期方法逻辑分散
- 大型类组件难以拆分和维护
- 优势:代码更简洁、更易于测试和复用、逻辑聚合
2. useState的工作原理是什么?
难度:⭐(基础) 标签:#useState #状态管理
问题描述: 解释useState的工作原理和使用注意事项。
参考答案要点:
- 返回
[state, setState]元组 - 实现原理:基于闭包和链表实现状态存储
- 重要规则:
- 每次渲染按顺序调用Hooks,不能放在条件语句中
- 函数式更新:
setState(prev => prev + 1),适用于依赖旧状态的场景 - 初始值可以是函数,避免重复计算
代码示例:
function Counter() {
const [count, setCount] = useState(0)
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
<button onClick={() => setCount((c) => c + 1)}>函数式+1</button>
</div>
)
}3. useEffect的依赖项数组有什么作用?
难度:⭐(基础) 标签:#useEffect #副作用 #依赖项
问题描述: 详细解释useEffect依赖项数组的不同写法及其效果。
参考答案要点:
| 依赖项写法 | 执行时机 |
|---|---|
| 不写依赖项 | 每次渲染后都执行 |
空数组[] | 只在挂载时执行,卸载时清理 |
有依赖项[a, b] | 挂载时执行,依赖变化时执行 |
- 返回的清理函数在卸载或重新执行前调用
- 依赖项必须包含所有在effect中使用的响应式值
代码示例:
useEffect(() => {
console.log("只在挂载时执行")
return () => console.log("卸载时清理")
}, [])
useEffect(() => {
console.log("count变化时执行:", count)
}, [count])4. useRef的作用是什么?
难度:⭐(基础) 标签:#useRef #DOM引用
问题描述: 解释useRef的多种使用场景。
参考答案要点:
- 获取DOM节点引用:操作原生DOM元素
- 保存可变值:存储不触发重新渲染的值
- 特性:
- 每次渲染返回同一ref对象
- 修改
ref.current不会导致重渲染 - 适合存储定时器ID、上一轮的props/state等
代码示例:
function Timer() {
const intervalRef = useRef(null)
const inputRef = useRef(null)
useEffect(() => {
intervalRef.current = setInterval(() => {}, 1000)
return () => clearInterval(intervalRef.current)
}, [])
const focusInput = () => {
inputRef.current.focus()
}
return <input ref={inputRef} />
}5. useContext如何使用?
难度:⭐(基础) 标签:#useContext #跨组件通信
问题描述: 介绍useContext的使用方法和注意事项。
参考答案要点:
- 接收Context对象,返回当前context值
- 组件会在context值变化时重新渲染
- 配合
React.createContext和Provider使用 - 可用于跨层级传递数据,避免prop drilling
代码示例:
const ThemeContext = React.createContext("light")
function App() {
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
)
}
function Toolbar() {
const theme = useContext(ThemeContext)
return <div className={theme}>Toolbar</div>
}6. useId的作用是什么?
难度:⭐(基础) 标签:#useId #React18 #无障碍
问题描述: 介绍React 18新增的useId Hook的作用和使用场景。
参考答案要点:
- React 18新增Hook
- 生成唯一的ID,用于无障碍性支持(如label和input的关联)
- 服务端渲染支持:在服务端和客户端生成相同的ID,避免hydration不匹配
- 返回的ID包含
:字符,确保唯一性
代码示例:
function PasswordField() {
const passwordId = useId()
return (
<>
<label htmlFor={passwordId}>Password:</label>
<input id={passwordId} type="password" />
</>
)
}进阶Hooks
7. useEffect和useLayoutEffect有什么区别?
难度:⭐⭐(中级) 标签:#useEffect #useLayoutEffect #执行时机
问题描述: 比较useEffect和useLayoutEffect的执行时机和使用场景。
参考答案要点:
| 特性 | useEffect | useLayoutEffect |
|---|---|---|
| 执行时机 | 异步,浏览器绘制后 | 同步,DOM更新后、绘制前 |
| 阻塞渲染 | 否 | 是 |
| SSR支持 | 正常 | 会警告(需用useEffect) |
| 用途 | 大多数副作用 | DOM测量和同步修改 |
- useLayoutEffect可能阻塞视觉更新,谨慎使用
- 用于测量DOM布局、防止视觉闪烁的场景
8. 什么是useMemo和useCallback?
难度:⭐⭐(中级) 标签:#useMemo #useCallback #性能优化
问题描述: 解释useMemo和useCallback的作用和使用场景。
参考答案要点:
- useMemo:缓存计算结果,依赖不变返回缓存值
- useCallback:缓存函数引用,依赖不变返回同一函数
- 用途:优化子组件渲染,避免不必要的重渲染
- 注意事项:不要滥用,缓存本身也有开销
代码示例:
function Parent() {
const [count, setCount] = useState(0)
const [text, setText] = useState("")
// 只有count变化时才重新计算
const expensiveValue = useMemo(() => {
return heavyComputation(count)
}, [count])
// 只有count变化时才创建新函数
const handleClick = useCallback(() => {
console.log(count)
}, [count])
return <Child onClick={handleClick} value={expensiveValue} />
}9. 什么是forwardRef?
难度:⭐⭐(中级) 标签:#forwardRef #Ref转发
问题描述: 解释forwardRef的作用和使用场景。
参考答案要点:
- 用于将ref转发给子组件
- 函数组件默认不能接受ref,需要使用forwardRef包裹
- 高阶组件中需要使用forwardRef转发ref
代码示例:
const FancyInput = forwardRef((props, ref) => {
return <input ref={ref} className="fancy-input" />
})
function Parent() {
const inputRef = useRef(null)
return <FancyInput ref={inputRef} />
}10. useImperativeHandle的作用是什么?
难度:⭐⭐(中级) 标签:#useImperativeHandle #命令式API
问题描述: 解释useImperativeHandle的作用和使用场景。
参考答案要点:
- 自定义暴露给父组件的实例值
- 配合forwardRef使用
- 隐藏组件内部实现细节
- 用于命令式操作子组件(如聚焦、播放等)
代码示例:
const FancyInput = forwardRef((props, ref) => {
const inputRef = useRef(null)
useImperativeHandle(ref, () => ({
focus: () => inputRef.current.focus(),
clear: () => {
inputRef.current.value = ""
},
}))
return <input ref={inputRef} />
})11. useReducer适合什么场景?
难度:⭐⭐(中级) 标签:#useReducer #状态管理
问题描述: 介绍useReducer的适用场景和使用方法。
参考答案要点:
- 适用于复杂状态逻辑,多个子值相关
- 下一个状态依赖于之前的状态
- 需要复用状态逻辑
- 类似Redux的工作方式
代码示例:
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()
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, { count: 0 })
return (
<>
Count: {state.count}
<button onClick={() => dispatch({ type: "increment" })}>+</button>
<button onClick={() => dispatch({ type: "decrement" })}>-</button>
</>
)
}12. useSyncExternalStore的作用是什么?
难度:⭐⭐(中级) 标签:#useSyncExternalStore #React18 #外部数据
问题描述: 介绍React 18新增的useSyncExternalStore Hook。
参考答案要点:
- React 18新增Hook
- 用于订阅外部数据源(如浏览器API、第三方状态管理库)
- 支持服务端渲染
- 第三方状态管理库常用(如Zustand、Redux)
代码示例:
function useOnlineStatus() {
return useSyncExternalStore(
(callback) => {
window.addEventListener("online", callback)
window.addEventListener("offline", callback)
return () => {
window.removeEventListener("online", callback)
window.removeEventListener("offline", callback)
}
},
() => navigator.onLine, // 客户端获取
() => true // 服务端默认值
)
}自定义Hooks
13. 什么是自定义Hook?
难度:⭐⭐(中级) 标签:#自定义Hook #逻辑复用
问题描述: 介绍自定义Hook的概念和编写规范。
参考答案要点:
- 以
use开头的函数,可以调用其他Hooks - 用于复用状态逻辑,而非UI
- 每次调用有独立的state和effect
- 常见例子:useWindowSize、useFetch、useLocalStorage
代码示例:
function useWindowSize() {
const [size, setSize] = useState({ width: 0, height: 0 })
useEffect(() => {
const handleResize = () => {
setSize({ width: window.innerWidth, height: window.innerHeight })
}
window.addEventListener("resize", handleResize)
handleResize()
return () => window.removeEventListener("resize", handleResize)
}, [])
return size
}14. 如何实现一个自定义的useDebounce Hook?
难度:⭐⭐(中级) 标签:#自定义Hook #防抖
问题描述: 实现一个防抖Hook,用于延迟更新值。
参考答案要点:
- 使用useState存储防抖后的值
- 使用useEffect设置定时器
- 清理函数清除之前的定时器
代码示例:
function useDebounce(value, delay) {
const [debouncedValue, setDebouncedValue] = useState(value)
useEffect(() => {
const timer = setTimeout(() => setDebouncedValue(value), delay)
return () => clearTimeout(timer)
}, [value, delay])
return debouncedValue
}
// 使用
function SearchInput() {
const [value, setValue] = useState("")
const debouncedValue = useDebounce(value, 500)
useEffect(() => {
// 500ms后才执行搜索
searchAPI(debouncedValue)
}, [debouncedValue])
return <input value={value} onChange={(e) => setValue(e.target.value)} />
}15. 如何用Hooks模拟生命周期?
难度:⭐⭐(中级) 标签:#Hooks #生命周期
问题描述: 使用Hooks模拟类组件的各个生命周期方法。
参考答案要点:
| 生命周期 | Hooks模拟方式 |
|---|---|
| componentDidMount | useEffect(() => {}, []) |
| componentDidUpdate | useEffect(() => {}, [deps]) |
| componentWillUnmount | useEffect(() => { return () => {} }, []) |
| getSnapshotBeforeUpdate | 无直接对应,可用useLayoutEffect近似 |
Hooks原理
16. Hooks的调用规则有哪些?
难度:⭐(基础) 标签:#Hooks规则 #ESLint
问题描述: 介绍Hooks的调用规则及其原因。
参考答案要点:
- 只在最顶层调用Hooks:不要在循环、条件或嵌套函数中调用
- 只在React函数组件或自定义Hooks中调用
- ESLint插件:
eslint-plugin-react-hooks可强制执行规则
原因:React通过调用顺序维护Hooks状态链表,条件调用会导致顺序不一致
17. 为什么不能在条件语句中使用Hooks?
难度:⭐⭐⭐(高级) 标签:#Hooks原理 #链表
问题描述: 深入解释为什么Hooks不能在条件语句中使用。
参考答案要点:
- React通过数组/链表存储Hooks状态
- 每个Hook对应一个节点,通过next指针连接
- 渲染时按调用顺序访问链表
- 条件调用会导致Hooks顺序错乱,造成状态混乱或报错
代码示例:
// ❌ 错误
if (condition) {
useEffect(() => {}, [])
}
// ✅ 正确
useEffect(() => {
if (condition) {
// 逻辑放在effect内部
}
}, [])18. 什么是Hooks的闭包陷阱?如何解决?
难度:⭐⭐⭐(高级) 标签:#闭包陷阱 #依赖项
问题描述: 解释Hooks中的闭包陷阱问题及其解决方案。
参考答案要点:
- 问题:useEffect/useCallback中引用的state是旧值
- 原因:闭包捕获了渲染时的state值
- 解决方案:
- 添加正确的依赖项
- 使用函数式更新
setState(prev => ...) - 使用useRef保存最新值
- 使用eslint-plugin-react-hooks检查依赖
代码示例:
// ❌ 闭包陷阱
useEffect(() => {
const timer = setInterval(() => {
console.log(count) // 永远是初始值
}, 1000)
return () => clearInterval(timer)
}, []) // count不在依赖项中
// ✅ 解决方案1:添加依赖
useEffect(() => {
const timer = setInterval(() => {
console.log(count)
}, 1000)
return () => clearInterval(timer)
}, [count])
// ✅ 解决方案2:使用函数式更新
useEffect(() => {
const timer = setInterval(() => {
setCount((c) => c + 1) // 不依赖外部count
}, 1000)
return () => clearInterval(timer)
}, [])19. useInsertionEffect的作用是什么?
难度:⭐⭐⭐(高级) 标签:#useInsertionEffect #CSS-in-JS
问题描述: 介绍React 18新增的useInsertionEffect Hook。
参考答案要点:
- React 18新增Hook
- 在DOM插入后立即同步执行
- 主要用于CSS-in-JS库注入样式
- 在所有DOM变更之前执行,比useLayoutEffect更早
20. useState返回的是数组而不是对象?
难度:⭐⭐(中级) 标签:#useState #解构赋值
问题描述: 解释为什么useState返回数组而不是对象。
参考答案要点:
- 数组解构可以自定义变量名:
const [count, setCount] = useState(0) - 对象解构需要保持属性名一致
- 数组解构更灵活,使用更方便
- 多次调用useState时,数组解构可以任意命名
三、React 18/19新特性
React 18核心特性
1. React 18有哪些重要更新?
难度:⭐(基础) 标签:#React18 #新特性
问题描述: 概述React 18的重要更新内容。
参考答案要点:
- 并发渲染(Concurrent Rendering):允许React中断渲染,优先处理高优先级任务
- 自动批处理(Automatic Batching):默认对所有更新进行批处理
- 新的Root API:
createRoot替代ReactDOM.render - Suspense支持SSR:服务端渲染支持Suspense
- 新增Hooks:useId、useTransition、useDeferredValue、useSyncExternalStore、useInsertionEffect
2. 什么是并发渲染(Concurrent Rendering)?
难度:⭐⭐(中级) 标签:#并发渲染 #Fiber
问题描述: 详细解释并发渲染的概念和实现原理。
参考答案要点:
- 允许React中断渲染,优先处理高优先级任务
- 基于Fiber架构和时间切片实现
- 用户交互(如输入)优先级高于列表渲染
- 需要配合新API(useTransition、useDeferredValue)使用
- 提高UI响应性,避免卡顿
3. 什么是自动批处理?
难度:⭐(基础) 标签:#自动批处理 #性能优化
问题描述: 解释React 18的自动批处理特性。
参考答案要点:
- React 18默认对所有更新进行批处理
- 包括Promise、setTimeout、原生事件中的更新
- 减少不必要的重新渲染
- 使用
flushSync可强制同步更新
代码示例:
// React 18自动批处理
setTimeout(() => {
setCount((c) => c + 1)
setFlag((f) => !f)
// 合并为一次重新渲染
}, 1000)4. React 18的createRoot和React 17的render有什么区别?
难度:⭐(基础) 标签:#createRoot #React18
问题描述: 比较React 18的createRoot和React 17的render API。
参考答案要点:
- createRoot启用并发特性
- 支持新的API如useTransition
- 更好的hydration支持
代码示例:
// React 17
import ReactDOM from "react-dom"
ReactDOM.render(<App />, document.getElementById("root"))
// React 18
import { createRoot } from "react-dom/client"
const root = createRoot(document.getElementById("root"))
root.render(<App />)5. Suspense在React 18中有什么改进?
难度:⭐⭐(中级) 标签:#Suspense #SSR
问题描述: 介绍React 18中Suspense的改进。
参考答案要点:
- 支持服务端渲染的流式传输
- 支持数据获取(配合React.lazy或数据获取库)
- 与Error Boundary配合使用
- 可以嵌套使用,实现更细粒度的加载状态
并发模式
6. useTransition的作用和使用场景?
难度:⭐⭐(中级) 标签:#useTransition #并发
问题描述: 介绍useTransition的作用和使用场景。
参考答案要点:
- 将状态更新标记为低优先级过渡
- 返回
[isPending, startTransition] - 适用于:搜索框输入、Tab切换、大量数据渲染
- 保持UI响应性,避免卡顿
代码示例:
function SearchResults() {
const [query, setQuery] = useState("")
const [results, setResults] = useState([])
const [isPending, startTransition] = useTransition()
const handleChange = (e) => {
const value = e.target.value
setQuery(value) // 高优先级,立即更新
startTransition(() => {
setResults(search(value)) // 低优先级,可中断
})
}
return (
<>
<input value={query} onChange={handleChange} />
{isPending && <Spinner />}
<Results data={results} />
</>
)
}7. useDeferredValue的作用和使用场景?
难度:⭐⭐(中级) 标签:#useDeferredValue #延迟更新
问题描述: 介绍useDeferredValue的作用和使用场景。
参考答案要点:
- 延迟某个值的更新
- 类似于防抖,但由React控制
- 适用于:延迟渲染列表、图表等非关键UI
- 与useTransition的区别:useDeferredValue延迟值,useTransition控制更新
代码示例:
function SearchPage() {
const [query, setQuery] = useState("")
const deferredQuery = useDeferredValue(query)
return (
<>
<input value={query} onChange={(e) => setQuery(e.target.value)} />
{/* 使用deferredQuery渲染搜索结果,延迟更新 */}
<SearchResults query={deferredQuery} />
</>
)
}8. useTransition和useDeferredValue有什么区别?
难度:⭐⭐(中级) 标签:#useTransition #useDeferredValue
问题描述: 比较useTransition和useDeferredValue的区别。
参考答案要点:
| 特性 | useTransition | useDeferredValue |
|---|---|---|
| 作用对象 | 状态更新 | 单个值 |
| 触发方式 | 手动包裹startTransition | 自动延迟 |
| 状态提示 | 提供isPending | 需手动比较 |
| 适用场景 | 复杂状态/组件加载 | 搜索结果等非关键UI |
9. startTransition和useTransition有什么关系?
难度:⭐⭐(中级) 标签:#startTransition #并发
问题描述: 解释startTransition和useTransition的关系。
参考答案要点:
startTransition是独立函数useTransition内部使用startTransitionstartTransition可在事件处理器外使用useTransition额外提供isPending状态
10. 什么是流式SSR(Streaming SSR)?
难度:⭐⭐⭐(高级) 标签:#StreamingSSR #服务端渲染
问题描述: 介绍流式SSR的概念和优势。
参考答案要点:
- 服务端分段发送HTML
- 优先显示重要内容
- 配合Suspense实现渐进式加载
- 提高首屏加载体验
React 19新特性
11. React 19有哪些新特性?
难度:⭐⭐(中级) 标签:#React19 #新特性
问题描述: 概述React 19的新特性。
参考答案要点:
- Actions:支持async函数处理表单提交
- 新Hooks:useOptimistic、useActionState、useFormStatus
- React Compiler:自动优化渲染
- RSC正式支持:React Server Components
- use API:读取Promise或Context
12. 什么是React Compiler(原React Forget)?
难度:⭐⭐⭐(高级) 标签:#ReactCompiler #自动优化
问题描述: 介绍React Compiler的概念和作用。
参考答案要点:
- 自动记忆化编译器
- 自动生成等效的useMemo/useCallback代码
- 减少手动优化的需要
- 通过AST分析依赖关系
13. useOptimistic的作用是什么?
难度:⭐⭐(中级) 标签:#useOptimistic #乐观更新
问题描述: 介绍React 19新增的useOptimistic Hook。
参考答案要点:
- React 19新增Hook
- 实现乐观更新
- 在异步操作完成前更新UI
- 失败时自动回滚
14. useActionState的作用是什么?
难度:⭐⭐(中级) 标签:#useActionState #表单
问题描述: 介绍React 19新增的useActionState Hook。
参考答案要点:
- React 19新增Hook
- 管理异步Action的状态
- 返回
[state, formAction, isPending] - 简化表单提交状态管理
15. use Hook的作用是什么?
难度:⭐⭐(中级) 标签:#use #React19 #Suspense
问题描述: 介绍React 19新增的use API。
参考答案要点:
- React 19新增API
- 在渲染中读取Promise或Context
- 配合Suspense使用
- 简化异步数据获取
16. 什么是React Server Components(RSC)?
难度:⭐⭐⭐(高级) 标签:#RSC #服务端组件
问题描述: 详细解释React Server Components的概念和优势。
参考答案要点:
- 在服务端渲染的组件,不发送到客户端
- 零客户端JS体积
- 可直接访问服务端资源(数据库、文件系统等)
- 与SSR区别:RSC不生成HTML,而是生成React元素树
17. React 18为什么放弃IE支持?
难度:⭐⭐(中级) 标签:#IE支持 #兼容性
问题描述: 解释React 18放弃IE支持的原因。
参考答案要点:
- 简化代码库
- 使用现代浏览器特性(如Map、Set、requestIdleCallback)
- 减少polyfill体积
- 提高开发效率
18. React 18的Strict Mode有什么变化?
难度:⭐⭐(中级) 标签:#StrictMode #开发模式
问题描述: 介绍React 18中Strict Mode的变化。
参考答案要点:
- 开发模式下组件会故意双重渲染
- 帮助检测副作用
- 检测过时的生命周期方法
- 为未来的并发特性做准备
四、状态管理
Context与Redux
1. Context API的优缺点是什么?
难度:⭐(基础) 标签:#Context #状态管理
问题描述: 分析Context API的优缺点。
参考答案要点: 优点:
- 内置,无需额外库
- 适合跨层级传递数据
缺点:
- 性能问题:任何值变化导致所有消费者重渲染
- 不适合高频更新
解决方案:拆分Context、使用useMemo缓存value
2. Redux的核心概念是什么?
难度:⭐(基础) 标签:#Redux #状态管理
问题描述: 介绍Redux的核心概念。
参考答案要点:
- Store:单一状态树
- Action:描述发生了什么的普通对象
- Reducer:纯函数,根据Action更新状态
- 单向数据流:Action -> Reducer -> Store -> View
3. Redux中间件是什么?常用有哪些?
难度:⭐⭐(中级) 标签:#Redux #中间件
问题描述: 介绍Redux中间件的概念和常用中间件。
参考答案要点:
- 扩展dispatch功能
- 常用:
- redux-thunk:处理异步操作
- redux-saga:处理复杂异步流程
- redux-logger:日志记录
- 中间件链式调用
- 实现原理:柯里化函数
4. Redux Toolkit是什么?有什么优势?
难度:⭐⭐(中级) 标签:#ReduxToolkit #RTK
问题描述: 介绍Redux Toolkit及其优势。
参考答案要点:
- Redux官方推荐的工具集
- 简化Redux配置和代码
- 内置immer、redux-thunk、reselect
- createSlice自动生成action creators和reducers
5. 如何避免Context的性能问题?
难度:⭐⭐(中级) 标签:#Context #性能优化
问题描述: 介绍避免Context性能问题的方法。
参考答案要点:
- 拆分多个小Context
- 使用useMemo缓存Provider的value
- 将状态拆分为原子化片段
- 使用状态管理库(Zustand、Jotai)
现代状态管理方案
6. Zustand和Redux有什么区别?
难度:⭐⭐(中级) 标签:#Zustand #Redux
问题描述: 比较Zustand和Redux的区别。
参考答案要点:
| 特性 | Redux | Zustand |
|---|---|---|
| 复杂度 | 较高 | 极简 |
| 学习成本 | 高 | 低 |
| 中间件 | 丰富 | 内置常用 |
| 代码量 | 较多 | 极少 |
| TypeScript | 需配置 | 原生支持 |
7. React Query/SWR的作用是什么?
难度:⭐⭐(中级) 标签:#ReactQuery #SWR #服务端状态
问题描述: 介绍React Query/SWR的作用。
参考答案要点:
- 服务端状态管理
- 自动缓存、重新获取、后台更新
- 处理加载、错误状态
- 去重请求、乐观更新
8. 什么是不可变性(Immutability)?为什么重要?
难度:⭐⭐(中级) 标签:#不可变性 #状态管理
问题描述: 解释不可变性的概念及其重要性。
参考答案要点:
- 不直接修改数据,而是创建新数据
- 便于比较变化(浅比较)
- 支持时间旅行调试
- React依赖不可变性进行优化
9. 什么是选择器(Selector)?
难度:⭐⭐(中级) 标签:#Selector #性能优化
问题描述: 介绍选择器的概念和作用。
参考答案要点:
- 从状态中提取派生数据
- 使用reselect创建记忆化选择器
- 避免重复计算
- 组件只订阅需要的数据
10. 如何实现全局状态管理?
难度:⭐⭐(中级) 标签:#状态管理 #全局状态
问题描述: 列举实现全局状态管理的方法。
参考答案要点:
- Context + useReducer
- Redux/Redux Toolkit
- Zustand
- Jotai/Recoil(原子化)
- MobX(响应式)
11. 什么是乐观更新(Optimistic Update)?
难度:⭐⭐(中级) 标签:#乐观更新 #用户体验
问题描述: 解释乐观更新的概念和实现方式。
参考答案要点:
- 先更新UI,再发送请求
- 失败时回滚
- 提升用户体验
- React Query/useOptimistic支持
12. 状态应该放在哪里?
难度:⭐⭐(中级) 标签:#状态管理 #最佳实践
问题描述: 介绍状态放置的原则。
参考答案要点:
- 优先放在使用它的组件
- 需要共享时提升到共同祖先
- 全局状态使用状态管理库
- 服务端状态使用React Query/SWR
13. 什么是派生状态(Derived State)?
难度:⭐⭐(中级) 标签:#派生状态 #状态管理
问题描述: 解释派生状态的概念。
参考答案要点:
- 可以从props或state计算得出的状态
- 避免重复存储
- 使用useMemo或选择器计算
- 避免getDerivedStateFromProps滥用
14. 如何处理表单状态?
难度:⭐⭐(中级) 标签:#表单 #状态管理
问题描述: 介绍表单状态处理的方法。
参考答案要点:
- 受控组件管理每个字段
- 使用表单库:React Hook Form、Formik
- 分离表单状态和UI状态
- 惰性验证优化性能
15. 什么是原子化状态管理(Jotai/Recoil)?
难度:⭐⭐⭐(高级) 标签:#Jotai #Recoil #原子化
问题描述: 介绍原子化状态管理的概念。
参考答案要点:
- 状态拆分为独立原子
- 组件只订阅需要的原子
- 自动依赖追踪
- 细粒度更新,避免不必要的重渲染
五、性能优化
渲染优化
1. React.memo的作用是什么?
难度:⭐(基础) 标签:#React.memo #性能优化
问题描述: 介绍React.memo的作用和使用场景。
参考答案要点:
- 高阶组件,缓存函数组件
- props浅比较,不变则不重渲染
- 可自定义比较函数
- 适用于纯展示组件
代码示例:
const MyComponent = React.memo(
function MyComponent({ name }) {
return <div>{name}</div>
},
(prevProps, nextProps) => {
// 自定义比较函数
return prevProps.name === nextProps.name
}
)2. 什么时候应该使用useMemo和useCallback?
难度:⭐⭐(中级) 标签:#useMemo #useCallback #性能优化
问题描述: 介绍useMemo和useCallback的适用场景。
参考答案要点:
- 计算成本高的值使用useMemo
- 传递给子组件的函数使用useCallback
- 依赖数组要正确填写
- 不要滥用,缓存也有开销
3. 如何实现代码分割和懒加载?
难度:⭐⭐(中级) 标签:#代码分割 #懒加载 #Suspense
问题描述: 介绍代码分割和懒加载的实现方法。
参考答案要点:
- 使用React.lazy和动态import
- 配合Suspense显示加载状态
- 路由级别分割
代码示例:
const LazyComponent = React.lazy(() => import("./LazyComponent"))
function App() {
return (
<Suspense fallback={<Loading />}>
<LazyComponent />
</Suspense>
)
}4. 什么是虚拟列表?如何实现?
难度:⭐⭐(中级) 标签:#虚拟列表 #大数据
问题描述: 介绍虚拟列表的概念和实现方法。
参考答案要点:
- 只渲染可视区域的元素
- 使用react-window或react-virtualized
- 适合大数据量列表
- 减少DOM节点数量
5. 如何排查组件不必要的重复渲染?
难度:⭐⭐(中级) 标签:#性能优化 #调试
问题描述: 介绍排查组件重复渲染的方法。
参考答案要点:
- React DevTools Profiler
- 使用memo、useMemo、useCallback
- 检查依赖数组
- 拆分组件,缩小重渲染范围
6. 什么是shouldComponentUpdate?
难度:⭐⭐(中级) 标签:#shouldComponentUpdate #类组件
问题描述: 介绍shouldComponentUpdate的作用。
参考答案要点:
- 类组件生命周期方法
- 控制是否重新渲染
- 返回false阻止渲染
- PureComponent自动实现浅比较
代码优化
7. 如何避免在渲染中创建新对象/函数?
难度:⭐⭐(中级) 标签:#性能优化 #渲染优化
问题描述: 介绍避免渲染中创建新对象/函数的方法。
参考答案要点:
- 使用useMemo缓存对象
- 使用useCallback缓存函数
- 避免内联函数定义
- 将常量移到组件外部
8. 什么是时间切片(Time Slicing)?
难度:⭐⭐⭐(高级) 标签:#时间切片 #并发
问题描述: 介绍时间切片的概念和实现原理。
参考答案要点:
- 将大任务拆分为小任务
- 在浏览器空闲时执行
- 基于requestIdleCallback
- React 18并发特性基础
9. 如何优化大型应用的启动时间?
难度:⭐⭐⭐(高级) 标签:#性能优化 #启动优化
问题描述: 介绍优化大型应用启动时间的方法。
参考答案要点:
- 代码分割和懒加载
- Tree Shaking移除无用代码
- 压缩和Gzip
- CDN分发
- 服务端渲染
10. 什么是Web Worker?在React中如何使用?
难度:⭐⭐⭐(高级) 标签:#WebWorker #性能优化
问题描述: 介绍Web Worker的概念和在React中的使用。
参考答案要点:
- 在后台线程运行JavaScript
- 不阻塞主线程
- 适合复杂计算
- 使用comlink简化通信
11. 如何优化图片加载?
难度:⭐⭐(中级) 标签:#图片优化 #性能优化
问题描述: 介绍优化图片加载的方法。
参考答案要点:
- 使用next/image或react-lazy-load-image-component
- 懒加载非首屏图片
- 使用WebP格式
- 响应式图片
- 预加载关键图片
12. 什么是Hydration?如何优化?
难度:⭐⭐⭐(高级) 标签:#Hydration #SSR
问题描述: 介绍Hydration的概念和优化方法。
参考答案要点:
- 服务端渲染后客户端激活
- 不匹配会导致重新渲染
- 确保服务端和客户端输出一致
- 使用suppressHydrationWarning
13. 如何使用Profiler进行性能分析?
难度:⭐⭐(中级) 标签:#Profiler #性能分析
问题描述: 介绍Profiler的使用方法。
参考答案要点:
- 测量组件渲染时间
- 识别性能瓶颈
- 只在开发模式使用
代码示例:
;<Profiler id="App" onRender={onRenderCallback}>
<App />
</Profiler>
function onRenderCallback(id, phase, actualDuration) {
console.log("Component:", id)
console.log("Phase:", phase)
console.log("Duration:", actualDuration)
}14. 什么是CSS-in-JS的性能影响?
难度:⭐⭐(中级) 标签:#CSS-in-JS #性能优化
问题描述: 介绍CSS-in-JS的性能影响和优化方法。
参考答案要点:
- 运行时生成CSS有开销
- 使用useInsertionEffect注入样式
- 考虑静态CSS方案
- 避免动态生成大量样式
15. key属性在列表渲染中的作用?
难度:⭐(基础) 标签:#Key #列表渲染
问题描述: 介绍key属性在列表渲染中的作用。
参考答案要点:
- 帮助React识别元素身份
- 优化Diff算法
- 稳定的key减少不必要的重建
- 避免使用数组索引作为key
六、React Router v6
1. React Router v6有哪些重大变化?
难度:⭐(基础) 标签:#ReactRouter #v6
问题描述: 介绍React Router v6的重大变化。
参考答案要点:
- Switch替换为Routes
- Route使用element而非component
- 嵌套路由配置更简洁
- 移除Redirect,使用Navigate
- 默认精确匹配,移除exact
2. BrowserRouter和HashRouter的区别?
难度:⭐(基础) 标签:#BrowserRouter #HashRouter
问题描述: 比较BrowserRouter和HashRouter的区别。
参考答案要点:
- BrowserRouter:使用History API,URL美观,需要服务端配置
- HashRouter:使用URL hash,兼容性好,URL有#
- 推荐使用BrowserRouter
3. 如何实现路由鉴权?
难度:⭐⭐(中级) 标签:#路由鉴权 #权限控制
问题描述: 介绍路由鉴权的实现方法。
参考答案要点:
- 高阶组件或自定义路由组件
- 检查用户权限
- 未登录重定向到登录页
代码示例:
function PrivateRoute({ children }) {
const isAuth = useAuth()
return isAuth ? children : <Navigate to="/login" />
}
// 使用
;<Route
path="/dashboard"
element={
<PrivateRoute>
<Dashboard />
</PrivateRoute>
}
/>4. useNavigate和useHistory有什么区别?
难度:⭐(基础) 标签:#useNavigate #路由导航
问题描述: 比较useNavigate和useHistory的区别。
参考答案要点:
- useHistory是v5的API
- useNavigate是v6的新API
- navigate支持相对路径
- 更简洁的API设计
5. 如何实现嵌套路由?
难度:⭐⭐(中级) 标签:#嵌套路由 #Outlet
问题描述: 介绍嵌套路由的实现方法。
参考答案要点:
- 使用Outlet渲染子路由
- 配置嵌套路由结构
代码示例:
;<Route path="/users" element={<Users />}>
<Route path=":id" element={<UserDetail />} />
</Route>
// Users组件中使用Outlet
import { Outlet } from "react-router-dom"
function Users() {
return (
<div>
<h1>Users</h1>
<Outlet /> {/* 子路由渲染位置 */}
</div>
)
}6. 如何获取路由参数?
难度:⭐(基础) 标签:#路由参数 #useParams
问题描述: 介绍获取路由参数的方法。
参考答案要点:
useParams:获取动态路由参数useSearchParams:获取查询参数useLocation:获取完整location对象
7. 如何实现路由懒加载?
难度:⭐⭐(中级) 标签:#路由懒加载 #代码分割
问题描述: 介绍路由懒加载的实现方法。
代码示例:
const Dashboard = lazy(() => import("./Dashboard"))
;<Route
path="/dashboard"
element={
<Suspense fallback={<Loading />}>
<Dashboard />
</Suspense>
}
/>8. 什么是数据路由(Data Router)?
难度:⭐⭐⭐(高级) 标签:#DataRouter #ReactRouter
问题描述: 介绍React Router v6.4+的数据路由特性。
参考答案要点:
- React Router v6.4+特性
- 并行加载数据和组件
- 解决瀑布流问题
- 使用loader和action
9. 如何实现路由守卫?
难度:⭐⭐(中级) 标签:#路由守卫 #权限控制
问题描述: 介绍路由守卫的实现方法。
参考答案要点:
- 自定义ProtectedRoute组件
- 在layout中统一处理
- 使用高阶组件
- 服务端鉴权配合
10. 编程式导航和声明式导航的区别?
难度:⭐(基础) 标签:#导航 #ReactRouter
问题描述: 比较编程式导航和声明式导航的区别。
参考答案要点:
- 声明式:Link、NavLink组件
- 编程式:useNavigate钩子
- 声明式适合模板中
- 编程式适合事件处理中
11. 如何处理404页面?
难度:⭐(基础) 标签:#404 #路由配置
问题描述: 介绍404页面的处理方法。
参考答案要点:
- 使用*通配符
- 放在所有路由最后
- 可配合Navigate重定向
代码示例:
<Route path="*" element={<NotFound />} />12. 如何实现路由动画?
难度:⭐⭐(中级) 标签:#路由动画 #动画
问题描述: 介绍路由动画的实现方法。
参考答案要点:
- 使用react-transition-group
- 或framer-motion
- 配合location.key
- 注意动画性能
七、服务端渲染(Next.js)
1. 什么是SSR、SSG、ISR?
难度:⭐(基础) 标签:#SSR #SSG #ISR
问题描述: 介绍SSR、SSG、ISR的概念和区别。
参考答案要点:
| 模式 | 说明 | 适用场景 |
|---|---|---|
| SSR | 服务端渲染,每次请求生成 | 实时数据、个性化 |
| SSG | 静态生成,构建时生成 | 内容不变、博客 |
| ISR | 增量静态再生,定时更新 | 商品列表、新闻 |
2. Next.js App Router和Pages Router的区别?
难度:⭐⭐(中级) 标签:#Next.js #AppRouter
问题描述: 比较Next.js App Router和Pages Router的区别。
参考答案要点:
- App Router基于文件夹路由
- 默认使用React Server Components
- 更强大的嵌套布局
- 支持平行路由、拦截路由
- 数据获取方式不同
3. 什么是React Server Components?
难度:⭐⭐⭐(高级) 标签:#RSC #服务端组件
问题描述: 介绍React Server Components的概念。
参考答案要点:
- 服务端渲染的组件
- 零客户端JS
- 可直接访问服务端资源
- 与客户端组件配合使用
4. 如何在Next.js中实现ISR?
难度:⭐⭐(中级) 标签:#ISR #Next.js
问题描述: 介绍Next.js中ISR的实现方法。
代码示例:
// pages router
export async function getStaticProps() {
return {
props: { data },
revalidate: 60, // 60秒后重新生成
}
}
// app router
export const revalidate = 605. 什么是Streaming SSR?
难度:⭐⭐⭐(高级) 标签:#StreamingSSR #Next.js
问题描述: 介绍Streaming SSR的概念。
参考答案要点:
- 分段发送HTML
- 配合Suspense
- 优先显示重要内容
- 提高感知性能
6. 如何在App Router中获取数据?
难度:⭐⭐(中级) 标签:#数据获取 #AppRouter
问题描述: 介绍App Router中的数据获取方法。
代码示例:
// Server Component
async function Page() {
const data = await fetch("https://api.example.com/data")
return <Component data={data} />
}参考答案要点:
- 直接在组件中使用async/await
- 自动去重相同请求
- 支持缓存配置
7. 什么是Server Actions?
难度:⭐⭐(中级) 标签:#ServerActions #Next.js
问题描述: 介绍Next.js 14+的Server Actions特性。
参考答案要点:
- Next.js 14+特性
- 服务端执行的函数
- 可直接在表单中使用
- 简化数据变更逻辑
8. 如何处理客户端和服务端的不一致?
难度:⭐⭐⭐(高级) 标签:#Hydration #SSR
问题描述: 介绍处理客户端和服务端不一致的方法。
参考答案要点:
- 避免在服务端使用浏览器API
- 使用dynamic导入禁用SSR
- 使用suppressHydrationWarning
- 确保数据一致性
9. 什么是Middleware?
难度:⭐⭐(中级) 标签:#Middleware #Next.js
问题描述: 介绍Next.js Middleware的概念和用途。
参考答案要点:
- 请求处理中间件
- 实现认证、重定向、AB测试
- 在matcher匹配的路由前执行
- 边缘运行,快速响应
10. 如何优化Next.js应用性能?
难度:⭐⭐(中级) 标签:#性能优化 #Next.js
问题描述: 介绍优化Next.js应用性能的方法。
参考答案要点:
- 使用Image组件优化图片
- 使用Font组件优化字体
- 代码分割和懒加载
- 合理使用缓存策略
- 分析bundle大小
11. 什么是Partial Prerendering(PPR)?
难度:⭐⭐⭐(高级) 标签:#PPR #Next.js
问题描述: 介绍Next.js 14+的Partial Prerendering特性。
参考答案要点:
- Next.js 14+实验性功能
- 静态外壳+动态内容
- 结合SSG和SSR优势
- 提高首屏加载速度
12. 如何实现国际化路由?
难度:⭐⭐(中级) 标签:#国际化 #i18n
问题描述: 介绍Next.js中国际化路由的实现方法。
参考答案要点:
- 使用next-intl或i18next
- App Router内置支持
- 配置locales和defaultLocale
- 中间件处理语言切换
13. 什么是Route Handlers?
难度:⭐⭐(中级) 标签:#RouteHandlers #API
问题描述: 介绍App Router中的Route Handlers。
参考答案要点:
- App Router中替代API Routes
- 在app目录下创建route.js
- 支持各种HTTP方法
- 类型安全
14. 如何使用Parallel Routes?
难度:⭐⭐⭐(高级) 标签:#ParallelRoutes #AppRouter
问题描述: 介绍Parallel Routes的使用方法。
参考答案要点:
- 使用@folder语法
- 同一页面显示多个路由
- 实现复杂布局
- 独立加载状态
15. 什么是Intercepting Routes?
难度:⭐⭐⭐(高级) 标签:#InterceptingRoutes #路由拦截
问题描述: 介绍Intercepting Routes的概念。
参考答案要点:
- 拦截路由导航
- 实现模态框效果
- 使用(.) (..) (...)语法
- 保留上下文
八、测试
1. React Testing Library的核心理念是什么?
难度:⭐(基础) 标签:#TestingLibrary #测试理念
问题描述: 介绍React Testing Library的核心理念。
参考答案要点:
- 测试用户如何与UI交互
- 不测试实现细节
- 可访问性优先的查询
- 鼓励更好的可访问性实践
2. 如何测试React组件?
难度:⭐(基础) 标签:#组件测试 #TestingLibrary
问题描述: 介绍React组件的测试方法。
代码示例:
import { render, screen, fireEvent } from "@testing-library/react"
test("button click increments counter", () => {
render(<Counter />)
const button = screen.getByText("Increment")
fireEvent.click(button)
expect(screen.getByText("1")).toBeInTheDocument()
})3. 什么是Mock?在测试中如何使用?
难度:⭐⭐(中级) 标签:#Mock #测试
问题描述: 介绍Mock的概念和使用方法。
参考答案要点:
- 模拟函数或模块
- jest.fn()创建mock函数
- jest.mock()模拟模块
- 控制返回值和验证调用
4. 如何测试异步操作?
难度:⭐⭐(中级) 标签:#异步测试 #TestingLibrary
问题描述: 介绍异步操作的测试方法。
代码示例:
import { waitFor } from "@testing-library/react"
test("fetches data", async () => {
render(<DataComponent />)
await waitFor(() => {
expect(screen.getByText("Data loaded")).toBeInTheDocument()
})
})5. 什么是MSW(Mock Service Worker)?
难度:⭐⭐(中级) 标签:#MSW #API模拟
问题描述: 介绍MSW的概念和用途。
参考答案要点:
- 拦截网络请求
- 在浏览器和Node中运行
- 模拟API响应
- 不修改应用代码
6. 如何测试Hooks?
难度:⭐⭐(中级) 标签:#Hooks测试 #renderHook
问题描述: 介绍Hooks的测试方法。
代码示例:
import { renderHook, act } from "@testing-library/react-hooks"
test("useCounter", () => {
const { result } = renderHook(() => useCounter())
act(() => {
result.current.increment()
})
expect(result.current.count).toBe(1)
})7. 什么是快照测试?
难度:⭐(基础) 标签:#快照测试 #回归测试
问题描述: 介绍快照测试的概念和使用场景。
参考答案要点:
- 比较当前输出与保存的快照
- 适合UI组件回归测试
- 更新快照:--updateSnapshot
- 谨慎使用,避免过度依赖
8. 如何测试表单?
难度:⭐⭐(中级) 标签:#表单测试 #user-event
问题描述: 介绍表单的测试方法。
参考答案要点:
- 使用user-event模拟用户输入
- 测试验证逻辑
- 测试提交处理
- 测试错误状态
9. 什么是E2E测试?常用工具有哪些?
难度:⭐(基础) 标签:#E2E测试 #端到端
问题描述: 介绍E2E测试的概念和常用工具。
参考答案要点:
- 端到端测试,模拟真实用户操作
- 常用工具:Cypress、Playwright、Selenium
- 测试完整用户流程
- 较慢但覆盖全面
10. 如何测试Context Provider?
难度:⭐⭐(中级) 标签:#Context测试 #Provider
问题描述: 介绍Context Provider的测试方法。
代码示例:
const wrapper = ({ children }) => (
<ThemeProvider theme="dark">{children}</ThemeProvider>
)
render(<Component />, { wrapper })九、源码原理
Fiber架构
1. 什么是Fiber架构?
难度:⭐⭐⭐(高级) 标签:#Fiber #架构
问题描述: 介绍React Fiber架构的概念和设计目标。
参考答案要点:
- React 16引入的新协调引擎
- 将渲染工作拆分为小单元
- 支持优先级调度和可中断渲染
- Fiber节点是链表结构
2. Fiber节点的数据结构是怎样的?
难度:⭐⭐⭐(高级) 标签:#Fiber #数据结构
问题描述: 介绍Fiber节点的数据结构。
代码示例:
function FiberNode() {
this.tag = tag // 组件类型
this.key = key // 唯一标识
this.type = null // 元素类型
this.stateNode = null // 真实DOM或组件实例
this.return = null // 父节点
this.child = null // 第一个子节点
this.sibling = null // 下一个兄弟节点
this.alternate = null // 双缓冲树对应节点
this.lanes = NoLanes // 更新优先级
}3. React的渲染流程是怎样的?
难度:⭐⭐⭐(高级) 标签:#渲染流程 #Fiber
问题描述: 详细介绍React的渲染流程。
参考答案要点:
1. Render阶段(可中断):
- 构建Fiber树
- 执行Diff算法
- 标记副作用
2. Commit阶段(不可中断):
- 执行DOM操作
- 执行生命周期和副作用
- 更新refs
4. 什么是Lane模型?
难度:⭐⭐⭐(高级) 标签:#Lane #优先级
问题描述: 介绍React的Lane模型。
参考答案要点:
- 位掩码表示优先级
- 不同Lane代表不同优先级
- SyncLane最高优先级
- TransitionLane低优先级
- 支持批量合并
调度与渲染
5. React的调度机制是怎样的?
难度:⭐⭐⭐(高级) 标签:#Scheduler #调度
问题描述: 介绍React的调度机制。
参考答案要点:
- 使用Scheduler包
- MessageChannel实现微任务
- 时间切片防止阻塞
- 优先级队列管理任务
6. 什么是双缓冲(Double Buffering)?
难度:⭐⭐⭐(高级) 标签:#双缓冲 #Fiber
问题描述: 介绍React的双缓冲机制。
参考答案要点:
- 两棵Fiber树交替使用
- current树:当前屏幕显示
- workInProgress树:正在构建
- 提交后交换指针
7. React为什么不使用requestIdleCallback?
难度:⭐⭐⭐(高级) 标签:#Scheduler #requestIdleCallback
问题描述: 解释React为什么不使用requestIdleCallback。
参考答案要点:
- requestIdleCallback兼容性差
- 调度不可控
- 使用MessageChannel实现更可控的调度
- 自定义Scheduler更灵活
8. 什么是时间切片(Time Slicing)?
难度:⭐⭐⭐(高级) 标签:#时间切片 #并发
问题描述: 介绍React的时间切片机制。
参考答案要点:
- 将大任务拆分为小任务
- 每个任务执行后检查剩余时间
- 时间不足则让出主线程
- 使用shouldYield判断
9. React的Diff算法具体是如何工作的?
难度:⭐⭐⭐(高级) 标签:#Diff算法 #源码
问题描述: 详细介绍React Diff算法的实现。
参考答案要点:
- 单节点Diff:比较type和key
- 多节点Diff:两轮遍历
- 第一轮:复用相同位置的节点
- 第二轮:处理移动、新增、删除
- key优化列表对比
10. 什么是Reconciliation?
难度:⭐⭐⭐(高级) 标签:#Reconciliation #协调
问题描述: 介绍React的Reconciliation过程。
参考答案要点:
- 协调过程:比较新旧虚拟DOM
- 确定需要更新的部分
- 生成更新计划
- 不直接操作DOM
11. React的合成事件系统是如何实现的?
难度:⭐⭐⭐(高级) 标签:#合成事件 #事件系统
问题描述: 介绍React合成事件系统的实现。
参考答案要点:
- 事件委托到root容器
- 统一封装SyntheticEvent
- 事件池复用(React 17前)
- 模拟事件冒泡
12. 什么是Hooks的调用顺序规则?
难度:⭐⭐⭐(高级) 标签:#Hooks #源码
问题描述: 深入解释Hooks的调用顺序规则。
参考答案要点:
- 基于数组/链表实现
- 按调用顺序存储状态
- 条件调用导致顺序错乱
- ESLint插件强制执行
13. useState是如何实现的?
难度:⭐⭐⭐(高级) 标签:#useState #源码
问题描述: 介绍useState的实现原理。
参考答案要点:
- 基于链表存储状态
- 每个Hook对应一个节点
- 通过next指针连接
- 渲染时按顺序访问
14. 什么是优先级调度?
难度:⭐⭐⭐(高级) 标签:#优先级 #调度
问题描述: 介绍React的优先级调度机制。
参考答案要点:
- 不同更新有不同优先级
- 用户交互 > 动画 > 数据加载
- 高优先级可中断低优先级
- Lane模型实现
15. React 18的并发特性是如何实现的?
难度:⭐⭐⭐(高级) 标签:#并发 #React18 #源码
问题描述: 介绍React 18并发特性的实现原理。
参考答案要点:
- Fiber架构支持可中断渲染
- Lane模型管理优先级
- 时间切片分配任务
- startTransition标记低优先级
十、高级特性与其他
1. 什么是高阶组件(HOC)?
难度:⭐⭐(中级) 标签:#HOC #高阶组件
问题描述: 介绍高阶组件的概念和使用场景。
参考答案要点:
- 接收组件返回新组件的函数
- 用于代码复用和逻辑抽象
- 不修改原组件,而是包装
- 常见用途:权限控制、数据获取、日志记录
2. 什么是Render Props模式?
难度:⭐⭐(中级) 标签:#RenderProps #模式
问题描述: 介绍Render Props模式的概念。
参考答案要点:
- 通过props传递渲染函数
- 共享组件逻辑
- 与HOC相比更灵活
- 可能产生嵌套地狱
3. HOC和Render Props有什么区别?
难度:⭐⭐(中级) 标签:#HOC #RenderProps #比较
问题描述: 比较HOC和Render Props的区别。
参考答案要点:
| 特性 | HOC | Render Props |
|---|---|---|
| 实现方式 | 函数包装 | props传递函数 |
| 嵌套问题 | 可能有 | 可能有 |
| 灵活性 | 较低 | 较高 |
| TypeScript | 类型推断较难 | 类型推断较好 |
4. 什么是自定义Hook的封装原则?
难度:⭐⭐(中级) 标签:#自定义Hook #封装
问题描述: 介绍自定义Hook的封装原则。
参考答案要点:
- 以use开头命名
- 一个Hook只做一件事
- 合理拆分,保持单一职责
- 提供清晰的API和文档
5. 如何实现组件级别的权限控制?
难度:⭐⭐(中级) 标签:#权限控制 #HOC
问题描述: 介绍组件级别权限控制的实现方法。
代码示例:
function withAuth(Component) {
return function WrappedComponent(props) {
const isAuth = useAuth()
if (!isAuth) return <Navigate to="/login" />
return <Component {...props} />
}
}6. 什么是微前端?在React中如何实现?
难度:⭐⭐⭐(高级) 标签:#微前端 #架构
问题描述: 介绍微前端的概念和在React中的实现。
参考答案要点:
- 将大型应用拆分为独立部署的小应用
- 实现方案:qiankun、Module Federation、single-spa
- 样式隔离、JS沙箱
- 共享依赖
7. React中的Refs有哪些使用方式?
难度:⭐(基础) 标签:#Refs #DOM操作
问题描述: 介绍React中Refs的使用方式。
参考答案要点:
- createRef:类组件使用
- useRef:函数组件使用
- callback ref:回调方式
- forwardRef:转发ref到子组件
8. 如何实现KeepAlive功能?
难度:⭐⭐⭐(高级) 标签:#KeepAlive #状态保持
问题描述: 介绍KeepAlive功能的实现方法。
参考答案要点:
- React不原生支持
- 使用display:none隐藏
- 或使用第三方库react-activation
- 维护组件状态不被销毁
9. 什么是React的严格模式(Strict Mode)?
难度:⭐(基础) 标签:#StrictMode #开发模式
问题描述: 介绍React严格模式的作用。
参考答案要点:
- 开发模式下检测潜在问题
- 故意双重渲染检测副作用
- 检测过时的生命周期
- 检测意外的副作用
10. React和Vue的设计哲学有什么区别?
难度:⭐⭐(中级) 标签:#React #Vue #比较
问题描述: 比较React和Vue的设计哲学。
参考答案要点:
| 特性 | React | Vue |
|---|---|---|
| 核心思想 | 函数式、不可变 | 响应式、可变 |
| 模板 | JSX | 模板语法 |
| 状态管理 | 手动管理 | 自动追踪依赖 |
| 学习曲线 | 较陡 | 较平缓 |
| 灵活性 | 高 | 中高 |
附录:手写代码题精选
1. 实现简版useState
let hookIndex = 0
const hooks = []
function useState(initialValue) {
const state = hooks[hookIndex] !== undefined ? hooks[hookIndex] : initialValue
hooks[hookIndex] = state
const currentIndex = hookIndex
hookIndex++
const setState = (newValue) => {
hooks[currentIndex] = newValue
render() // 触发重新渲染
}
return [state, setState]
}
function render() {
hookIndex = 0
// 重新渲染组件
}2. 实现简版useEffect
let effectIndex = 0
const effectDeps = []
function useEffect(callback, deps) {
const hasChanged =
!deps ||
!effectDeps[effectIndex] ||
!deps.every((dep, i) => dep === effectDeps[effectIndex][i])
if (hasChanged) {
callback()
effectDeps[effectIndex] = deps || []
}
effectIndex++
}3. 实现自定义useDebounce
function useDebounce(value, delay) {
const [debouncedValue, setDebouncedValue] = useState(value)
useEffect(() => {
const timer = setTimeout(() => setDebouncedValue(value), delay)
return () => clearTimeout(timer)
}, [value, delay])
return debouncedValue
}4. 实现自定义useFetch
function useFetch(url) {
const [data, setData] = useState(null)
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
useEffect(() => {
setLoading(true)
fetch(url)
.then((res) => res.json())
.then((data) => {
setData(data)
setLoading(false)
})
.catch((err) => {
setError(err)
setLoading(false)
})
}, [url])
return { data, loading, error }
}5. 实现自定义useLocalStorage
function useLocalStorage(key, initialValue) {
const [value, setValue] = useState(() => {
try {
const item = localStorage.getItem(key)
return item ? JSON.parse(item) : initialValue
} catch {
return initialValue
}
})
useEffect(() => {
try {
localStorage.setItem(key, JSON.stringify(value))
} catch (error) {
console.error("Error saving to localStorage:", error)
}
}, [key, value])
return [value, setValue]
}复习建议与重点题目
复习建议
-
分阶段复习
- 第一阶段:掌握基础概念(⭐题目)
- 第二阶段:深入Hooks和组件通信(⭐⭐题目)
- 第三阶段:理解源码原理和高级特性(⭐⭐⭐题目)
-
动手实践
- 手写常用Hooks(useDebounce、useFetch等)
- 实现小型React项目
- 阅读React源码关键部分
-
面试技巧
- 回答时先给出概念定义
- 结合实际项目经验说明
- 主动提及优缺点和适用场景
- 准备代码示例
重点题目推荐
🔴 必会题目(面试高频)
| 题目 | 难度 | 重要性 |
|---|---|---|
| useState和useEffect的工作原理 | ⭐⭐ | ⭐⭐⭐⭐⭐ |
| useEffect依赖项数组的作用 | ⭐ | ⭐⭐⭐⭐⭐ |
| useMemo和useCallback的区别 | ⭐⭐ | ⭐⭐⭐⭐⭐ |
| React.memo的作用 | ⭐ | ⭐⭐⭐⭐⭐ |
| setState是同步还是异步 | ⭐⭐ | ⭐⭐⭐⭐⭐ |
| 虚拟DOM和Diff算法 | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| React 18并发特性 | ⭐⭐ | ⭐⭐⭐⭐ |
| Hooks的闭包陷阱 | ⭐⭐⭐ | ⭐⭐⭐⭐ |
🔵 进阶题目(大厂常问)
| 题目 | 难度 | 重要性 |
|---|---|---|
| Fiber架构原理 | ⭐⭐⭐ | ⭐⭐⭐⭐ |
| Lane模型 | ⭐⭐⭐ | ⭐⭐⭐⭐ |
| React Compiler | ⭐⭐⭐ | ⭐⭐⭐ |
| React Server Components | ⭐⭐⭐ | ⭐⭐⭐⭐ |
🟢 加分题目(展示深度)
| 题目 | 难度 | 重要性 |
|---|---|---|
| 时间切片实现 | ⭐⭐⭐ | ⭐⭐⭐ |
| 优先级调度 | ⭐⭐⭐ | ⭐⭐⭐ |
| useInsertionEffect | ⭐⭐⭐ | ⭐⭐⭐ |