第 18 卷
8// 正确:在函数组件中调用 Hooks 9function GoodComponent() {
10const [name, setName] = useName();
11return <input value={name} onChange={(e) => setName(e.target.value)} />;
12}
2. 实践层面的最佳规则(推荐遵守)
(1)自定义 Hooks 必须以 use 开头命名
规则:自定义 Hooks 的名称必须以「use」作为前缀(如 useRequest、useForm、useUser)。
原因:这是 React 官方的约定,目的是让开发者快速识别 Hooks,同时让 ESLint 等工具(如 eslintplugin-react-hooks)能够检测到 Hooks 的使用场景,避免违反核心规则(如在普通函数中调用)。
错误示例:const requestHook = () => { ... }(未以 use 开头,无法被识别为 Hooks);
正确示例:const useRequest = () => { ... }。
(2)依赖数组必须完整且准确
规则:对于 useEffect、useCallback、useMemo 等需要依赖数组的 Hooks,必须将回调函数中使用的所有组件内变量(state、props、函数等)都加入依赖数组。
原因:依赖数组的作用是告诉 React“当哪些变量变化时,需要重新执行回调函数/重新创建函数/重新计算结果”。若依赖数组不完整,会导致回调函数使用过期的变量(闭包陷阱),出现状态错乱、逻
辑异常等问题。
错误示例:
Code block
1 2function BadComponent({ id }) {
3const [data, setData] = useState(null);
4
| 5 | //错误:回调中使用了id,但未加入依赖数组,id变化时不会重新请求数据 |
|---|---|
| 6 | useEffect(() => { |
| 7 | const fetchData = async () => { |
| 8 | const res = await fetch(`https://api.example.com/data/${id}`); |
| 9 | const data = await res.json(); |
| 10 | setData(data); |
| 11 | }; |
正确示例:
Code block
1 2function GoodComponent({ id }) {
3const [data, setData] = useState(null);
4
| 5 | //正确:将id加入依赖数组,id变化时重新请求数据 |
|---|---|
| 6 | useEffect(() => { |
| 7 | const fetchData = async () => { |
| 8 | const res = await fetch(`https://api.example.com/data/${id}`); |
| 9 | const data = await res.json(); |
| 10 | setData(data); |
| 11 | }; |
| 12 | fetchData(); |
| 13 | }, [id]); //依赖数组包含所有用到的变量 |
14 15return <div>{data?.name}</div>;
16}
(3)避免在 Hooks 回调中执行副作用(useMemo/useCallback)
规则:useMemo 和 useCallback 的回调函数应是「纯函数」,无副作用(如 DOM 操作、网络请求、修改状态等);副作用应统一放在 useEffect 中处理。
原因:useMemo 和 useCallback 的核心作用是缓存,其回调函数的执行时机不确定(React 可能为了性能暂停/恢复执行),若包含副作用,可能导致副作用执行次数异常、逻辑错乱。
错误示例:
Code block
1 2function BadComponent() {
3const [count, setCount] = useState(0);
4
| 5 | //错误:useMemo回调中包含副作用(修改状态) |
|---|---|
| 6 | useMemo(() => { |
| 7 | setCount(count + 1); //副作用,执行时机不确定 |
| 8 | return count * 2; |
正确示例:
Code block
1 2function GoodComponent() {
| 3 | const [count, setCount] = useState(0); |
|---|---|
| 4 | const doubledCount = useMemo(() => { |
| 5 | return count * 2; //纯函数,无副作用 |
| 6 | }, [count]); |
7 8// 正确:副作用放在 useEffect 中
9useEffect(() => { 10setCount(prev => prev + 1);
11}, []);
12 13return <div>{doubledCount}</div>;
14}
(4)避免过度使用 Hooks
规则:不追求“ Hooks 数量最少”,但也避免不必要的 Hooks 拆分或滥用(如将简单状态拆分为多个 useState,或过度使用 useMemo/useCallback 缓存)。
原因:合理的 Hooks 拆分能提升代码可读性(如将不同功能的状态分开管理),但过度拆分会增加代码复杂度;过度使用缓存类 Hooks(useMemo/useCallback)会增加 React 的性能开销(缓存存
储、依赖对比),反而降低性能。
(5)清理 useEffect 中的副作用
规则:对于有“持续副作用”的 useEffect(如定时器、事件监听、网络请求),必须在回调函数中返回清理函数,用于组件卸载或依赖变化时清理副作用,避免内存泄漏。
示例:清理定时器
Code block
1 2function GoodComponent() {
3useEffect(() => { 4const timer = setInterval(() => {
5console.log('定时器执行');
6}, 1000);
全站分卷索引 · 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 树