We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
在前端框架出现之前,通常会使用 jQuery 这样的库来开发页面。jQuery 是一个过程驱动的库,开发者需要直接调用浏览器的宿主环境 API,例如 DOM 操作等。这意味着开发者需要手动管理页面状态和交互,通过执行一系列的操作来更新页面。
然而,随着前端框架的出现,工作方式发生了根本性的变化,从过程驱动转变为状态驱动。在状态驱动的模式下,开发者不再直接操作宿主环境 API,而是通过前端框架提供的运行时核心模块来管理页面状态和更新。这些核心模块,例如 React 中的 Reconciler 和 Vue 中的 Renderer,负责将开发者编写的代码翻译成对应的宿主环境 API 调用,以更新页面。
Reconciler
Renderer
Reconciler 的中文名叫协调器,它负责处理 React 元素的更新并在内部构建虚拟 DOM,这个过程是 React 框架实现高效的 UI 渲染和更新的核心逻辑所在。以下是 Reconciler 主要做的事情:
我们先在 packages 目录下新建一个 react-reconciler 文件夹,执行 npm init,并更改配置文件如下,执行 pnpm i 安装依赖。
packages
react-reconciler
npm init
pnpm i
// packages/react-reconciler/package.json { "name": "react-reconciler", "version": "1.0.0", "description": "react reconciler", "module": "index.ts", "dependencies": { "shared": "workspace: *" }, "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC" }
FiberNode(纤维节点)是 Reconciler 的核心数据结构之一,用于构建协调树。Reconciler 使用 FiberNode 来表示 React 元素树中的节点,并通过比较 Fiber 树的差异,找出需要进行更新的部分,生成更新指令,来实现 UI 的渲染和更新。
FiberNode
每个 FiberNode 都表示着 React 元素树中的一个节点,它包含了以下几个重要的字段:
接下来我们来实现一下 FiberNode 这个数据结构。
在 packages/react-reconciler/src/ 目录下新建 fiber.ts 文件,用来实现 FiberNode 类;新建 workTags.ts 文件,用于标识不同类型的工作单元;新建 fiberFlags.ts 文件,用于标识不同类型的副作用;代码如下:
packages/react-reconciler/src/
fiber.ts
workTags.ts
fiberFlags.ts
// packages/react-reconciler/src/fiber.ts import { Props, Key, Ref } from 'shared/ReactTypes'; import { WorkTag } from './workTags'; import { NoFlags, Flags } from './fiberFlags'; export class FiberNode { tag: WorkTag; key: Key; stateNode: any; type: any; return: FiberNode | null; sibling: FiberNode | null; child: FiberNode | null; index: number; ref: Ref; pendingProps: Props; memoizedProps: Props | null; memoizedState: any; alternate: FiberNode | null; flags: Flags; subtreeFlags: Flags; updateQueue: unknown; constructor(tag: WorkTag, pendingProps: Props, key: Key) { // 类型 this.tag = tag; this.key = key; this.ref = null; this.stateNode = null; // 节点对应的实际 DOM 节点或组件实例 this.type = null; // 节点的类型,可以是原生 DOM 元素、函数组件或类组件等 // 构成树状结构 this.return = null; // 指向节点的父节点 this.sibling = null; // 指向节点的下一个兄弟节点 this.child = null; // 指向节点的第一个子节点 this.index = 0; // 索引 // 作为工作单元 this.pendingProps = pendingProps; // 表示节点的新属性,用于在协调过程中进行更新 this.memoizedProps = null; // 已经更新完的属性 this.memoizedState = null; // 更新完成后新的 State this.alternate = null; // 指向节点的备份节点,用于在协调过程中进行比较 this.flags = NoFlags; // 表示节点的副作用类型,如更新、插入、删除等 this.subtreeFlags = NoFlags; // 表示子节点的副作用类型,如更新、插入、删除等 this.updateQueue = null; // 更新计划队列 } }
// packages/react-reconciler/src/workTags.ts export type WorkTag = | typeof FunctionComponent | typeof HostRoot | typeof HostComponent | typeof HostText; export const FunctionComponent = 0; export const HostRoot = 3; export const HostComponent = 5; export const HostText = 6;
// packages/react-reconciler/src/fiberFlags.ts export type Flags = number; export const NoFlags = 0b0000000; export const PerformedWork = 0b0000001; export const Placement = 0b0000010; export const Update = 0b0000100; export const ChildDeletion = 0b0001000;
Reconciler 的工作流程总的来说就是对 Fiber 树进行一次 深度优先遍历(DFS) ,首先访问根节点,然后依次访问左子树和右子树,通过比较新节点(新生成的 React Element)和旧节点(现有的 FiberNode),生成更新计划,并打上不同的标记。
React Element
遍历 Fiber 树: React 使用深度优先搜索(DFS)算法来遍历 Fiber 树,首先会从 Fiber 树的根节点开始进行遍历,遍历整个组件树的结构。
比较新旧节点: 对于每个 Fiber 节点,Reconciler 会比较新节点(即新的 React Element)和旧节点(即现有的 FiberNode)之间的差异,比较的内容包括节点类型、属性、子节点等方面的差异。
生成更新计划: 根据比较的结果,Reconciler 会生成一个更新计划,用于确定需要进行的操作,更新计划通常包括哪些节点需要更新、哪些节点需要插入到 DOM 中、哪些节点需要删除等信息。
打标记(Tagging): 为了记录不同节点的操作,React 会为每个节点打上不同的标记。例如,如果节点需要更新,可能会打上更新标记(Update Tag);如果节点是新创建的,可能会打上插入标记(Placement Tag);如果节点被移除,可能会打上删除标记(Deletion Tag)等。
更新 Fiber 节点: 根据生成的更新计划和标记,Reconciler 会更新对应的 Fiber 节点以反映组件的最新状态。更新操作可能包括更新节点的状态、更新节点的属性、调用生命周期方法等。
递归处理子节点: 对于每个节点的子节点,React 会递归地重复进行上述的比较和更新操作,以确保整个组件树都得到了正确的处理。
当所有 React Element 都比较完成之后,会生成一棵新的 Fiber 树,此时,一共存在两棵 Fiber 树:
current
workInProgress
下面我们来实现一下 Reconciler 的完整工作流程。
在 packages/react-reconciler/src/ 目录下新建 workLoop.ts 文件:
workLoop.ts
// packages/react-reconciler/src/workLoop.ts import { FiberNode } from './fiber'; let workInProgress: FiberNode | null = null; function renderRoot(root: FiberNode) { prepareFreshStack(root); do { try { workLoop(); break; } catch (e) { console.warn('workLoop发生错误:', e); workInProgress = null; } } while (true); } // 初始化 workInProgress 变量 function prepareFreshStack(root: FiberNode) { workInProgress = root; } // 深度优先遍历,向下递归子节点 function workLoop() { while (workInProgress !== null) { performUnitOfWork(workInProgress); } } function performUnitOfWork(fiber: FiberNode) { // 比较并返回子 FiberNode const next = beginWork(fiber); fiber.memoizedProps = fiber.pendingProps; if (next == null) { // 没有子节点,则遍历兄弟节点或父节点 completeUnitOfWork(fiber); } else { // 有子节点,继续向下深度遍历 workInProgress = next; } } // 深度优先遍历,向下递归子节点 function completeUnitOfWork(fiber: FiberNode) { let node: FiberNode | null = fiber; do { // 生成更新计划 completeWork(node); // 有兄弟节点,则遍历兄弟节点 const sibling = node.sibling; if (sibling !== null) { workInProgress = sibling; return; } // 否则向上返回,遍历父节点 node = node.return; // workInProgress 最终指向根节点 workInProgress = node; } while (node !== null); }
至此,我们就完成了 Reconciler 的大致工作流程,下一节将深入实现 Reconciler 的更新机制。
相关代码可在 git tag v1.3 查看,地址:https://github.com/2xiao/my-react/tree/v1.3
git tag v1.3
The text was updated successfully, but these errors were encountered:
2xiao
No branches or pull requests
1. Reconciler 简介
在前端框架出现之前,通常会使用 jQuery 这样的库来开发页面。jQuery 是一个过程驱动的库,开发者需要直接调用浏览器的宿主环境 API,例如 DOM 操作等。这意味着开发者需要手动管理页面状态和交互,通过执行一系列的操作来更新页面。
然而,随着前端框架的出现,工作方式发生了根本性的变化,从过程驱动转变为状态驱动。在状态驱动的模式下,开发者不再直接操作宿主环境 API,而是通过前端框架提供的运行时核心模块来管理页面状态和更新。这些核心模块,例如 React 中的
Reconciler
和 Vue 中的Renderer
,负责将开发者编写的代码翻译成对应的宿主环境 API 调用,以更新页面。Reconciler
的中文名叫协调器,它负责处理 React 元素的更新并在内部构建虚拟 DOM,这个过程是 React 框架实现高效的 UI 渲染和更新的核心逻辑所在。以下是Reconciler
主要做的事情:我们先在
packages
目录下新建一个react-reconciler
文件夹,执行npm init
,并更改配置文件如下,执行pnpm i
安装依赖。2. 实现 FiberNode
FiberNode
(纤维节点)是Reconciler
的核心数据结构之一,用于构建协调树。Reconciler
使用FiberNode
来表示 React 元素树中的节点,并通过比较 Fiber 树的差异,找出需要进行更新的部分,生成更新指令,来实现 UI 的渲染和更新。每个
FiberNode
都表示着 React 元素树中的一个节点,它包含了以下几个重要的字段:接下来我们来实现一下
FiberNode
这个数据结构。在
packages/react-reconciler/src/
目录下新建fiber.ts
文件,用来实现FiberNode
类;新建workTags.ts
文件,用于标识不同类型的工作单元;新建fiberFlags.ts
文件,用于标识不同类型的副作用;代码如下:3. 实现 Reconciler 工作流程
Reconciler
的工作流程总的来说就是对 Fiber 树进行一次 深度优先遍历(DFS) ,首先访问根节点,然后依次访问左子树和右子树,通过比较新节点(新生成的React Element
)和旧节点(现有的FiberNode
),生成更新计划,并打上不同的标记。遍历 Fiber 树:
React 使用深度优先搜索(DFS)算法来遍历 Fiber 树,首先会从 Fiber 树的根节点开始进行遍历,遍历整个组件树的结构。
比较新旧节点:
对于每个 Fiber 节点,
Reconciler
会比较新节点(即新的React Element
)和旧节点(即现有的FiberNode
)之间的差异,比较的内容包括节点类型、属性、子节点等方面的差异。生成更新计划:
根据比较的结果,
Reconciler
会生成一个更新计划,用于确定需要进行的操作,更新计划通常包括哪些节点需要更新、哪些节点需要插入到 DOM 中、哪些节点需要删除等信息。打标记(Tagging):
为了记录不同节点的操作,React 会为每个节点打上不同的标记。例如,如果节点需要更新,可能会打上更新标记(Update Tag);如果节点是新创建的,可能会打上插入标记(Placement Tag);如果节点被移除,可能会打上删除标记(Deletion Tag)等。
更新 Fiber 节点:
根据生成的更新计划和标记,
Reconciler
会更新对应的 Fiber 节点以反映组件的最新状态。更新操作可能包括更新节点的状态、更新节点的属性、调用生命周期方法等。递归处理子节点:
对于每个节点的子节点,React 会递归地重复进行上述的比较和更新操作,以确保整个组件树都得到了正确的处理。
当所有 React Element 都比较完成之后,会生成一棵新的 Fiber 树,此时,一共存在两棵 Fiber 树:
current
作为参考来比较新的树与旧树的差异,决定如何更新 UI;workInProgress
上的更新完成,它将会被提交为新的current
,成为下一次渲染的参考树,并清空旧的current
树。下面我们来实现一下
Reconciler
的完整工作流程。在
packages/react-reconciler/src/
目录下新建workLoop.ts
文件:至此,我们就完成了
Reconciler
的大致工作流程,下一节将深入实现Reconciler
的更新机制。相关代码可在
git tag v1.3
查看,地址:https://github.com/2xiao/my-react/tree/v1.3The text was updated successfully, but these errors were encountered: