第 22 卷
• 提交阶段(Commit):同步、不可中断,负责根据协调阶段标记的 effectTag,批量执行真实
• 优势:协调阶段的异步可中断特性不会影响真实 DOM 状态,提交阶段的同步执行确保 DOM 操作
的原子性,避免 DOM 状态混乱。
4. 性能优化:无缓存 vs 双缓存机制
• React 15 协调算法:
- 无缓存机制,每次更新都需要重新创建完整的虚拟 DOM 树,再与旧树进行 Diff 对比;
- 问题:重复创建虚拟 DOM 树增加内存开销,Diff 对比效率较低。
• React Fiber 协调算法:
• 引入「双缓存机制」,通过 Fiber 节点的 alternate 属性维护两棵 Fiber 树:
- current 树:当前渲染在页面上的 Fiber 树,与真实 DOM 一一对应;
• workInProgress 树:正在进行更新的 Fiber 树,用于执行 Diff 对比、标记 effectTag,更新过程中
不会影响 current 树;
• 更新完成后,通过切换 current 树与 workInProgress 树的引用,完成 DOM 更新,旧的 current
树变为下一次更新的 workInProgress 树;
• 优势:避免重复创建 Fiber 节点,减少内存开销;同时,workInProgress 树的异步构建不会影响
当前页面的渲染状态,提升稳定性。
5. 关键总结
React Fiber 的协调算法是对传统栈协调器的「颠覆性重构」,核心目标是「解决同步渲染的卡顿问题」。通过“链表遍历替代递归遍历”“引入优先级调度”“分离协调与提交阶段”“双缓存机
制”四大核心优化,实现了异步可中断的协调过程,既保障了关键操作的响应速度,又提升了渲染效率与稳定性。
三十三、什么是虚拟 DOM?以及它如何帮助提升 React 应用的性能?
- 什么是虚拟 DOM?
虚拟 DOM(Virtual DOM,简称 VDOM)是 React 等前端框架中用于「描述真实 DOM 结构的轻量级 JavaScript 对象」,是对真实 DOM 的抽象映射。它不直接操作浏览器的真实 DOM,而是通过对象的
形式记录真实 DOM 的关键信息(如节点类型、属性、子节点等),本质是“在内存中模拟 DOM 树的结构”。
例如,一个真实的 div 节点:
对应的虚拟 DOM 对象(简化版)可能是:
Code block
1{
| 2 | type: 'div', //节点类型(原生DOM类型/组件类型) |
|---|---|
| 3 | props: { //节点属性 |
| 4 | className: 'container', |
| 5 | id: 'root' |
| 6 | }, |
| 7 | children: [ //子节点(同样是虚拟DOM对象) |
| 8 | { |
| 9 | type: 'p', |
| 10 | props: {}, |
| 11 | children: ['Hello React'] //文本节点 |
| 12 | } |
| 13 | ], |
| 14 | key: null //用于Diff算法的唯一标识(列表节点常用) |
| 15 | } |
核心特点:
• 轻量级:仅包含真实 DOM 的必要信息,不包含浏览器 DOM 节点的复杂属性与方法,操作成本远
低于真实 DOM;
• 内存中存在:虚拟 DOM 是 JavaScript 对象,存储在内存中,操作时不会触发浏览器的重排
(Reflow)与重绘(Repaint);
- 可描述性:完整映射真实 DOM 的层级结构与属性信息,能够精准描述页面的 UI 状态。
- 虚拟 DOM 如何提升 React 应用的性能?
浏览器操作真实 DOM 的成本极高(尤其是重排/重绘),而虚拟 DOM 通过「减少真实 DOM 的操作次数」和「最小化 DOM 更新范围」,显著提升 React 应用的性能,具体优化逻辑可分为三个核心步骤:
(1)避免频繁操作真实 DOM,减少重排/重绘
在传统原生 JavaScript 开发中,若需要频繁更新页面(如列表数据刷新、表单输入联动),会直接多次操作真实 DOM,每次操作都会触发浏览器的重排/重绘,导致性能开销巨大。
虚拟 DOM 的优化逻辑:
• React 中所有的 UI 更新都会先操作虚拟 DOM(内存中的 JavaScript 对象),而非直接操作真实
• 多次虚拟 DOM 操作会被合并为一次真实 DOM 更新,避免了频繁操作真实 DOM 导致的多次重排/
• 例如:连续修改一个元素的 className 和文本内容,React 会先在虚拟 DOM 中完成两次修改,再
通过一次真实 DOM 操作同步到页面,而非两次独立的 DOM 操作。
(2)通过 Diff 算法,最小化 DOM 更新范围
当应用状态变化时,React 会创建一棵新的虚拟 DOM 树,然后通过「Diff 算法」对比新旧两棵虚拟 DOM 树的差异,找出“需要更新的最小部分”,仅将差异部分同步到真实 DOM 中,而非整体替换整
个 DOM 树。
核心优势:
- 传统 DOM 更新可能需要替换整个父节点及其所有子节点,即使只有一个子节点变化;
• 虚拟 DOM + Diff 算法能精准定位变化的节点,仅更新变化的部分,避免了不必要的 DOM 操作,极
大降低了性能开销;
• 例如:列表中某一个列表项的文本变化,React 会通过 Diff 算法找到该列表项对应的虚拟 DOM 节
点,仅更新该节点的文本内容,而不影响列表中的其他节点。
(3)抽象 DOM 操作,提升开发效率与跨平台能力
虚拟 DOM 对 DOM 操作进行了抽象封装,开发者无需直接编写 DOM 操作代码,只需关注应用状态与 UI 的映射关系(如编写 JSX),React 会自动完成虚拟 DOM 到真实 DOM 的同步。
间接性能优化:
- 减少开发者手动编写低效 DOM 操作代码的可能性(如冗余的 DOM 查询、重复的 DOM 修改);
• 虚拟 DOM 是平台无关的抽象层,除了映射浏览器 DOM,还可映射到其他平台(如 React Native
中的原生组件),实现“一次编写,多平台运行”,避免了为不同平台单独优化 DOM 操作的成本。
3. 注意事项:虚拟 DOM 并非“更快”,而是“更高效”
虚拟 DOM 本身存在一定的性能开销(如创建虚拟 DOM 对象、执行 Diff 算法),因此对于简单的、单次的 DOM 操作,虚拟 DOM 的性能可能不如直接操作真实 DOM。但在复杂应用中,虚拟 DOM 通
过“合并操作”和“最小化更新”带来的性能优势,远大于其自身的开销,整体提升应用的性能与可维护性。
三十四、React 的 Diff 算法是如何工作的(也称为 Reconciliation 算
法)?请解释它如何比较两个虚拟 DOM 树的。
React 的 Diff 算法(又称协调算法/Reconciliation 算法)是 React 核心优化机制之一,核心目标是「高效对比新旧两棵虚拟 DOM 树的差异,找出需要更新的最小部分」,从而最小化真实 DOM 的操
全站分卷索引 · 28 卷
- 1第 1 卷
- React18 核心面试题详细解答
- 一、React18 核心源码精读
- 1. 核心架构分层(源码核心目录)
- • 调度层(Scheduler):核心包 scheduler,负责任务优先级排序、时间切片(Time
- • 协调层(Reconciler):核心包 react-reconciler,负责虚拟 DOM 对比(Diff 算法)、
- • 渲染层(Renderer):如 react-dom(浏览器端)、 react-native(原生端),负责将协
- 2. 核心新特性源码关键点
- • 核心概念:不再是同步阻塞式渲染,而是可以中断、暂停、恢复或放弃的渲染流程,源码中通过
- • 关键实现:基于 Fiber 架构的「可中断遍历」,Fiber 节点作为最小调度单元,每个 Fiber 节点遍历
- • 核心逻辑:源码中通过 unstable_batchedUpdates 方法封装批处理逻辑,React18 中取消
- • 关键细节:批处理的本质是延迟执行 flushSync(刷新状态更新),当所有批量更新收集完成
- • 核心 API: useTransition、 startTransition,源码中通过标记更新的优先级
- 2第 2 卷
- • 声明式编程(Declarative):开发者只需描述「UI 应该是什么样子」(基于状态),无需关心
- • 组件化思想(Componentization):将复杂 UI 拆分为独立、可复用的组件,组件拥有自己的状
- • 单向数据流(Unidirectional Data Flow):状态(State)只能从父组件传递给子组件(通过
- • 虚拟 DOM(Virtual DOM)与不可变状态(Immutability):
- 2. React 与 Vue 的异同
- 四、React 事件机制(与普通 DOM 事件的区别及工作原理)
- 1. 什么是 React 事件机制
- 2. React 合成事件与普通 DOM 事件的区别
- 3. React 事件机制的工作原理(三步流程)
- • React 在组件挂载时,会遍历组件的事件属性(如 onClick),将其注册为事件委托(React16
- • 注册时,React 会为每个合成事件分配一个唯一的标识,并将事件回调函数与标识关联,存储在内
- 3第 3 卷
- 五、React.Component 与 React.PureComponent 的区别
- 1. 核心区别:shouldComponentUpdate 方法
- • React.Component:默认没有实现 shouldComponentUpdate 方法,该方法默认返回
- ◦ 若需优化性能,开发者需手动实现 shouldComponentUpdate 方法,手动对比
- • React.PureComponent:自动实现了 shouldComponentUpdate 方法,该方法内部会对
- 2. 其他补充差异
- ◦ React.Component:适用于所有类组件场景,尤其是组件的 props 或 state 为复杂嵌套
- ◦ React.PureComponent:适用于组件的 props 和 state 均为基本类型,或为简单对象/数
- ◦ PureComponent 的浅比较存在「局限性」:若 props 或 state 是复杂嵌套对象/数组,
- ◦ React.PureComponent 对应函数组件中的 React.memo( React.memo 是高阶组件,对
- 六、React 的高阶组件(HOC):定义、与普通组件的区别及使用场景
- 1. 什么是高阶组件(HOC)
- 4第 4 卷
- 2. 组件重新渲染时,render 函数内部发生的事情
- • 执行 render 函数/函数组件主体:
- • 类组件:执行 render 方法,执行过程中会解析组件内部的 JSX 语法,同时计算组件内的变量、
- • 函数组件:直接执行函数组件的主体代码,从函数开始到 return 语句结束,包括内部变量声明、
- • render 函数是「纯函数」(理想情况下),不应包含副作用操作(如修改状态、调用接口、操作
- • 重新渲染时,若 Diff 算法发现新旧虚拟 DOM 无差异,React 会跳过真实 DOM 的更新操作,这是
- 八、避免 React 中不必要渲染的性能优化方案
- 1. 类组件优化方案
- • 使用 React.PureComponent:纯组件自动实现了 shouldComponentUpdate 方法,对
- • 手动重写 shouldComponentUpdate:对于复杂场景(如嵌套对象的 props),可手动实现
- • 避免在 render 中创建新对象/新函数:render 中创建的新对象(如 { name: 'xxx' })、新
- 2. 函数组件优化方案
- 5第 5 卷
- 1. 默认异步场景(大部分日常开发场景)
- • 常见场景:
- • React 生命周期方法(如 componentDidMount、 componentDidUpdate、
- • 常见场景:
- 3. React18 中的变化:自动批处理(Automatic Batching)
- 总结
- • 非 React 控制的环境(原生事件、定时器、Promise):React18 前同步,React18 后默认异步
- • 无论同步还是异步,都不建议依赖 setState 后的 this.state 做后续逻辑,如需使用更新
- 十一、React 中 setState 的批量更新过程具体实现
- 1. 核心前提:批量更新上下文标识
- • 进入批量更新上下文时(如合成事件回调、生命周期方法执行前),将 isBatchingUpdates
- • 退出批量更新上下文时(如合成事件回调、生命周期方法执行完成后),将
- 6第 6 卷
- 3. React18 自动批处理(Automatic Batching)的扩展实现
- • 将「批量更新上下文」从「全局标识」改为「基于根节点的上下文管理」(通过 createRoot
- • 在所有异步场景(原生事件、 setTimeout、 Promise 回调等)中,自动包裹批量更新逻辑,
- • 若需强制取消批量更新(立即执行更新),可使用 ReactDOM.flushSync 方法,该方法会强制
- • 核心价值:减少 DOM 操作次数。若没有批量更新,多次 setState 会触发多次渲染,而批量更
- • 注意事项:在批量更新上下文中,无法立即获取 this.state 的最新值(因为状态尚未合并更
- 十二、React 的生命周期及各阶段方法
- 1. 挂载阶段(组件从无到有,首次渲染到 DOM)
- 2. 更新阶段(组件状态/Props 变化,触发重新渲染)
- 3. 卸载阶段(组件从 DOM 中移除,销毁)
- • componentWillUnmount():组件卸载前执行,仅执行一次。常用于:解绑原生 DOM 事件
- 4. 特殊生命周期方法(错误处理)
- 7第 7 卷
- • 优化原理: PureComponent 自动实现了 shouldComponentUpdate 方法,内部对
- • 若浅比较无变化, shouldComponentUpdate 返回 false,阻止组件重新渲染;若有变化则
- • 适用场景:组件的 Props/State 为基本类型或简单引用类型(无嵌套结构),且渲染仅依赖
- • 优化方法 2:手动重写 shouldComponentUpdate:
- • 优化原理:开发者根据组件的实际渲染依赖,自定义对比逻辑,仅在关键属性变化时返回
- • 优势:适用于复杂场景(如嵌套对象的 Props),可通过「深比较关键属性」实现精准优化(注
- 2. 辅助优化阶段:挂载阶段与卸载阶段
- • 挂载阶段优化:componentDidMount 中请求数据:
- • 优化原理:避免在 render 或 constructor 中请求数据(会导致多次请求或阻塞渲染),
- • 卸载阶段优化:componentWillUnmount 中清理资源:
- • 优化原理:若组件卸载前未清理资源(如定时器、原生事件监听、未完成的网络请求),这些资源
- 3. 其他生命周期相关优化:避免在 render 中创建新对象
- 8第 8 卷
- • 实现逻辑:父组件定义回调函数,通过 Props 传递给子组件;子组件触发事件时,调用该回调函
- • 方式 3:父组件获取子组件实例/DOM:Refs 传递:
- • 实现逻辑:父组件通过 useRef(函数组件)或 createRef(类组件)创建 Ref 对象,传递给
- • 适用场景:父组件需要直接操作子组件的 DOM(如获取输入框焦点)或调用子组件的方法(如类
- • 示例(函数组件转发 Ref):
- 2. 兄弟组件通信
- • 方式:通过共同父组件中转:
- • 实现逻辑:兄弟组件的通信需借助它们的共同父组件:兄组件通过回调函数将数据传递给父组件,
- • 适用场景:兄弟组件之间需要共享数据或交互(如兄组件点击按钮,弟组件展示对应数据),且组
- • 示例:
- 3. 跨层级组件通信(祖孙、远亲组件)
- • 方式 1:Context API(React 内置):
- 9第 9 卷
- 1. 对 Redux 的核心理解
- • 单一数据源:整个应用的状态集中存储在一个“状态树”(store)中,避免多个组件分散存储状态
- • 状态不可变:状态(state)是只读的,不能直接修改,只能通过触发“动作”(action)生成新的
- • 使用纯函数修改状态:通过“减速器”(reducer)函数处理状态更新,reducer 是纯函数(无副
- 2. Redux 主要解决的问题
- • 解决跨组件状态共享问题:在 React 中,父子组件可通过 Props 传递状态,但跨层级组件(如祖孙
- • 解决状态变化不可预测问题:组件内部通过 setState 直接修改状态时,状态变化逻辑分散在各
- • 解决异步状态管理复杂问题:React 组件中处理异步逻辑(如网络请求)时,异步结果修改状态的
- • 解决组件复用状态逻辑困难问题:多个组件需要复用相同的状态管理逻辑(如用户登录状态、表单
- 1. Redux 核心工作原理
- • store:全局唯一的状态容器,存储整个应用的状态树(state),提供 getState()(获取状
- • action:状态变化的“动作描述”,是修改状态的唯一触发源,必须是纯对象,且必须包含
- 10第 10 卷
- • 示例(登录异步请求):
- • 核心原理:通过“生成器函数(generator)”编写异步逻辑,将异步流程与组件解耦,支持更复
- • 核心思路:监听特定的 action,当 action 被 dispatch 时,执行对应的生成器函数处理异步逻辑,
- • 核心原理:基于 RxJS 库,将 action 视为“数据流”,通过响应式操作符(如 switchMap、
- 2. Redux 中间件的定义与作用
- 3. 编写 Redux 中间件的步骤(自定义中间件)
- • 第一层函数(store):接收 Redux 的 store 对象,可通过 store.getState() 获取当前状
- • 第二层函数(next):接收“下一个中间件的 dispatch 方法”(若没有下一个中间件,则是原始
- • 第三层函数(action):接收组件 dispatch 的 action,在此层编写核心处理逻辑(如异步、日
- 11第 11 卷
- 12第 12 卷
- 2. Redux 与 Vuex 共有的设计思想
- • 集中式状态管理:将应用中所有组件共享的状态集中存储在一个全局容器(store)中,避免状态分
- • 单向数据流:状态流转遵循“单向”规则,确保状态变化可预测。核心流程均为“组件触发操作
- • 分离状态修改逻辑:将状态修改逻辑与组件 UI 逻辑分离,组件仅负责触发操作(如点击按钮触发
- • 状态可追踪:通过规范的状态修改流程,使得每一次状态变化都有明确的触发源头(如 action 描
- • 组件解耦:通过全局 store 实现组件间的间接通信,无需依赖组件层级关系(如父子、跨层级),
- • 只能在函数组件的顶层调用(不可在条件判断、循环、嵌套函数中调用),确保 Hooks 执行顺序
- • 通过“钩子”的方式将 React 内部能力“注入”到函数组件中,无需编写类组件即可实现复杂功
- • useEffect:用于处理函数组件的副作用(如网络请求、DOM 操作、定时器),替代类组件的生命
- 2. React 团队引入 Hooks 的原因
- • 解决类组件 this 指向混乱问题:类组件中 this 指向动态变化(如事件回调中 this 可能为
- • 解决生命周期逻辑分散与冗余问题:类组件的生命周期方法(如 componentDidMount、
- 13第 13 卷
- 14第 14 卷
- 15第 15 卷
- • 严格遵循 Hook 使用规则:只能在函数组件顶层或其他自定义 Hook 中调用,不可在条件判断、循
- • 自定义 Hook 是逻辑复用,不是组件复用:每个组件调用自定义 Hook 时,内部的内置 Hooks 都
- • 避免过度封装:仅抽取真正复用的逻辑,简单逻辑无需封装为自定义 Hook,否则会增加代码复杂
- • 可组合多个自定义 Hook:一个自定义 Hook 内部可调用其他自定义 Hook,实现更复杂的逻辑
- 二十六、useCallback 和 useMemo 的区别及其使用场景
- 1. 核心定义与本质区别
- • useCallback:缓存的是「函数引用」,返回一个记忆化的函数。当组件重新渲染时,若依赖项未
- • useMemo:缓存的是「函数执行的结果」,返回一个记忆化的值。当组件重新渲染时,若依赖项
- 2. 语法对比
- 3. 关键区别总结
- 4. 使用场景详解
- 16第 16 卷
- 5. 注意事项
- • 不要过度使用:useCallback 和 useMemo 本身也有性能开销(需要存储缓存、对比依赖项),对
- • 依赖数组必须完整:若缓存的函数/计算依赖组件内的变量(如 state、props),必须将这些变量
- • useMemo 不可用于副作用:useMemo 的回调函数必须是纯函数(无 DOM 操作、网络请求等副作
- 二十七、useEffect 与 useLayoutEffect 在 React 中的不同及其使用场景
- 1. 核心区别:执行时机
- • useEffect:「异步执行」,在浏览器完成页面渲染(绘制 DOM)之后执行。React 会先更新
- • useLayoutEffect:「同步执行」,在 React 完成 DOM 更新但尚未将 DOM 绘制到页面之前执
- 2. 执行流程对比
- 3. 关键区别总结
- 4. 使用场景详解
- 17第 17 卷
- 18第 18 卷
- 19第 19 卷
- 20第 20 卷
- 21第 21 卷
- • 优先级调度,保障关键任务优先执行:
- • 为不同更新任务分配优先级,高优先级任务(如用户输入、动画)可打断低优先级任务,优先完成
- • 例如:用户在输入框打字时,输入事件的优先级高于列表数据的普通更新,Fiber 会优先处理输入
- • 减少不必要的计算,提升渲染效率:
- • 可中断的协调过程避免了因任务阻塞导致的重复计算(如 React 15 中若同步协调被打断,重新触
- • Fiber 节点的链表结构便于精准定位中断位置,恢复后继续处理,减少无效计算;同时,Fiber 对
- 三十一、在 React Fiber 中,什么是 Fiber 节点?它如何与传统的 React
- • 组件相关信息: type(组件类型,如函数组件、类组件、div 等原生 DOM 类型)、
- • 状态相关信息: memoizedState(组件的状态数据,如类组件的 state、函数组件的 Hooks 链
- • 链表结构信息: child(指向第一个子 Fiber 节点)、 sibling(指向兄弟 Fiber 节点)、
- • 任务调度信息: priority(当前 Fiber 节点对应的任务优先级)、 effectTag(标记需要执
- • 其他辅助信息: alternate(指向当前 Fiber 节点的备用节点,用于存储更新过程中的临时状
- 22第 22 卷
- • 提交阶段(Commit):同步、不可中断,负责根据协调阶段标记的 effectTag,批量执行真实
- • 优势:协调阶段的异步可中断特性不会影响真实 DOM 状态,提交阶段的同步执行确保 DOM 操作
- 4. 性能优化:无缓存 vs 双缓存机制
- • React 15 协调算法:
- • React Fiber 协调算法:
- • 引入「双缓存机制」,通过 Fiber 节点的 alternate 属性维护两棵 Fiber 树:
- • workInProgress 树:正在进行更新的 Fiber 树,用于执行 Diff 对比、标记 effectTag,更新过程中
- • 更新完成后,通过切换 current 树与 workInProgress 树的引用,完成 DOM 更新,旧的 current
- • 优势:避免重复创建 Fiber 节点,减少内存开销;同时,workInProgress 树的异步构建不会影响
- 5. 关键总结
- • 轻量级:仅包含真实 DOM 的必要信息,不包含浏览器 DOM 节点的复杂属性与方法,操作成本远
- • 内存中存在:虚拟 DOM 是 JavaScript 对象,存储在内存中,操作时不会触发浏览器的重排
- 23第 23 卷
- • 假设 1:同类型的组件产生相似的 DOM 结构,不同类型的组件产生不同的 DOM 结构:
- • 若两个组件类型相同(如都是 <div> 或都是自定义组件 <UserList>),则认为它们的 DOM 结构相
- • 若两个组件类型不同,则直接销毁旧组件对应的 DOM 结构,创建新组件对应的 DOM 结构,无需
- • 假设 2:列表节点通过唯一的 key 属性,可以确定节点在列表中的位置是否变化:
- • 对于列表类子节点(如 <ul> 下的多个 <li>),若未设置 key,React 会通过“索引”对比,可能导
- • 若设置了唯一的 key,React 可通过 key 快速定位节点的新增、删除、移动操作,提升列表对比效
- 2. Diff 算法的核心工作流程:分层对比(深度优先)
- • 节点类型不同(如旧节点是 <div>,新节点是 <p>):
- • 节点类型相同(如新旧节点都是 <div>):
- • 对比节点的属性(props):若属性有变化(如 className、id、事件绑定等),则更新真实
- • 非列表子节点(如 <div> 下的 <span>、<img> 等无序子节点):
- • 列表子节点(如 <ul> 下的多个 <li>,或通过 map 生成的子节点):
- 24第 24 卷
- • 索引 0:旧节点是“列表项 1”,新节点是“新列表项”,类型相同,React 会复用旧节点的 DOM
- • 结果:若原列表项 1 的输入框有内容,插入新节点后,新列表项的输入框会保留原列表项 1 的内
- • Diff 算法认为索引 0 的节点从 A 变为 B(销毁 A,创建 B),索引 1 的节点从 B 变为 A(销毁 B,
- • key 必须唯一:同一列表中不能有重复的 key,否则 React 会报警告,且 Diff 算法无法正确匹配节
- • key 必须稳定:key 不应随渲染次数变化(如不推荐用 Math.random() 生成 key),否则每次渲染
- • 避免用索引作为 key:仅当列表是“静态列表”(无插入、删除、排序操作)且无状态时,可临时
- 5. 与 Diff 算法的关系总结
- 1. 核心设计思路差异:“整体协调” vs “精准靶向”
- • React Diff 算法:
- • 核心思路是「整体协调(Reconciliation)」,更侧重“任务调度的兼容性”。由于 React 引入了
- • 设计前提是「组件类型决定 DOM 结构相似性」:若组件类型不同,直接销毁旧组件 DOM 树并重
- • Vue Diff 算法:
- 25第 25 卷
- • 设计前提是「模板编译优化」:Vue 基于模板语法开发,编译阶段会对模板进行静态分析(如标记
- 2. 子节点对比策略差异(核心差异点)
- • React:
- • key 的核心作用是「建立新旧节点的映射关系」,用于判断节点的“新增/删除/移动”。但 React
- • 4. 对于需要移动的节点,通过“最长递增子序列”计算最小移动步数(React16+ 引入,优化移动
- • Vue:
- • Vue2 和 Vue3 的列表 diff 均对“移动优化”做了深度适配,尤其是 Vue3 的“最长递增子序列”优
- • 1. Vue2:采用“双指针法”遍历新旧列表,通过 key 对比节点,同时记录节点移动、新增、删除
- • 2. Vue3:在 Vue2 基础上优化,通过“最长递增子序列”确定无需移动的节点,仅对其他节点进行
- • React:无编译阶段的静态节点标记,即使节点属性和内容完全不变(如静态文本、固定样式节
- • Vue:编译阶段会标记“静态节点”(如 <span>静态文本</span>)和“静态根节点”(包含多个
- • React:
- 26第 26 卷
- • 流式 SSR:服务端可分块发送 HTML 内容,先发送静态部分,再发送动态数据加载完成的部分,减
- • suspense fallback:在服务端渲染时,若组件数据未加载完成,可先渲染 fallback 占位符(如加
- 3. 开发体验与稳定性优化
- 4. 其他重要特性
- • useId:生成跨服务端和客户端的唯一 ID,解决服务端渲染时 ID 不匹配的问题(如表单标签关联、
- • useDeferredValue:延迟更新非紧急状态,类似 useTransition,但更侧重“值的延迟传递”(如
- 三十八、在 React 17 中,事件处理机制经历了哪些变化?特别是与之前版
- 1. 核心变化一:事件委托目标从 document 改为根节点(root)
- • 当用户触发真实 DOM 事件时,事件冒泡到 document 节点,被 React 统一的事件监听器捕获,再
- • 1. 若第三方库或原生代码修改了 document 的事件监听器(如阻止冒泡),会干扰 React 事件的
- • 3. 多个 React 应用共存时(如同一页面嵌入多个独立 React 应用),所有应用的事件都会委托到
- • 将事件委托的目标从 document 改为「应用的根节点」(即 ReactDOM.render 挂载的节点,如
- 27第 27 卷
- 28第 28 卷
- 四十、React 18 引入了新的协调器(Concurrent Mode)。请从源码角度
- 1. 源码核心依赖模块
- •(1)Scheduler(调度器):源码位置 react-scheduler,负责任务的优先级排序、时间切
- •(2)Reconciler(协调器):源码位置 react-reconciler,基于 Fiber 架构,负责组件树的
- •(3)Lane 模型(优先级模型):源码中通过二进制位表示任务优先级(如 ImmediateLane 最
- 2. 工作原理核心流程(源码视角)
- • 可中断判断: shouldYield() 函数由 Scheduler 提供,判断当前时间切片是否用尽(默认
- • 任务恢复:当高优先级任务执行完成或时间切片重新分配后,调度器会再次调用 workLoop,从
- • 任务放弃:若在中断期间,有更高优先级的更新任务覆盖了当前任务(通过 Lane 模型判断),则
- •(1)时间切片(Time Slicing):Scheduler 基于 requestIdleCallback 模拟实现时间切
- •(2)优先级抢占:通过 Lane 模型标记任务优先级,高优先级任务可在低优先级任务执行过程
- 4. 源码层面的关键设计:双缓存 Fiber 树