模仿 big-react,使用 Rust 和 WebAssembly,从零实现 React v18 的核心功能。深入理解 React 源码的同时,还锻炼了 Rust 的技能,简直赢麻了!
代码地址:https://github.com/ParadeTo/big-react-wasm
本文对应 tag:v15
Based on big-react,I am going to implement React v18 core features from scratch using WASM and Rust.
Code Repository:https://github.com/ParadeTo/big-react-wasm
The tag related to this article:v15
本次更新详见这里,下面来过一遍整个流程。
The details of this update can be seen here. Let’s go through the entire process below.
跟 useState 一样,首先需要在 react 包中导出这个方法,它接收两个参数:
Like useState, we first need to export this method from the react package. It takes two parameters:
1 |
|
然后,我们需要分别为首次渲染和更新实现 mount_effect 和 update_effect。其中 mount_effect 会给 FiberNode 的 Hook 链表上新增一个 Hook 节点,其 memoized_state 属性指向一个 Effect 对象,同时这个对象还会被加入 FiberNode 上的 update_queue, 它是一个环形队列。另外,FiberNode 还被被标记为 PassiveEffect:
Next, we need to implement mount_effect and update_effect for the initial render and updates, respectively. mount_effect adds a new Hook node to the linked list of Hooks on the FiberNode, with its memoized_state property pointing to an Effect object. This object is also added to the update_queue on the FiberNode, which is a circular queue. Additionally, the FiberNode is marked with PassiveEffect:

update_effect 的工作与 mount_effect 类似,会更新 Effect 节点,不过他会把传入的 deps 与之前的 prev_deps 中的元素依次进行浅比较,如果全部相同就不会给 FiberNode 标记 PassiveEffect。
The work of update_effect is similar to mount_effect, updating the Effect node, but it performs a shallow comparison of the incoming deps with the previous prev_deps. If they are all the same, it will not mark the FiberNode with PassiveEffect.
Effect 中的属性包含这些:
The properties included in Effect are as follows:
1 | pub struct Effect { |
接着,Render 阶段不需要更改,Commit 阶段我们需要在 commit_mutation_effects 前新增处理 useEffect 的逻辑:
During the Render phase, no changes are needed. In the Commit phase, we need to add logic to handle useEffect before commit_mutation_effects:
1 | // useEffect |
这里,我们使用上一篇文章实现的 scheduler 来调度一个任务执行 flush_passive_effects 方法:
Here, we use the scheduler implemented in the previous article to schedule a task to execute the flush_passive_effects method:
1 | fn flush_passive_effects(pending_passive_effects: Rc<RefCell<PendingPassiveEffects>>) { |
这里的 pending_passive_effects 是 FiberRootNode 上的一个属性,用于保存此次需要执行的 Effect:
The pending_passive_effects here is a property on the FiberRootNode, used to store the Effect that needs to be executed this time:
1 | pub struct PendingPassiveEffects { |
其中,因为组件卸载需要处理的 Effect 保存在 unmount 中,因为更新需要处理的 Effect 保存在 update 中。从代码中看到,这里会先处理因组件卸载需要处理的 Effect,即使这个组件顺序比较靠后,比如这个例子:
Among them, the Effect that needs to be handled due to component unmounting is saved in unmount, and the Effect that needs to be handled due to updates is saved in update. From the code, we can see that the Effect due to component unmounting is handled first, even if the component is later in the sequence, like in this example:
1 | function App() { |
点击后会先执行 Child2 的 useEffect 的 destroy,打印 child2 destroy。而如果换成这样:
After clicking, the destroy of Child2‘s useEffect will be executed first, printing child2 destroy. But if it’s changed to this:
1 | function App() { |
点击后会先执行 Child1 的 useEffect 的 destroy,打印 child1 destroy。
After clicking, the destroy of Child1‘s useEffect will be executed first, printing child1 destroy.
那 pending_passive_effects 里面的 Effect 是什么时候加进去的呢?答案是在 commit_mutation_effects 中,有两种情况:
So when are the Effect in pending_passive_effects added? The answer is in commit_mutation_effects, there are two situations:
如果
FiberNode节点被标记需要删除且为FunctionComponent类型,则需要把update_queue中的Effect加入pending_passive_effects中的unmount列表中。If the
FiberNodenode is marked for deletion and is of theFunctionComponenttype, then theEffectin theupdate_queueneeds to be added to theunmountlist inpending_passive_effects.
1 | fn commit_deletion( |
如果
FiberNode节点被标记为PassiveEffect,则需要把update_queue中的Effect加入pending_passive_effects中的update列表中。If the
FiberNodenode is marked withPassiveEffect, then theEffectin theupdate_queueneeds to be added to theupdatelist inpending_passive_effects.
1 | if flags & Flags::PassiveEffect != Flags::NoFlags { |
大致流程介绍完毕,更多细节请参考这里。
The general process is now complete, for more details please refer to here.