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:v1
Tools
Rust: A secure, efficient, and modern programming language (omitting ten thousand words). You can simply follow the installation instructions provided on the official website.
wasm-pack: A one-stop tool for building, testing, and publishing Rust WebAssembly.
cargo-generate: Quickly create Rust projects by using existing Git repositories as templates.
For more information, you can refer to the Rust and WebAssembly.
Project Structure
First, let's set up the following project structure:
.
├── Cargo.toml
├── package.json
├── examples
│ └── hello-world // React project initialized by vite
├── packages
│ ├── react // WASM project created by cargo generate --git https://github.com/rustwasm/wasm-pack-template
│ ├── react-dom // WASM project created by cargo generate --git https://github.com/rustwasm/wasm-pack-template
│ ├── react-reconciler // Rust project created by cargo new
│ └── shared // Rust project created by cargo new
The Cargo.toml file is shown below, similar to the commonly mentioned monorepo architecture in frontend development.
[workspace]
members = [
"packages/react",
"packages/react-dom",
"packages/react-reconciler",
"packages/shared"
]
Because react and react-dom will export methods for JavaScript to call, we need to create a WASM project using cargo generate --git https://github.com/rustwasm/wasm-pack-template for those two. The other two can be created as regular Rust projects using cargo new.
Setting up the Debugging Environment
Let's delete the code in hello-world/src/main.tsx and write a very simple example:
import {createRoot} from 'react-dom'
const comp = <div>hello world</div>
console.log(comp)
console.log(createRoot(document.getElementById('root')))
When running in the development environment, you can see the compiled code in the browser's debug window as follows:
Now, our goal is to make the hello-world use the React version we are currently developing. To get the above code to run successfully, we need to do the following steps:
- Modify the
package.jsonin the hello-world project:
"react": "file://../../packages/react/pkg/react",
"react-dom": "file://../../packages/react-dom/pkg/react-dom",
- Add a packaging command to the
package.jsonin the root directory, usingwasm-packto package react and react-dom into WASM:
"scripts": {
"build:react": "wasm-pack build packages/react --out-dir pkg/react --out-name jsx-dev-runtime",
"build:react-dom": "wasm-pack build packages/react-dom --out-dir pkg/react-dom --out-name index",
"build": "npm run build:react && npm run build:react-dom"
},
- Add the following code to the
lib.rsin both react and react-dom:
// react/src/lib.rs
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn jsxDEV() -> String {
"hello world".to_string()
}
// react-dom/src/lib.rs
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn createRoot(container: &JsValue) -> JsValue {
JsValue::null()
}
Since the naming convention in Rust is generally snake_case, it's better to change it to this:
// react/src/lib.rs
use wasm_bindgen::prelude::*;
#[wasm_bindgen(js_name = jsxDev)]
pub fn jsx_dev() -> String {
"hello world".to_string()
}
// react-dom/src/lib.rs
use wasm_bindgen::prelude::*;
#[wasm_bindgen(js_name = createRoot)]
pub fn create_root(container: &JsValue) -> JsValue {
JsValue::null()
}
- Run
npm run buildin the root directory, then runpnpm installandnpm run devin the hello-world directory, open the page in the browser, and you will see the following output:
This indicates that the method exported by WASM has been successfully called on the JS side, and the debugging environment is set up. However, the slightly troublesome part is that if the code is modified, step 4 needs to be executed again.
In the next article, we will implement the jsx_dev function, so stay tuned.


Top comments (2)
#[wasm_bindgen(js_name = jsxDev)]change to#[wasm_bindgen(js_name = jsxDEV)]yes, you are right