React面试宝典【Part1:基础核心】
一、React 基础核心
1. React核心特点
声明式UI、组件化、单向数据流、虚拟DOM、JSX、函数式编程思想;React只是View层库,不是完整框架。
详解
- 组件化:UI 拆分为独立、可复用组件,单向数据流,便于维护和复用。
- 虚拟 DOM:通过内存中虚拟 DOM 对比差异(Diff),最小化真实 DOM 操作,提升性能。
- JSX 语法:JS 与 HTML 结合,声明式编写 UI,结构清晰。
- 单向数据流:数据自上而下流动,状态可预测,便于调试。
- Diff 算法:高效对比虚拟 DOM 树,只更新变化部分。
- 函数组件 + Hooks:替代类组件,实现状态与副作用逻辑复用。
总结:React本质是一个状态管理容器,而非单纯的UI库。它的核心特点都是为了解决团队协作下的可维护性和确定性渲染问题。
2. JSX是什么?编译原理
JSX是JS语法糖,编译后转为React.createElement(type, props, children);
区别HTML:className、htmlFor、大括号表达式、防止XSS自动转义、不能直接返回相邻JSX(需Fragment)。
详解
JSX是什么
JSX 是 JavaScript 的语法扩展,HTML 结构 + JS 逻辑写在一起,React 用来声明式描述 UI,本质不是 HTML,也不是浏览器直接识别的语法。
编译原理
- JSX 会被 Babel(或 TypeScript)编译
- 本质是转换成
React.createElement()函数调用 - 最终生成虚拟 DOM 对象(React Element)
示例
<div className="box">hello</div>
编译后:
React.createElement('div', { className: 'box' }, 'hello')
核心要点
- JSX → 编译 →
createElement→ 虚拟 DOM 对象 - 浏览器不认识 JSX,必须编译
- 支持表达式
{}、组件嵌套、属性小驼峰(className、onClick)
3. 虚拟DOM(Virtual DOM)是什么?优缺点
用JS对象描述DOM结构;数据变更生成新VDOM,通过Diff对比最小化更新真实DOM。
✅优点:跨平台、批量DOM更新、解耦UI与DOM;
❌缺点:额外内存开销;不要迷信虚拟DOM,原生高频操作依然更快。
详解
虚拟 DOM是用普通 JS 对象模拟真实 DOM 树结构,是 React 对真实 DOM 的一层抽象。
更新时先在虚拟 DOM 做 Diff 对比,再批量、最小化操作真实 DOM,提升渲染效率。
优点
- 减少频繁、直接操作真实 DOM,大幅提升性能
- 跨平台(React Native、SSR 等),同一套虚拟 DOM 可适配不同渲染层
- 声明式开发,只需关心状态变化,无需手动操作 DOM
- 批量更新 + Diff 算法,最小化 DOM 操作
缺点
- 首次渲染有额外 JS 对象创建与 Diff 开销
- 简单场景下,直接操作 DOM 反而更快
- 内存占用略高(维护虚拟 DOM 树)
4. key作用?为什么不能用index?
key帮助Diff算法识别节点,建立新旧Fiber节点映射;
index风险:数组增删、排序时key不变,导致组件状态错乱(表单值、DOM动画异常);
✅最佳实践:使用稳定唯一业务ID。
详解
key 作用
- 给列表项
唯一标识,帮助 React Diff 算法快速定位节点变化,提升列表更新效率。 - 避免组件复用、状态错乱,保证列表项正确复用与重新排序。
为什么不能用 index 做 key
- 列表
增删/排序时,index 会跟着变,导致 React 误判节点身份。 - 引发组件状态错乱、渲染错误、性能劣化。
- 只有列表完全静态、永不增删排序时,index 才勉强可用,但仍不推荐。
正确做法
使用数据唯一 ID(id、uuid 等)作为 key。
5. 受控组件 vs 非受控组件
- 受控组件:value由state控制,onChange同步更新;表单数据统一托管React(推荐,大部分场景)
- 非受控组件:用ref获取DOM原生值;适合文件上传、一次性获取值、大量简单表单追求性能。
详解
受控组件 - 表单值由 React state 管理,值随 state 同步更新
- 通过
onChange同步 state,数据可预测、可校验 - 适合需要实时校验、联动、动态禁用等复杂场景
import { useState } from 'react';
function Controlled() {
const [value, setValue] = useState('');
return (
<input
value={value}
onChange={(e) => setValue(e.target.value)}
placeholder="受控组件"
/>
);
}
非受控组件
- 表单值由 DOM 自身管理,React 不控制
- 通过
ref获取 DOM 节点值,类似原生表单 - 简单、少代码,适合一次性提交、无需实时控制的场景
import { useRef } from 'react';
function Uncontrolled() {
const inputRef = useRef(null);
const handleSubmit = () => {
console.log(inputRef.current.value);
};
return (
<>
<input ref={inputRef} placeholder="非受控组件" />
<button onClick={handleSubmit}>提交</button>
</>
);
}
对比
- 受控:数据可控,代码稍多,推荐绝大多数场景
- 非受控:代码简洁,不可控,适合简单表单
-
6. 类组件 vs 函数组件,官方为什么主推函数组件
- 无this指向陷阱,代码简洁
- Hooks可以复用状态逻辑(类组件只能HOC/render-props,嵌套地狱)
- 没有实例,内存开销更小
- 更好适配并发渲染、React未来架构
&gt; 补充:类组件并未废弃,新项目优先函数+Hooks
详解
一、类组件 vs 函数组件
- 类组件:ES6 class 继承
React.Component,有this、生命周期、state、实例方法。 - 函数组件:普通函数 + Hooks,无
this、无生命周期,更轻量。
二、官方主推函数组件的原因
-
代码更简洁、体积更小
无冗余 class 语法、无this绑定问题,逻辑更直观。 -
Hooks 让逻辑复用更优雅
替代高阶组件、render props 等复杂复用模式,自定义 Hook 可独立拆分状态逻辑。 -
无 this 困扰
避免 bind、箭头函数、this 指向混乱,降低出错率。 -
更容易编译优化
函数组件是纯函数,利于 tree-shaking、编译器优化,未来性能潜力更大。
【补充】Tree-shaking:基于 ES6 模块(import/export)的死代码消除,打包时只打包用到的代码,剔除没被引用的部分。 -
更好的逻辑隔离与组合
按业务逻辑拆分,而非按生命周期拆分,代码可读性与可维护性更强。 -
趋势与生态统一
React 团队长期投入 Hooks,新特性只面向函数组件,类组件进入维护模式。
7. React合成事件(事件委托)
React17前委托到document,18/17委托到root容器;
- 合成事件是模拟事件对象,不是原生event;
- 事件冒泡规则:合成事件冒泡 ≠ 原生事件;原生阻止冒泡不会影响合成事件;
- 坑:异步中无法直接拿到event(事件池复用,React17移除事件池)。
详解
一、合成事件(SyntheticEvent)
React 自己封装的跨浏览器兼容事件对象,不是原生事件。 - 抹平浏览器差异,API 统一
- 做了事件池复用,提升性能
- 异步里要用需
event.persist()
二、事件委托原理
React 几乎把所有事件委托到 document(React 17 移到 root 容器):
- 真实 DOM 只在顶层绑定少量事件
- 事件冒泡到顶层后,React 统一分发、执行对应组件 handler
- 减少事件绑定数量,大幅提升性能,尤其长列表
三、特点
- 性能更好:少绑定、少内存
- 兼容统一:不用写
e || window.event - 阻止冒泡:
e.stopPropagation()有效 - 原生事件与合成事件混用可能出现顺序问题
例子:
function SyntheticDemo() {
// 这里的 e 是 React 合成事件,不是原生 event
const handleClick = (e) => {
console.log(e); // SyntheticBaseEvent
console.log(e.target);
console.log(e.nativeEvent); // 拿到原生事件
// 阻止冒泡/默认行为
e.stopPropagation();
e.preventDefault();
};
return <button onClick={handleClick}>点我</button>;
}
事件委托体现:
页面上只在根容器绑定一次事件
所有子元素点击都冒泡上去,React 统一派发
大量 DOM 时性能远好于逐个绑定