Frontend Web
Join us at HeatSync Labs for a tour of front end web work in Rust. Yep, were doing front end in Rust, too. Theres a lot to talk about so help us by knocking off some of these topics:
- static site generators
- single-page application (SPA) and web apps
- isomorphic web apps
- elm inspired frameworks
- virtual dom libraries
- jsx style templating
- and of course web assembly
Tour of Front End Web via @jacobRosenthal
Static sites
Cheating a bit I know, but static site generators belong here somewhere. You can write your web content in some kind of template form, generally markdown, and just apply some theme to it and have it spit out a site. This is common in Golang via Hugo and very much available in Rust too. Our big one is Zola (né Gutenberg) I built our rust.azdevs.org in zola.
And then mdbook tool, what the Rust Book is made with. All the code snippets are by default tested be tested with mdbook test and as a result prs can be tested.
Frontend web is actually Wasm
Pure for the web is actually a story of Wasm.
So were not arguing here if you're going to be utilizing Rust in your future web development. You will be and may already be. Tools like wasm-pack are already spitting out and publishing Rust crates as Wasm to the NPM registry.
Rust compiled to wasm is better at performant code you previously would have to hope your browser or os gave you as an api call:
- tight event loop, games, fluid interfaces
- math transformations (not offered native by the os or browser, like ml commonly is now) audio, video
For projects where these are a minor part of your web feel free to continue consuming Rust in small quantities. Tightening up this process is the current focus of the Rust Web working group so you're in good hands.
But some amongst us want to go further, to Pure Rust Frontend Web.
Why not pure Rust
Because Rust Web Working Group daddy told you not to
"Additionally, full Web apps are not a unique advantage for Rust" "Our hope with the toolkit is that a rising tide will lift all boats, regardless where your project lands on that spectrum" Rust web working group's 2019 official statement
Wasm calls with arguments for anything other than primitives like i32 f64 still need a bit of js glue code and are going to perform worse than js calls for the near future. Were waiting on interface-types and reference-types to put Wasm calls at an advantage over js calls as they can be statically validated once rather than dynamically checked every time. This was well laid out by this recent Mozilla blog from Lin Clark
Because {your favorite language here} will have an article on hackernews about their pure Wasm frontend framework in the next few years. If you're thinking ahead you're realizing Wasm is going to increasingly be a target for most languages. If you hang out long enough golang and even typescript will be offering some frontend web thing compiled to Wasm. Theyre not anywhere near though. They need a garbage collector and GC proposal isn't moving fast.
Why pure Rust for web?
-
You want to write more Rust or less JS
-
You're doing something performant (audio/video/game/ui) Moores law is dead. The lie that we can sacrifice performance is over and if were going to compete on performance you want to be on the side of Rust. And what comes for free with this performance and size optimization is embedded and iot applications. No more half ass poorly featured web standard stuff on your 8bit arduino in C++ you can't figure out how to patch.
-
You want to use A LOT of Rust(Wasm) crates. Why glue them together in js land? Especially when someday soon(ish) Wasm api calls will be faster than js calls
-
Concurrency is what Rust was built for and threading is coming Next and more important, Wasm threads are coming. Web Workers already allow concurrency but they provide very limited ability to share resources between threads. Yew is offering an actor model based on today's capabilites, but you can only message pass with postMessage and it requires all resources to be Clone so its not terribly performant. Its generally used for kicking off a single costly process. Soon with SharedArrayBuffer you'll be able to share memory between threads. But it seemed to get pushed back post Spectre and only Chrome has reenabled it since then. This wasm multithreading concurrency post from last year lays the roadmap out.
Ecosystem
The web working group is actually the Wasm group. As always in Rust, the Wasm Book is really good and should be your starting place.
The smallest spa you could start with is a little Wasm app that mutates the DOM compiled to Wasm and loaded as a script. This shouldn't sound as odd as you might think, as this is what React and all the rest have been doing for years now.
In src/lib.rs should look pretty familiar. We're leaning on the web-sys another wasm wg joint. These are autogenerated bindings to all the web api.
let window = web_sys::window().expect("no global `window` exists");
let document = window.document().expect("should have a document on window");
let body = document.body().expect("document should have a body");
// Manufacture the element we're gonna append
let val = document.create_element("p")?;
val.set_inner_html("Hello from Rust!");
body.append_child(&val)?;
Ok(())
Note, almost everything gated by features in order to maintain get small code size, so if you get an error saying a dom element type wasn't found remember you need to manually enable features one by one in the Cargo.toml
Then wasm-pack
tool which drives Cargo will generate our wasm output.
$ cargo install wasm-pack
$ wasm-pack build --target web
$ ls pkg/
README.md without_a_bundler.d.ts without_a_bundler_bg.d.ts
package.json without_a_bundler.js without_a_bundler_bg.wasm
So well need to load our 'script'. Theres one issue with Wasm, we need to manually init
<script type="module"> import init, { add } from "./pkg/without_a_bundler.js"; async function run() { await init(); } run(); </script>
Then you just need some kind of simple webserver python3 -m http.server
or python -m SimpleHTTPServer
We can add the rest of the autogenerated JavaScript APIs that are guaranteed to exist in all standards-compliant ECMAScript environments such as such as Array, Date, and eval with the js-sys crate.
We still need efficient dom updates. Many people are working across many different concepts, but two members of the Wasm WG have competing visions that stand out:
- @fitzgen has a bump allocated virtual domdodrio
- @Pauan has a stack allocated frp signals approach called dominator which apparently gets better performance than vdoms.
Who wants to write Rust though? Cant I have a JSX style html dsl? Yes, I have several to sell you but lets focus on typed-html
let mut doc: DOMTree<String> = html!(
<html>
<head>
<title>"Hello Kitty"</title>
<meta name=Metadata::Author content="Not Sanrio Co., Ltd"/>
</head>
<body>
<h1>"Hello Kitty"</h1>
<p class="official">
"She is not a cat. She is a human girl."
</p>
{ (0..3).map(|_| html!(
<p class="emphasis">
"Her name is Kitty White."
</p>
)) }
<p class="citation-needed">
"We still don't know how she eats."
</p>
</body>
</html>
);
let doc_str = doc.to_string();
What about Elm and React inspired frameworks? Yes yes and Yes.
- seed 'realworld' example and Quickstart with webpack typescript
- yew wasm-pack template
- gloo The WG's official effort to build modular toolkit for the entire ecosystem.. Frankly has gone quiet lately.
Soo..
So whats our status. Nothing here is at all battle tested. If you were paying attention besides the working group's efforts, these are all mostly 1 person outfits. Some of it is occasionally benchmarked but not most of it yet. Nothing considered production or 1.0 yet. By no means arguming you should be shipping Rust for frontend web today, but these issues are going to close faster than we think and you may be suprised.
Crates you should know
- chrono via @finkegabriel "aims to be a feature-complete superset of the time library"
- facade via @jacobrosenthal "a framework to rapidly add web-UI to any Rust program. It let enrich your console or server app with an awesome UI just in a matter of seconds. Facade fuses web-server and WASM-based web-app into Rust program. No external files. No external dependencies. Just your binary."