模仿 big-react,使用 Rust 和 WebAssembly,从零实现 React v18 的核心功能。深入理解 React 源码的同时,还锻炼了 Rust 的技能,简直赢麻了!
代码地址:https://github.com/ParadeTo/big-react-wasm
本文对应 tag:v3
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:v3
前言
Introduction
上一篇文章末本计划本篇实现 render 阶段,但考虑到内容太多,还是分开写比较好。说是架构设计,有点夸张了,其实主要的目的在于要实现 Renderer
和 Reconciler
的解耦,让 Reconciler
可以支持不同的 Renderer
,因为后续还需要实现 ReactNoop
Renderer 用来跑测试用例。
In the previous article, the plan was to implement the render phase in this article. However, considering the amount of content, it is better to separate it into multiple parts. Calling it “architecture design” may be a bit exaggerated. The main goal is to decouple the Renderer and Reconciler to enable the Reconciler to support different Renderer implementations. This is necessary because we will need to implement the ReactNoop Renderer for running test cases in the future.
Reconciler
Rust 中要实现我们的目的,离不开 Trait,所以我们在 Reconciler
中先定义好 HostConfig
Trait:
To achieve our goal in Rust, traits are indispensable. Therefore, we first define the HostConfig
trait in the Reconciler
to lay the foundation:
1 | // react-reconciler/src/lib.rs |
这里暂时只定义了部分方法,需要注意的是由于不同 Renderer
下的 stateNode
是不一样的,这里的类型都不能确定,所以使用了 std::any::Any
。
Here, only a few methods are defined, and it’s important to note that the stateNode
under different Renderers
can have different types. To handle this uncertainty, the std::any::Any
type is used.
接着来定义 Reconciler
:
Next, let’s define the Reconciler
:
1 | // react-reconciler/src/lib.rs |
Reconciler
中包含 host_config
属性,使用 Trait Object 来表示泛型,当某个 Renderer
中实例化一个 Reconciler
对象时,需要传入实现了 HostConfig
Trait 的类型的实例。
In the Reconciler
struct, the host_config
property is included and uses a Trait Object to represent the generic type. When instantiating a Reconciler
object within a specific Renderer
, an instance of a type that implements the HostConfig
trait needs to be passed.
其他两个方法先简单的实现一下,供调试用。接下来看看 Renderer
。
Let’s implement the other two methods quickly for debugging purposes. Next, we’ll take a look at the Renderer
.
Renderer
Renderer 中当然首先要实现 HostConfig
:
The first thing in Renderer is to implement the HostConfig
.
1 | // react-dom/src/host_config.rs |
可以看到我们可以通过 downcast
把 Any
类型转化为具体的类型,这里暂时都很简单地实现了一下。
然后我们定义一个 Renderer
:
As we can see, we can use downcast
to convert the Any
type into a specific type. Here, we have implemented the method in a simple manner for now.
Next, let’s define a Renderer
:
1 | // react-dom/src/renderer.rs |
他包含 root
和 reconciler
两个属性,其中 root
是在调用 create_root
时通过 Reconciler
的 create_container
方法生成的:
The Renderer
includes two properties, root
and reconciler
. The root is generated by the create_container
method of the Reconciler
when create_root
is called.
1 |
|
测试
Testing
一切就绪了,我们加点代码来调试一下,我们把 hello-world
中的例子改成如下所示:
Everything is ready, let’s add some code to debug. We’ll modify the example in hello-world
as follows:
1 | import {createRoot} from 'react-dom' |
然后在 Reconciler
中,我们先硬编码实现首次渲染:
Then, in the Reconciler
, let’s start by implementing the initial rendering with hardcoded content:
1 | pub fn update_container(&self, element: Rc<JsValue>, root: Rc<RefCell<FiberRootNode>>) { |
不出意外,重新构建并安装依赖,运行 hello world 项目就可以在浏览器中看到内容了。
If everything goes well, rebuild and install the dependencies. You should be able to see the content in the browser when running the hello world project.