likes
comments
collection
share

从零实现 React v18,但 WASM 版 - [12] 实现多节点更新流程

作者站长头像
站长
· 阅读数 53

模仿 big-react,使用 Rust 和 WebAssembly,从零实现 React v18 的核心功能。深入理解 React 源码的同时,还锻炼了 Rust 的技能,简直赢麻了!

代码地址:github.com/ParadeTo/bi…

本文对应 tag:v12

之前已经实现了单节点的更新流程,这篇文章继续介绍如何实现多节点的更新。

值得注意的是这里,TS 中可以像这样声明一个 Map 的类型:

Map<string | number, Fiber>

但是 WASM 中的 keyJsValue 类型的,当我们尝试这样声明类型时,会提示 JsValue 没有实现 HashEq trait:

HashMap<JsValue, Rc<RefCell<FiberNode>>>

那实现一下不就行了?由于 Rust 中的“孤儿规则”限制,我们不能直接给 JsValue 实现这些 trait,需要新建一个 struct 包裹一下:

struct Key(JsValue);

impl PartialEq for Key {
    fn eq(&self, other: &Self) -> bool {
        Object::is(&self.0, &other.0)
    }
}

impl Eq for Key {}

impl Hash for Key {
    fn hash<H: Hasher>(&self, state: &mut H) {
        if self.0.is_string() {
            self.0.as_string().unwrap().hash(state)
        } else if let Some(n) = self.0.as_f64() {
            n.to_bits().hash(state)
        } else if self.0.is_null() {
            "null".hash(state)
        }
    }
}

多节点 Diff 完成后,会给需要移动的 FiberNode 打上 Placement 的标记,给需要删除的 FiberNode 的父节点打上 ChildDeletion 的标记,以下面这个为例:

// before
<ul>
  <li key={1}>a</li> // key: "1"
  <li>b</li> // key: 1
  <li>c</li> // key: 2
  <li key={4}>d</li> // key: "4"
</ul>

// after
<ul>
  <li>b</li> // key: 0
  <li>c</li> // key: 1
  <li key={1}>d</li> // key: "1"
</ul>

begin work 和 complete work 后的结果如下所示:

从零实现 React v18,但 WASM 版 - [12] 实现多节点更新流程

稍微解释下:

  • 传入的 key 都会转为 string 类型,如果没有传 key,则用索引作为 key。
  • key 为 0 的 li 节点找不到相同 key 的节点,所以需要插入一个新节点,标记为 Placement
  • key 为 1 的 li 节点可复用,只需要更新其子节点内容由 b 到 c,标记为 Update
  • key 为 "1" 的 li 节点可复用,但由于 oldIndex 小于 lastPlacedIndex,所以需要移动,标记为 Placement

接下来,就到了 commit 阶段,该阶段会按照深度优先遍历的方式处理节点上的副作用。

在执行插入操作时,会在 siblings 中尝试找到一个插入点 before。难点在于这个插入点可能并不是他的同级 sibling。比如 <div/><B/> 其中 B 是 FunctionComponent 类型: function B() {return <div/>},这里 before 实际是 B 的 child,实际情况层级可能更深。同时,如果一个 FiberNode 被标记 Placement,那他就是不稳定的(他对应的 Element 在本次 commit 阶段会移动),也不能作为 before

如果能找到这个插入点 before,则调用 parent.insertBefore(node, before),否则调用 parent.appendChild(node)

还是上面的例子,会依次处理 li(0) -> c -> li("1") -> ul 上的副作用,结果将会如下所示:

  • commitPlacement 从零实现 React v18,但 WASM 版 - [12] 实现多节点更新流程
  • commitUpdate 从零实现 React v18,但 WASM 版 - [12] 实现多节点更新流程
  • commitPlacement 从零实现 React v18,但 WASM 版 - [12] 实现多节点更新流程
  • commitDeletion 从零实现 React v18,但 WASM 版 - [12] 实现多节点更新流程

本次更新详见这里。跪求 star 并关注公众号“前端游”。

转载自:https://juejin.cn/post/7365879319598678028
评论
请登录