模仿 big-react,使用 Rust 和 WebAssembly,从零实现 React v18 的核心功能。深入理解 React 源码的同时,还锻炼了 Rust 的技能,简直赢麻了!
代码地址:https://github.com/ParadeTo/big-react-wasm
本文对应 tag:v22
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:v22
前面几篇文章都是围绕 React 性能优化相关的特性展开的,不过还差一个 memo,今天就来实现一下。以下面代码为例:
The previous articles were focused on exploring performance optimization features related to React. However, there is still one missing feature: memo. Today, let’s implement it. Take the following code as an example:
1 | import {useState, memo} from 'react' |
首次渲染时,会打印:
When initially rendered, the following will be printed:
1 | App render 0 |
点击后,应该只有第一个 Cpn 组件会重新渲染,控制台打印:
After clicking, only the first Cpn component should be re-rendered, and the console will print:
1 | App render 1 |
下面我们来看看要怎么实现。
Now let’s see how to implement this.
首先,需要从 react 这个库中导出 memo 方法,如下所示:
First, we need to import the memo method from the React library, as shown below:
1 |
|
翻译成 JS 的话,是这样:
In JavaScript, the translation would be as follows:
1 | export function memo( |
跟之前的 context Provider 类似,这里也是返回了一个对象,并且把传入的组件保存在了 type 字段中,同时把第二个参数存在了 compare 字段中,该字段的作用应该都清楚,就不赘述了。很明显,这里又是一个新的 FiberNode 类型,我们需要在 begin work 中增加对该类型的处理:
Similar to the previous context Provider, here we also return an object. The passed-in component is saved in the type field, and the second argument is stored in the compare field. The purpose of the compare field should be clear, so I won’t elaborate on it. Clearly, this is a new FiberNode type, and we need to add handling for this type in the begin work phase.
1 |
|
这里的代码很好懂,如果有 current,说明不是首次渲染,可以看是否可以进行性能优化。
首先还是通过 check_scheduled_update_or_context 判断子孙组件中是否有满足这次更新优先级的节点,如果没有则进行 memo 相关的性能优化,具体来说为:
- 获取
compare函数,如果没有则用默认的shallow_equal(该函数用于对比两个对象是否相等,key 相同且其值相同时两个对象相等,值的比较为浅比较) - 将新旧
props传入上面得到的函数 - 如果
compare返回 true,则进入bailout逻辑
否则,进入 update_function_component 逻辑,因为 memo 只是在 FunctionComponent 外面多套了一层而已。注意到这里的 update_function_component 的参数跟之前不一样了,之前只有 work_in_progress 和 render_lane 是因为只考虑 FunctionComponent 的情况下,可以从 work_in_progress 的 _type 中获取 Component,现在加入了 MemoComponent,则需要从 work_in_progress 的 _type 中的 type 来获取 Component。
其他比较细微的改动就不介绍了,详情请见这里。
The code here is easy to understand. If there is a current value, it means it’s not the initial render, so we can check if there are any nodes in the descendant components that meet the priority of this update, and if not, we can perform performance optimizations related to memoization. Specifically:
- We retrieve the
comparefunction, and if it doesn’t exist, we use the defaultshallow_equalfunction (which compares two objects for equality by comparing their keys and values, performing a shallow comparison). - We pass the new and old
propsto the function obtained above. - If the
comparefunction returns true, we enter thebailoutlogic.
Otherwise, we enter the update_function_component logic because memo is just an additional layer outside the FunctionComponent. Note that the parameters for update_function_component are different now. Previously, we only had work_in_progress and render_lane because we only considered the case of a FunctionComponent, where we could retrieve the Component from work_in_progress‘s _type. Now, with the addition of MemoComponent, we need to retrieve the Component from work_in_progress‘s _type‘s type.
I won’t go into other minor changes, but you can find more details here.