React 面试题
基础题
参考答案:
React 的核心思想可概括为四点:
- 组件化:把 UI 拆分为独立、可复用、可组合的组件,每个组件管理自己的状态和渲染。
- 声明式:开发者只需描述“UI 应该长什么样”,React 负责根据状态变化高效更新 DOM。
- 单向数据流:数据自顶向下通过 props 传递,子组件通过回调通知父组件修改状态,数据流向清晰可预测。
- 虚拟 DOM:用轻量 JS 对象描述真实 DOM,通过 Diff 算法最小化真实 DOM 操作,提升性能。
相比命令式 DOM 操作,声明式让代码更易理解、测试和复用。
评分维度:
- 能说出 3 个以上核心思想(40%)
- 能结合实际说明(30%)
- 能对比声明式与命令式(30%)
参考答案:
JSX 是 JavaScript 的语法扩展,允许在 JS 中编写类似 HTML 的结构。它既不是字符串,也不是模板引擎,而是会被编译为 React.createElement 调用。
示例:
const element = <h1 className="title">Hello</h1>;
// 编译后等价于
const element = React.createElement('h1', { className: 'title' }, 'Hello');
JSX 规则:
- 必须有一个根节点(或用 Fragment
<></>)。 - 使用
className而非class,htmlFor而非for。 - 表达式用
{}包裹,语句不行。 - 标签名小写表示 HTML 元素,大写表示组件。
评分维度:
- 能解释 JSX 本质(30%)
- 能说明编译结果(30%)
- 能说出基本规则并给出示例(40%)
参考答案:
| 维度 | 类组件 | 函数组件 |
|---|---|---|
| 定义方式 | ES6 class | 普通函数 |
| 状态管理 | this.state / this.setState |
useState / useReducer |
| 生命周期 | componentDidMount 等 |
useEffect 组合模拟 |
| 代码量 | 较冗长 | 简洁 |
| this 问题 | 需要绑定 this | 无 this |
| 官方推荐 | legacy 场景 | 主流推荐 |
函数组件配合 Hooks 后能力完全覆盖类组件,且更利于逻辑复用和 Tree Shaking。类组件目前仅遗留项目或 Error Boundary 等少数场景使用。
评分维度:
- 能区分两种组件(40%)
- 能说明生命周期和 Hooks 差异(30%)
- 能说明函数组件优势(30%)
参考答案:
受控组件是指表单元素的值由 React 状态驱动,并通过 onChange 事件同步更新状态。
示例:
function Input() {
const [value, setValue] = useState('');
return (
<input
value={value}
onChange={e => setValue(e.target.value)}
/>
);
}
特点:
- 输入值始终由 React 状态决定。
- 便于做校验、格式化、联动控制。
- 与非受控组件(通过 ref 读取 DOM 值)相比,受控组件更符合 React 数据驱动思想。
评分维度:
- 能解释受控组件(40%)
- 能写出示例(30%)
- 能对比非受控组件及适用场景(30%)
参考答案:
key 是 React Diff 算法中用于标识列表元素的特殊属性,帮助 React 判断元素是新增、删除、移动还是复用。
最佳实践:
- 使用稳定且唯一的标识(如数据库 ID)作为 key。
- 避免使用数组索引作为 key,除非列表纯展示且不会增删排序;否则会导致状态错位、性能下降。
示例:
{list.map(item => (
<li key={item.id}>{item.name}</li>
))}
key 只在兄弟节点间比较,不参与组件 props 传递,因此不能通过 props.key 读取。
评分维度:
- 能解释 key 作用(40%)
- 能说明为什么不能用索引作为 key(30%)
- 能给出正确使用方式(30%)
进阶题
参考答案:
Fiber 是 React 16 引入的协调(Reconciliation)引擎重写,目标是把渲染工作拆分为可中断、可优先级调度的小单元。
核心能力:
- 增量渲染:把更新拆成多个小任务,避免长时间阻塞主线程。
- 任务暂停与恢复:浏览器空闲时继续执行,高优先级任务可插队。
- 错误边界:通过 Fiber 树定位错误范围。
- 双缓冲:维护
current树(当前屏幕)和workInProgress树(正在计算),渲染完成后一次性切换。
Fiber 节点包含类型、props、state、effect、child、sibling、return 等指针,构成链表树,支持遍历和中断。
评分维度:
- 能解释 Fiber 解决的问题(30%)
- 能说明任务可中断与优先级调度(40%)
- 能提到双缓冲与错误边界(30%)
参考答案:
useEffect 的第二个参数是依赖数组,控制 effect 执行时机:
- 不传:每次渲染后都执行。
- 空数组
[]:只在组件挂载和卸载时执行,常用于初始化或订阅。 - 有依赖:依赖变化时执行,变化通过
Object.is比较。
依赖数组必须包含 effect 中使用的所有响应式值(state、props、函数等),否则会产生“闭包陷阱”,读到过期的状态。
示例:
useEffect(() => {
console.log(count);
}, [count]);
// 订阅与清理
useEffect(() => {
const handler = () => setCount(c => c + 1);
window.addEventListener('click', handler);
return () => window.removeEventListener('click', handler);
}, []);
如果 effect 里用到了某个值但遗漏在依赖中,ESLint react-hooks/exhaustive-deps 会警告。对于函数依赖,可用 useCallback 稳定引用或把函数定义在 effect 内部。
评分维度:
- 能说明三种情况(35%)
- 能强调依赖完整性(25%)
- 能举例说明闭包问题和清理函数(25%)
- 知道如何处理函数依赖(15%)
参考答案:
三者都用于性能优化,但作用层次不同:
- React.memo:高阶组件,缓存函数组件。props 浅比较不变时跳过重渲染。
- useMemo:缓存计算结果。依赖不变时返回上次计算值。
- useCallback:缓存函数引用。依赖不变时返回同一函数实例,常用于避免子组件 props 引用变化导致重渲染。
示例:
const memoizedValue = useMemo(() => compute(a, b), [a, b]);
const memoizedFn = useCallback(() => doSomething(a), [a]);
// 配合 React.memo 使用
const Child = React.memo(({ onClick }) => <button onClick={onClick}>Click</button>);
注意:不要滥用,它们本身也有开销。优先定位真实性能瓶颈(如大列表、复杂渲染、频繁重渲染的子组件),再用 React DevTools Profiler 验证。同时注意依赖数组的完整性,否则 memo 会失效。
评分维度:
- 能区分三者作用(35%)
- 能说明使用场景(30%)
- 能提醒不要滥用并提到 Profiler(20%)
- 能说明依赖完整性对 memo 的影响(15%)
参考答案:
Context 提供了一种跨组件层级传递数据的方式,避免 props 层层透传(props drilling)。
基本用法:
const ThemeContext = createContext('light');
function App() {
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
function Toolbar() {
const theme = useContext(ThemeContext);
return <div>{theme}</div>;
}
注意事项:
- Context 适合低频变化的全局数据,如主题、语言、用户信息。
- 不适合高频更新场景,因为 Provider value 变化会导致所有消费组件重渲染。
- 可结合
useMemo拆分 Context,减少不必要更新。
评分维度:
- 能解释 Context 作用(30%)
- 能写出基本用法(40%)
- 能说明性能注意事项(30%)
参考答案:
主要变化:
<Routes>替代<Switch>:Routes 支持相对路由和嵌套路由声明。element替代component:直接写 JSX,更灵活。useNavigate替代useHistory:const navigate = useNavigate(); navigate('/home');- 嵌套路由声明化:子路由在父路由中直接声明,配合
<Outlet />渲染。 - Hook API 调整:移除
useParams之外的部分旧 API,新增useLocation、useMatch等。 - 路由配置对象:支持
useRoutes进行配置化路由。
v6 整体更强调声明式、嵌套和相对路径,适合构建复杂路由结构。
评分维度:
- 能说出 3 个以上变化(40%)
- 能说明 API 变化(30%)
- 能说明嵌套路由改进(30%)
高级题
参考答案:
Concurrent Mode(并发特性,React 18 正式引入)允许 React 同时准备多个版本的 UI,并根据任务优先级决定何时提交到屏幕。
核心 API:
- useTransition:把非紧急更新标记为 transition,避免阻塞用户输入。
const [isPending, startTransition] = useTransition(); startTransition(() => setQuery(value)); - useDeferredValue:延迟更新某个值,让紧急更新先渲染。
- Suspense:声明式处理异步边界,支持数据获取和流式 SSR。
解决的问题:
- 大量数据更新时界面卡顿。
- 用户输入被渲染阻塞。
- 更好的加载状态控制。
评分维度:
- 能解释并发概念(30%)
- 能说出主要 API(40%)
- 能说明解决的问题(30%)
参考答案:
React 18 主要特性:
- 自动批处理(Automatic Batching):在 setTimeout、Promise 等异步回调中也能自动合并多次状态更新,减少重渲染。
- Concurrent Rendering:可中断渲染,提升交互响应。
- 新的 Root API:
const root = ReactDOM.createRoot(document.getElementById('root')); root.render(<App />); - useTransition / useDeferredValue:区分紧急与非紧急更新。
- Suspense 改进:支持数据获取、流式 SSR(Streaming SSR)。
- useId:生成稳定唯一 ID,适用于表单 label 与 SSR hydration。
- Strict Mode 行为调整:开发环境下会故意双重调用某些函数,帮助发现副作用。
评分维度:
- 能说出 4 个以上特性(40%)
- 能解释 createRoot(30%)
- 能说明自动批处理(30%)
参考答案:
选择应基于应用规模和状态特征:
| 场景 | 推荐方案 |
|---|---|
| 组件局部状态 | useState / useReducer |
| 跨组件低频状态 | Context + useState/useReducer |
| 中小型应用 | Zustand、Jotai、Recoil |
| 大型应用、复杂流程 | Redux、MobX、Redux Toolkit |
| 服务端状态 | React Query / SWR / RTK Query |
选择依据:
- 状态复杂度与更新频率。
- 是否需要中间件、时间旅行调试。
- 团队熟悉度和生态成熟度。
- 是否需要服务端缓存与自动同步。
避免把所有状态都放全局,优先使用局部状态,减少不必要的耦合。
评分维度:
- 能说出 3 种以上方案(30%)
- 能说明选择依据(40%)
- 能避免过度使用全局状态(30%)
参考答案:
React Server Components(RSC)是在服务端渲染的组件,不会打包到客户端。它们可以直接访问数据库、文件系统等服务端资源,只把渲染结果发送到客户端。
与 SSR 的区别:
- SSR:生成完整 HTML,客户端仍需下载组件 JS 并 hydrate。
- RSC:不生成 HTML,而是返回特殊序列化数据;组件代码不进入 bundle,减少客户端 JS 体积。
特点:
- 可访问服务端资源。
- 与客户端组件可组合使用。
- 天然适合数据密集型页面。
Next.js App Router 是当前 RSC 的主要实践载体。
评分维度:
- 能解释 Server Components(40%)
- 能说明与 SSR 区别(30%)
- 能说明优势与典型场景(30%)
参考答案:
React 性能优化应遵循“先测量,后优化”:
- 避免不必要渲染:
React.memo、useMemo、useCallback。 - 拆分任务:
useTransition、useDeferredValue把非紧急更新推迟。 - 代码分割:
React.lazy+Suspense,按路由/组件懒加载。 - 长列表优化:虚拟列表(react-window、react-virtualized)。
- Context 优化:拆分 Context、使用
useMemo缓存 value。 - 状态管理优化:避免全局状态滥用,服务端状态用 React Query/SWR。
- 图片与资源优化:懒加载、WebP、响应式图片。
- 使用 Profiler:定位真实瓶颈。
避免过早优化,先通过 React DevTools Profiler 确认问题所在。
评分维度:
- 能说出 4 种以上方法(40%)
- 能解释原理(30%)
- 能提醒不要过早优化并提到 Profiler(30%)
补充题
参考答案:
错误边界(Error Boundary)是 React 组件,用于捕获子组件树中的 JavaScript 错误,防止整个应用崩溃,并展示兜底 UI。
特点:
- 只能捕获渲染期、生命周期和构造函数中的错误。
- 无法捕获事件处理函数、异步代码、SSR、自身错误的错误。
- 必须使用类组件实现,通过
static getDerivedStateFromError或componentDidCatch。
示例:
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError() {
return { hasError: true };
}
render() {
return this.state.hasError ? <h1>出错了</h1> : this.props.children;
}
}
评分维度:
- 能解释概念(40%)
- 能说明限制(30%)
- 能写出基本示例(30%)
参考答案:
使用 useRef(函数组件)或 createRef(类组件)获取 DOM 节点。
示例:
function Input() {
const inputRef = useRef(null);
useEffect(() => {
inputRef.current.focus();
}, []);
return <input ref={inputRef} />;
}
ref 也可以指向组件实例(类组件)或自定义值。注意 ref 不会触发组件重渲染,修改 ref.current 是副作用,应在事件处理或 useEffect 中操作。
评分维度:
- 能说出 useRef/createRef(40%)
- 能写出示例(30%)
- 能说明 ref 的特性与注意事项(30%)
参考答案:
Portal 允许把子节点渲染到父组件 DOM 树以外的节点,常用于模态框、提示框、抽屉等需要脱离当前布局上下文的场景。
示例:
import { createPortal } from 'react-dom';
function Modal({ children }) {
return createPortal(
<div className="modal">{children}</div>,
document.getElementById('modal-root')
);
}
Portal 中的子组件事件仍会冒泡到 React 组件树中的父组件,因此事件行为与普通子组件一致。
评分维度:
- 能解释概念(40%)
- 能说明使用场景(30%)
- 能写出基本示例(30%)
参考答案:
SyntheticEvent 是 React 对浏览器原生事件的跨浏览器包装对象,提供一致的 API(如 stopPropagation、preventDefault)。
特点:
- 抹平浏览器差异,开发者无需判断 IE/Chrome 的不同 API。
- 采用事件委托:React 17 之前委托到
document,React 17 之后委托到 Root 容器。 - SyntheticEvent 会被复用(事件池,React 17 之前),因此异步访问事件属性需先
e.persist();React 17 之后已取消事件池。
示例:
function Button() {
const handleClick = (e) => {
e.preventDefault();
console.log(e.target);
};
return <button onClick={handleClick}>Click</button>;
}
评分维度:
- 能解释合成事件(40%)
- 能说明跨浏览器一致性和事件委托(30%)
- 能提到事件池变化(30%)
参考答案:
React 的未来发展方向:
- Server Components 普及:减少客户端 JS,提升数据密集型页面性能。
- 并发特性成为默认:useTransition、useDeferredValue、Suspense 会越来越常用。
- 与框架深度集成:Next.js、Remix 等全栈框架成为 React 应用的主流载体。
- 边缘渲染与流式 SSR:借助 Edge Function、Streaming 提升首屏与交互体验。
- AI 辅助开发:结合 AI 工具提升组件生成、测试、文档效率。
作为开发者,应持续关注 React 核心原理(Fiber、Hooks、并发),同时根据业务场景选择合适的技术栈,避免盲目追新。
评分维度:
- 能说出 3 个以上趋势(40%)
- 能分析原因(30%)
- 能表达理性学习态度(30%)
参考答案:
React 19 主要新特性:
- Actions:把异步数据变更函数标记为 action,自动处理 pending 状态。
- useOptimistic:乐观更新,异步操作完成前先更新 UI。
- use Hook:在渲染中读取 Promise / Context,配合 Suspense 使用。
- React Compiler:编译时自动记忆化,减少手动
useMemo/useCallback/React.memo。 - Server Actions:客户端直接调用服务端函数。
- 改进的表单处理:原生表单与 React 状态更深度集成。
示例:
function Messages({ messages }) {
const [optimisticMessages, addOptimisticMessage] = useOptimistic(
messages,
(state, text) => [...state, { text, sending: true }]
);
async function sendMessage(formData) {
const text = formData.get("message");
addOptimisticMessage(text);
await deliverMessage(text);
}
return (
<form action={sendMessage}>
{optimisticMessages.map((msg, i) => (
<div key={i} style={{ opacity: msg.sending ? 0.5 : 1 }}>{msg.text}</div>
))}
<input name="message" />
<button>发送</button>
</form>
);
}
评分维度:
- 能说出 4 个以上新特性(40%)。
- 能解释 Actions / useOptimistic / use 至少一个(30%)。
- 能说明 React Compiler 的意义(30%)。
参考答案:
Server Actions 允许在服务端定义函数,客户端像调用普通函数一样调用它们,无需手写 REST/GraphQL API。
示例:
// actions.js
"use server";
export async function updateName(formData) {
await db.user.update({ where: { id: 1 }, data: { name: formData.get("name") } });
}
// page.jsx
import { updateName } from "./actions";
export default function Page() {
return <form action={updateName}><input name="name" /><button>更新</button></form>;
}
优势:
- 减少样板代码。
- 类型安全。
- 可直接访问数据库和内部服务。
- 渐进增强,JS 禁用时原生表单仍可工作。
安全:必须验证输入、校验权限、防止 CSRF。
评分维度:
- 能解释 Server Actions 概念(30%)。
- 能写出基本示例(30%)。
- 能说明优势和注意事项(40%)。
参考答案:
Hydration 是 SSR/SSG 应用把服务端生成的 HTML 激活为可交互组件的过程。如果服务端和客户端首次渲染结果不一致,React 会报 hydration 错误。
常见原因:
new Date()、Math.random()、crypto.randomUUID()在两端结果不同。- 客户端从
localStorage读取主题/语言。 - 浏览器 API(
window、document)在服务端不存在。 - 第三方脚本修改了 React 管理的 DOM。
解决方案:
- 把客户端逻辑放到
useEffect或ClientOnly组件中。 - 统一数据源,时间/随机数从服务端传入或固定。
- 对无法避免的不一致加
suppressHydrationWarning。 - 监控 hydration 错误并接入错误上报。
评分维度:
- 能解释 hydration 概念(30%)。
- 能说出 3 个以上常见原因(30%)。
- 能给出 3 个以上解决方案(40%)。
领域编号:E06 React
最后更新:2026-06-24