From b846c7f11849e53506d1d55b6cdde2c9a3380ff2 Mon Sep 17 00:00:00 2001 From: youxingzhi Date: Sun, 7 Apr 2024 11:42:57 +0800 Subject: [PATCH] blog-3 --- Cargo.lock | 2 ++ examples/hello-world/src/main.tsx | 4 +-- packages/react-dom/Cargo.toml | 3 +- packages/react-dom/src/host_config.rs | 41 +++++++++++++++++++++++ packages/react-dom/src/lib.rs | 20 ++++++++--- packages/react-dom/src/renderer.rs | 28 ++++++++++++++++ packages/react-reconciler/Cargo.toml | 3 +- packages/react-reconciler/src/fiber.rs | 2 ++ packages/react-reconciler/src/lib.rs | 46 ++++++++++++++++++++++++++ packages/shared/src/lib.rs | 9 ++++- 10 files changed, 149 insertions(+), 9 deletions(-) create mode 100644 packages/react-dom/src/host_config.rs create mode 100644 packages/react-dom/src/renderer.rs create mode 100644 packages/react-reconciler/src/fiber.rs diff --git a/Cargo.lock b/Cargo.lock index bfe3995..9d49099 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -82,6 +82,7 @@ version = "0.1.0" dependencies = [ "console_error_panic_hook", "react-reconciler", + "shared", "wasm-bindgen", "wasm-bindgen-test", "web-sys", @@ -93,6 +94,7 @@ version = "0.1.0" dependencies = [ "console_error_panic_hook", "react", + "shared", "wasm-bindgen", "wasm-bindgen-test", "web-sys", diff --git a/examples/hello-world/src/main.tsx b/examples/hello-world/src/main.tsx index 78a90af..e70c233 100644 --- a/examples/hello-world/src/main.tsx +++ b/examples/hello-world/src/main.tsx @@ -2,6 +2,6 @@ import {createRoot} from 'react-dom' const comp =
hello world
-console.log(comp) -console.log(createRoot(document.getElementById("root"))) +const root = createRoot(document.getElementById("root")) +root.render(comp) diff --git a/packages/react-dom/Cargo.toml b/packages/react-dom/Cargo.toml index d5289e9..78d0653 100644 --- a/packages/react-dom/Cargo.toml +++ b/packages/react-dom/Cargo.toml @@ -12,8 +12,9 @@ default = ["console_error_panic_hook"] [dependencies] wasm-bindgen = "0.2.84" -web-sys = { version = "0.3.69", features = ["console"] } +web-sys = { version = "0.3.69", features = ["console", "Window", "Document", "Text", "Element"] } react-reconciler = { path = "../react-reconciler" } +shared = { path = "../shared" } # The `console_error_panic_hook` crate provides better debugging of panics by # logging them with `console.error`. This is great for development, but requires # all the `std::fmt` and `std::panicking` infrastructure, so isn't great for diff --git a/packages/react-dom/src/host_config.rs b/packages/react-dom/src/host_config.rs new file mode 100644 index 0000000..5921218 --- /dev/null +++ b/packages/react-dom/src/host_config.rs @@ -0,0 +1,41 @@ +use std::any::Any; +use std::rc::Rc; + +use web_sys::{Element, Text, window}; + +use react_reconciler::HostConfig; +use shared::log; + +pub struct ReactDomHostConfig; + +impl HostConfig for ReactDomHostConfig { + fn create_text_instance(&self, content: String) -> Rc { + let window = window().expect("no global `window` exists"); + let document = window.document().expect("should have a document on window"); + Rc::new(document.create_text_node(content.as_str())) + } + + fn create_instance(&self, _type: String) -> Rc { + let window = window().expect("no global `window` exists"); + let document = window.document().expect("should have a document on window"); + match document.create_element(_type.as_ref()) { + Ok(element) => Rc::new(element), + Err(_) => todo!(), + } + } + + fn append_initial_child(&self, parent: Rc, child: Rc) { + let parent = parent.clone().downcast::().unwrap(); + let child = child.downcast::().unwrap(); + match parent.append_child(&child) { + Ok(_) => { + log!("append_initial_child successfully ele {:?} {:?}", parent, child); + } + Err(_) => todo!(), + } + } + + fn append_child_to_container(&self, child: Rc, parent: Rc) { + todo!() + } +} \ No newline at end of file diff --git a/packages/react-dom/src/lib.rs b/packages/react-dom/src/lib.rs index 5a09ad6..4487e37 100644 --- a/packages/react-dom/src/lib.rs +++ b/packages/react-dom/src/lib.rs @@ -1,8 +1,20 @@ use wasm_bindgen::prelude::*; +use react_reconciler::Reconciler; + +use crate::host_config::ReactDomHostConfig; +use crate::renderer::Renderer; +use crate::utils::set_panic_hook; + mod utils; +mod renderer; +mod host_config; -#[wasm_bindgen] -pub fn createRoot(container: &JsValue) -> JsValue { - JsValue::null() -} +#[wasm_bindgen(js_name = createRoot)] +pub fn create_root(container: &JsValue) -> Renderer { + set_panic_hook(); + let reconciler = Reconciler::new(Box::new(ReactDomHostConfig)); + let root = reconciler.create_container(container); + let renderer = Renderer::new(root, reconciler); + renderer +} \ No newline at end of file diff --git a/packages/react-dom/src/renderer.rs b/packages/react-dom/src/renderer.rs new file mode 100644 index 0000000..e744b61 --- /dev/null +++ b/packages/react-dom/src/renderer.rs @@ -0,0 +1,28 @@ +use std::cell::RefCell; +use std::rc::Rc; + +use wasm_bindgen::JsValue; +use wasm_bindgen::prelude::*; +use wasm_bindgen::prelude::wasm_bindgen; + +use react_reconciler::fiber::FiberRootNode; +use react_reconciler::Reconciler; + +#[wasm_bindgen] +pub struct Renderer { + root: Rc>, + reconciler: Reconciler, +} + +impl Renderer { + pub fn new(root: Rc>, reconciler: Reconciler) -> Self { + Self { root, reconciler } + } +} + +#[wasm_bindgen] +impl Renderer { + pub fn render(&self, element: &JsValue) { + self.reconciler.update_container(Rc::new(element.clone()), self.root.clone()) + } +} \ No newline at end of file diff --git a/packages/react-reconciler/Cargo.toml b/packages/react-reconciler/Cargo.toml index c225c52..e30daae 100644 --- a/packages/react-reconciler/Cargo.toml +++ b/packages/react-reconciler/Cargo.toml @@ -12,8 +12,9 @@ default = ["console_error_panic_hook"] [dependencies] wasm-bindgen = "0.2.84" -web-sys = { version = "0.3.69", features = ["console"] } +web-sys = { version = "0.3.69", features = ["console", "Text", "Window", "Document", "HtmlElement"] } react = { path = "../react" } +shared = { path = "../shared" } # The `console_error_panic_hook` crate provides better debugging of panics by # logging them with `console.error`. This is great for development, but requires # all the `std::fmt` and `std::panicking` infrastructure, so isn't great for diff --git a/packages/react-reconciler/src/fiber.rs b/packages/react-reconciler/src/fiber.rs new file mode 100644 index 0000000..96c2bc7 --- /dev/null +++ b/packages/react-reconciler/src/fiber.rs @@ -0,0 +1,2 @@ +#[derive(Debug)] +pub struct FiberRootNode {} \ No newline at end of file diff --git a/packages/react-reconciler/src/lib.rs b/packages/react-reconciler/src/lib.rs index e69de29..31ddbf6 100644 --- a/packages/react-reconciler/src/lib.rs +++ b/packages/react-reconciler/src/lib.rs @@ -0,0 +1,46 @@ +use std::any::Any; +use std::cell::RefCell; +use std::rc::Rc; + +use wasm_bindgen::JsValue; +use web_sys::{Element, window}; +use web_sys::js_sys::Reflect; + +use crate::fiber::FiberRootNode; + +pub mod fiber; + +pub trait HostConfig { + fn create_text_instance(&self, content: String) -> Rc; + fn create_instance(&self, _type: String) -> Rc; + fn append_initial_child(&self, parent: Rc, child: Rc); + fn append_child_to_container(&self, child: Rc, parent: Rc); +} + +pub struct Reconciler { + host_config: Box, +} + +impl Reconciler { + pub fn new(host_config: Box) -> Self { + Reconciler { host_config } + } + pub fn create_container(&self, container: &JsValue) -> Rc> { + Rc::new(RefCell::new(FiberRootNode {})) + } + + pub fn update_container(&self, element: Rc, root: Rc>) { + let props = Reflect::get(&*element, &JsValue::from_str("props")).unwrap(); + let _type = Reflect::get(&*element, &JsValue::from_str("type")).unwrap(); + let children = Reflect::get(&props, &JsValue::from_str("children")).unwrap(); + let text_instance = self.host_config.create_text_instance(children.as_string().unwrap()); + let div_instance = self.host_config.create_instance(_type.as_string().unwrap()); + self.host_config.append_initial_child(div_instance.clone(), text_instance); + let window = window().unwrap(); + let document = window.document().unwrap(); + let body = document.body().expect("document should have a body"); + body.append_child(&*div_instance.clone().downcast::().unwrap()); + } +} + + diff --git a/packages/shared/src/lib.rs b/packages/shared/src/lib.rs index 433e5d7..3fd8ffe 100644 --- a/packages/shared/src/lib.rs +++ b/packages/shared/src/lib.rs @@ -1 +1,8 @@ -pub static REACT_ELEMENT_TYPE: &str = "react.element"; \ No newline at end of file +pub static REACT_ELEMENT_TYPE: &str = "react.element"; + +#[macro_export] +macro_rules! log { + ( $( $t:tt )* ) => { + web_sys::console::log_1(&format!( $( $t )* ).into()); + } +} \ No newline at end of file