
Show HN: Percy – A Rust and WebAssembly isomorphic virtual DOM implementation - chinedufn
https://github.com/chinedufn/percy
======
nevi-me
Hi chinedufn, this looks very interesting. I am at the same stage as you re.
"I REALLY want to use this for everything ...".

I see you're using wasm_bindgen quite a bit, but from a cursory look at the
code; I can't figure out something:

I can see how you're using wasm in the front-end, but could you please
elaborate a bit more about in the backend? I presume that your backend is Rust
based?

I spent last weekend porting some code from NodeJS to a wasm module. I got it
sort of working on Node, but haven't tried it on the browser yet (ran out of
weekend).

I'm quite optimistic about wasm [and Rust with wasm_bindgen]. I'd been
thinking of writing a native add-on for Node, but the idea that I can use wasm
instead is exciting.

~~~
chinedufn
Sure!

The backend is pretty much this (messy) file -
[https://github.com/chinedufn/percy/blob/master/examples/isom...](https://github.com/chinedufn/percy/blob/master/examples/isomorphic/server/src/server.rs)
.

It

1\. Pulls in your application crate 2\. Initializes your app (more or less
sets initial state 3\. Renders your app's virtual DOM into an HTML string 4\.
Serves the HTML to the client, along with the initial state serialized into
JSON in a script tag (using serde-json) 5.Also serves the WASM script and the
JS that initializes the WASM

So that's your server crate. Then your client crate also pulls in your same
application crate and you compile it to WebAssembly and that's what runs
browser side.

Feel free to let me know if any of that was poorly explained!

~~~
nevi-me
Thanks, that makes sense. I haven't built web servers with Rust as yet; so it
was a bit difficult to follow the code.

~~~
steveklabnik
This is good enough for an example, but for a "real" web server, you wouldn't
do this; you'd use a framework that handles a lot of this kind of thing for
you. I _think_ this code was adapted from the final chapter of the Rust book,
which is mostly about learning how to write a simple thing than being
production-ready.

------
dtx1
Can someone ELI5(+) what an isomorphic virtual dom is?

~~~
blattimwind
An isomorphic virtual DOM is much like an inverted-index homomorphic shadow
DOM, but instead of using a shadowed mapping they virtualize the original
elements _themselves_.

~~~
dtx1
I'm not sure if you are you are fucking with me or if i'm just ery stupid....
:(

~~~
navaati
AFAIK he's fucking with you ^^

~~~
chris_wot
Yup. Otherwise he's basically talking about a structure-preserving index that
maps DOM elements - that are rendered outside the main document DOM tree - to
a file.

And that's not really what's going on here.

------
thinkpad20
How does this compare to the Yew framework?

~~~
chinedufn
Yew is awesome and just knowing that something like that was possible inspired
Percy. I also looked at Yew's `html!` macro when figuring out how Percy's
could / should work.

One difference is that Yew is powered by stdweb and Percy is powered by wasm-
bindgen.

I'm personally SUPER bullish on wasm-bindgen because it's been designed from
day 1 to be able to take advantage of the host bindings proposal when it
materializes.

Host bindings tl;dr is that instead of needing to go through JS to interact
with browser APIs you can interact with them directly.

Another difference is that to my knowledge Yew doesn't support server side
rendering ( which was why I couldn't use it even though I wanted to :( ).

Without having used Yew I don't want to comment any further than those high
level differences.

I can say that a big focus of Percy is to be a grab bag of modules / tooling
for frontend Rust web apps with a major focus of you being able to swap out
the parts that you think are bad for other peoples' better implementations.
That dream isn't realized yet.. but I think that Rust's generics / traits
could make this feel very clean!

~~~
JD557
>One difference is that Yew is powered by stdweb and Percy is powered by wasm-
bindgen. >I'm personally SUPER bullish on wasm-bindgen because it's been
designed from day 1 to be able to take advantage of the host bindings proposal
when it materializes.

I find it funny that you mentioned that. I actually was trying out wasm-
bindget/yew recently and ended up using yew because of the wasm-bindgen
limitations (mostly related to using anything with generics in the type
signature).

The experience will probably improve on the future, but right now yew's actor
model seems like a much simpler way to encapsulate rust libraries.

------
Buetol
The use of the html macro is incredibly nice, no need for a special "JSX"
language, it’s all builting thanks to macros. I’m working on the same thing
for python (using pyxl/mixt/lys and brython) and you just proved that there’s
interrest in that !

------
andrethegiant
Heads up, there's something already called Percy in the JS space.
[https://percy.io](https://percy.io)

~~~
jhabdas
Pretty sure there's something called everything in the JS space. Thankfully
this is the Rust/WASM space so all good names get to be recycled.

~~~
mekkkkkk
Damn, time to get started on React.rs. Is rQuery taken?

~~~
oconnor663
Not only is rQuery not taken, jQuery isn't taken either :-D

------
_bxg1
[makes side-eyes at React team to utilize webassembly]

~~~
skybrian
Why? Has anyone shown this would be faster or a smaller download?

~~~
_bxg1
If they moved their virtual DOM (the meat of the work React does and its
primary bottleneck) over to webassembly, it would almost certainly be faster.
I can't really speak to the download size, though I wouldn't be surprised if
that improved too.

~~~
julian37
It's worth keeping in mind that WebAssembly can't access the DOM directly; it
has to call into JS in order to do that. I haven't investigated it myself but
I'd wager that because of this, any performance benefits would be negligible.

WebAssembly also can't access JS object structures directly, which means that
the virtual DOM would have to live in WebAssembly land in the first place, ie.
anything generated by JS that you want to end up in the DOM would have to be
copied over first, or rather, you want all your VDOM-generating code to be
WebAssembly code as well if you want the whole thing to be efficient.

~~~
_bxg1
The whole point of a virtual DOM is to simulate and reconcile changes before
pushing those changes to the real DOM; it would be fairly clean to do all that
processing in wasm and then send the result over to JS for reification.

It's true that there would be some challenges when it comes to "sending" data
to the virtual DOM; in particular, event objects could get complicated. I
assumed this project has solved that problem, but I didn't read very deeply
into it.

~~~
skybrian
Keep in mind that WebAssembly strings are not JavaScript strings, so there is
additional data conversion overhead that needs to be accounted for, versus a
JavaScript implementation. And it's not just events and DOM updates. If you
wanted to preserve React's render API, the input data for re-rendered DOM
needs to be converted from JS before doing a diff.

With slower data conversion and faster internal calculation, it's plausible
that it might overall be either slower or faster. I don't see any way to know
without doing some experiments. It would certainly be more complicated.

It seems like the sort of JavaScript library where WebAssembly would be an
easy win would have low API bandwidth (not much data crossing the boundary)
and do a lot of expensive internal calculations. As a UI framework, React does
some internal calculations but has relatively high API bandwidth.

~~~
steveklabnik
WebAssembly doesn't have strings, so you can model them just like JS strings
if you want. It really depends on what you want to do.

------
jhabdas
Has Facebook picked up on this yet? Good thing the Web is a Web of documents.

~~~
steveklabnik
Facebook is a Rust user, but given that this was just announced, I doubt that
they're suddenly using it ;)

> Good thing the Web is a Web of documents.

That was three for three years. It's been 23 years since we've had executable
code as well.

------
jlebrech
can you write an app in just once file and mark what can run in the client and
what must only run on the server?

~~~
chinedufn
As in an example app?

If you don't need server side rendering you can have one Rust file but you
also need a JS file to initialize the WebAssembly module. So just one file
isn't reasonably possible.

For a server side rendered app you need to have a cargo workspace with 3
crates (they can be in the same repo so not a huge deal).

One crate is a `cdylib` that you compile to WebAssembly to serve your app to
the client. This is a light wrapper around your actual application.

One crate is your actual application.

And one crate is your server, which is also a light wrapper around your actual
application.

That server side rendering structure can be found here -
[https://github.com/chinedufn/percy/tree/master/examples/isom...](https://github.com/chinedufn/percy/tree/master/examples/isomorphic)

But in terms of a super minimal example without server side rendering.. I'll
be working on adding one (along with updating the current example)!

~~~
jlebrech
what i want to see is to have all logic to be centered around features, and
have a framework compile the server side and client server out of one logical
piece of code, if that even makes sense.

a bit like in the old days of php when you'd have the serverside code and html
rendered in their, but this time nearly everything is running on the client
unless the client can't be trusted.

you could have some kind of ring system, where something can never run on the
client right up to always run on client, but all in one file.

but maybe that's nasty.

~~~
somenewacc
ASP.NET does this, you use its components and it generates client side
javascript. In a more functional way, there's another language that does this
caled Opa! [http://opalang.org/](http://opalang.org/)

~~~
jlebrech
yes, like runat="server" kinda thing.

------
lovescalajs
Another good tool if you want to write front end in a great general purpose
language is scalajs. It allows you to reuse the dame Scala code for backend
and front end.

