Skip to main content

React 设计原理 - 卡颂

s34363380

《React 设计原理》致力于剖析 React 设计理念与实现原理,基于 React 18 源码讲解。全书分为 3 篇,第 1 篇为理念篇(第 1 章~第 2 章),讲解 React 在主流前端框架中的定位与设计理念;第 2 篇为架构篇(第 3 章~第 5 章),讲解 React 架构中的 3 个阶段——render、commit、schedule,以及如何在架构中践行设计理念;第 3 篇为实现篇(第 6 章~第 8 章),贯穿 React 架构中的 3 个阶段,讲解具体 API 的实现细节。

关于作者

卡颂 是前端技术专家、技术作家:

  • 前端技术专家:深入研究和实践 React 技术栈
  • 技术博主:长期在个人博客和各大技术平台分享前端技术文章
  • 开源贡献者:积极参与开源社区,贡献代码和技术内容
  • 技术作家:擅长将复杂的技术原理以通俗易懂的方式讲解

卡颂以其"深入源码、剖析原理"的写作风格著称,他不满足于表面的 API 使用,而是深入源码层面,揭示 React 设计背后的原理和思想。

核心内容

1. React 的设计理念

React 的核心设计理念:

1. 声明式编程 (Declarative)
- 描述 UI 应该是什么样子
- 而非命令式地操作 DOM
- 代码更易读、更易维护

// 命令式
const div = document.createElement('div');
div.textContent = 'Hello World';
document.body.appendChild(div);

// 声明式
<div>Hello World</div>

2. 组件化 (Component-Based)
- UI 拆分为独立可复用的组件
- 每个组件管理自己的状态
- 组件组合构建复杂 UI

3. 单向数据流 (One-Way Data Flow)
- 数据从父组件流向子组件
- props 向下传递
- 状态变化触发重新渲染

4. 函数式编程思想 (Functional Programming)
- 纯函数:相同输入 = 相同输出
- 不可变数据
- 副作用隔离

2. Fiber 架构

Fiber 架构的核心:

1. 为什么需要 Fiber?
- React 15 及之前:递归是不可中断的
- 大型组件树:可能导致主线程阻塞
- 用户体验:卡顿、掉帧

2. Fiber 是什么?
- 数据结构:表示组件节点
- 执行单元:可中断、可恢复的最小工作单元
- 链表结构:替代递归遍历

3. Fiber 节点结构
type FiberNode = {
type: 组件类型,
props: 属性,
return: 父节点,
child: 子节点,
sibling: 兄弟节点,
effectTag: 副作用标记,
next: 下一个有副作用的节点
}

4. 双缓冲机制 (Double Buffering)
- current Fiber: 当前屏幕显示
- workInProgress Fiber: 正在构建的新树
- 完成后一次性提交

3. Render 阶段

Render 阶段的工作流程:

1. 触发更新
- setState()
- useState() setter
- 父组件重新渲染

2. 创建 Fiber 树
- 从根节点开始
- 深度优先遍历
- 构建完整的 Fiber 树

3. Diff 算法
- 比较新旧 Fiber 树
- 标记需要更新的节点
- 三种 Diff 策略:
* 同类型:继续比较子节点
* 不同类型:删除旧节点,创建新节点
* key 值:优化列表渲染

4. 收集副作用
- 需要插入的节点
- 需要更新的节点
- 需要删除的节点

5. 可中断性
- 使用 requestIdleCallback
- 时间分片
- 高优先级任务可插队

4. Commit 阶段

Commit 阶段的工作流程:

1. 不可中断
- 必须同步执行完成
- 避免 UI 不一致

2. 三个子阶段
(1) Before Mutation
- 读取 Fiber 树
- 记录 DOM 状态

(2) Mutation
- 应用 DOM 操作
- 插入、更新、删除

(3) Layout
- 调用 useEffect 回调
- 执行使用端效果

3. 提交到屏幕
- 更新 refs
- 触发生命周期
- 执行副作用回调

4. 优先级调度
- 用户交互 > 动画 > 数据获取 > 空闲任务
- Lane 模型管理优先级

5. Schedule 调度

调度系统的核心概念:

1. 为什么要调度?
- 任务有轻重缓急
- 避免低优先级任务阻塞高优先级
- 优化用户体验

2. Lane 模型
- 用位运算表示优先级
- 不同 Lane 代表不同优先级
- 支持批量更新和合并

const SyncLane = 0b00001; // 同步任务
const InputContinuousLane = 0b00100; // 用户输入
const DefaultLane = 0b10000; // 默认优先级

3. 调度策略
- 高优先级:立即执行
- 中优先级:短时间延迟
- 低优先级:空闲时执行

4. 饥饿问题处理
- 低优先级任务等待过久会升级
- 避免永远得不到执行

6. Hooks 实现原理

Hooks 的工作原理:

1. 链表结构
- 每个 Hook 是一个节点
- 按调用顺序连接
- 依赖顺序的一致性

2. useState 实现
function useState(initialState) {
// 从链表中获取或创建 Hook
const hook = getHook();

// 如果是初次渲染
if (!hook.hasState) {
hook.state = initialState;
hook.hasState = true;
}

// 返回 [state, setState]
return [hook.state, createSetter(hook)];
}

3. useEffect 实现
- 保存依赖数组
- 比较依赖是否变化
- 决定是否需要执行

4. 规则与限制
- 只能在顶层调用
- 不能在条件语句中调用
- 依赖数组决定执行时机

7. Concurrent 特性

Concurrent 并发生态:

1. 可中断渲染
- Render 阶段可中断
- 高优先级任务优先
- 恢复时复用已完成工作

2. 并发更新
- 多次更新合并处理
- 批量更新优化
- 避免不必要的重渲染

3. Suspense
- 等待异步数据
- 显示 fallback UI
- 自动重试机制

4. useTransition
- 标记过渡更新
- 低优先级更新
- 不影响用户交互

5. useDeferredValue
- 延迟更新值
- 优先渲染新输入
- 再更新派生内容

经典摘录

React 的设计目标:构建可预测、易于理解的 UI。

声明式的力量在于:描述"是什么",而非"怎么做"。

Fiber 架构的核心:将不可中断的递归,变为可中断的链表遍历。

单向数据流让应用行为更可预测。

Diff 算法的本质:找到最小更新路径。

调度的本质:在合适的时间,做合适的事。

Hooks 让函数组件拥有了"记忆"能力。

Concurrent 的意义:让 UI 响应用户优先。

读书心得

《React 设计原理》是一本深入 React 源码的书籍。读完之后,我对 React 的理解从"如何使用"提升到了"为什么这样设计"的层面。

Fiber 架构的讲解让我印象深刻。之前只知道 Fiber 解决了卡顿问题,但不清楚具体原理。通过阅读源码分析,理解了 Fiber 如何将递归变为可中断的链表遍历,这是多么巧妙的设计。

双缓冲机制的设计思想源于图形学,React 将其应用到 UI 更新中。current 树表示当前屏幕显示,workInProgress 树在后台构建,完成后一次性提交。这种设计保证了 UI 的一致性。

Lane 优先级模型解决了任务调度问题。不同的更新有不同的优先级,用户交互应该优先处理,数据获取可以稍后执行。这种细粒度的控制让 React 更加灵活。

Hooks 实现原理的剖析解开了我的很多疑惑。为什么 Hooks 不能条件调用?因为底层是链表结构,调用顺序必须一致。为什么需要依赖数组?因为需要比较依赖是否变化来决定是否执行。

这本书适合有一定 React 使用经验的开发者。如果你对 React 的工作原理感到好奇,想深入理解其设计思想,这本书值得一读。