#virtual-dom #js #vdom #javascript

dodrio-js-api

An API for implementing Dodrio rendering components with JavaScript

2 unstable releases

0.2.0 May 8, 2020
0.1.0 Mar 13, 2019

#8 in #vdom


Used in dodrio

MPL-2.0 license

195KB
3.5K SLoC

Rust 3K SLoC // 0.1% comments JavaScript 359 SLoC // 0.1% comments Shell 12 SLoC

Implementing dodrio render components with JavaScript.

This crate provides a Rust type JsRender that wraps a JavaScript object with a render method. JsRender implements dodrio::Render by calling its wrapped object's render method to get a JavaScript virtual DOM represented as a tree of JavaScript values. It then converts this tree of JavaScript values into dodrio's normal bump-allocated virtual DOM representation.

This is likely much slower than rendering virtual DOMs directly into the bump allocator from the Rust side of things! Additionally, the shape of the JavaScript virtual DOM is a bit funky and unidiomatic. Keep in mind that this crate exists as a proof of concept for integrating JavaScript components into dodrio -- which is itself also experimental -- and so this crate definitely has some rough edges.

Example

Here is a JavaScript implementation of a rendering component:

class Greeting {
constructor(who) {
this.who = who;
}

render() {
return {
tagName: "p",
attributes: [
{
name: "class",
value: "greeting",
},
],
listeners: [
{
on: "click",
callback: this.onClick.bind(this),
}
],
children: [
"Hello, ",
{
tagName: "strong",
children: [this.who],
}
],
};
}

async onClick(vdom, event) {
// Be more excited!
this.who += "!";

// Schedule a re-render.
await vdom.render();

console.log("re-rendering finished!");
}
}

And here is a Rust rendering component that internally uses the JS rendering component:

use dodrio::{Node, Render, RenderContext, Vdom};
use dodrio_js_api::JsRender;
use js_sys::Object;
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern {
// Import the JS `Greeting` class.
#[wasm_bindgen(extends = Object)]
#[derive(Clone, Debug)]
type Greeting;

// And the `Greeting` class's constructor.
#[wasm_bindgen(constructor)]
fn new(who: &str) -> Greeting;
}

/// This is our Rust rendering component that wraps the JS rendering component.
pub struct GreetingViaJs {
js: JsRender,
}

impl GreetingViaJs {
/// Create a new `GreetingViaJs`, which will internally create a new JS
/// `Greeting`.
pub fn new(who: &str) -> GreetingViaJs {
let js = JsRender::new(Greeting::new(who));
GreetingViaJs { js }
}
}

/// And finally the `Render` implementation! This adds a `<p>` element and some
/// text around whatever the inner JS `Greeting` component renders.
impl<'a> Render<'a> for GreetingViaJs {
fn render(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
use dodrio::builder::*;
p(&cx)
.children([
text("JavaScript says: "),
self.js.render(cx),
])
.finish()
}
}

Dependencies

~6.5–9MB
~173K SLoC