Rust/Wasm and Yew (and you too)

Web-Specific

About Me

  • Full Stack Developer

    • https://freshconsulting.com
  • Twitch Streaming:

    • https://twitch.tv/cmgriffing
  • Awesome Hoodie Collection

  • Former Snowboard Bum

What is a Yew developer’s favorite song?

Its not Un-Yew-sual

Introducing Yew

What is Yew?

Yew is a modern Rust framework for creating multi-threaded front-end web apps with WebAssembly.

Project Stats

  • Started: December 2017

Wasm? Wat?

In its own words

  • a binary instruction format for a stack-based virtual machine

  • a portable compilation target for programming languages

Other Wasm-capable Languages

https://github.com/appcypher/awesome-wasm-langs

  • UI:

    • Blazor (C#)
  • Generic:

    • NWSTK (C++)
    • Cheerp (C)
    • TinyGo
    • and many more

My Favorite Video about Wasm

Getting Started

Project Setup: Manually

  • Make sure you have Rust and Cargo

    • rustup is recommended
  • Get some tooling

    • wasm-pack (recommended)
    • wasm-bindgen (not recommended)
    • cargo-web (older)

Starter Templates

Official

Unofficial

Examples

https://github.com/yewstack/yew/tree/master/examples

Best places to start:

  • todomvc
  • custom_components
  • dashboard

What is a Yew developer’s favorite musical instrument?

A Yew-kulele

Features: html!

html!

  • • A jsx-like syntax
  • • HTML and SVG

Beware! Recursion Limit Error Prone


#![recursion_limit="1024"]

html! {
    <div>
        <button onclick=self.link.callback(|_| Msg::AddOne)>{ "+1" }</button>
        <p>{ self.value }</p>
        <ul class="item-list">
            { self.props.items.iter().map(renderItem).collect::<Html>() }
        </ul>
    </div>
}

html!: Fragments

html! {
    <>
        <div></div>
        <p></p>
    </>
}

html!: Iterators

html! {
    <ul class="item-list">
        { self.props.items.iter().map(renderItem).collect::<Html>() }
    </ul>
}
html! {
    <ul class="item-list">
        { for self.props.items.iter().map(renderItem) }
    </ul>
}

html!: CSS Classes

html! {
  <div class="container"></div>
  <div class="container center-align"></div>
  <div class=format!("{}-container", size)></div>
  <div class=("class-1", "class-2")></div>
  <div class=vec!["class-1", "class-2"]></div>
  <div class=self.classes()></div>
}

html!: Literals

let text = "lorem ipsum";
html!{
    <>
        <div>{text}</div>
        <div>{"dolor sit"}</div>
        <span>{42}</span>
    </>
}

html!: Expressions

html! {
  <div>
    {
      if show_link {
        html! {
          <a href="https://example.com">{"Link"}</a>
        }
      } else {
        html! {}
      }
    }
  </div>
}

Features: Components

Components: Lifecycle

Components: Props Example

pub struct Container(Props);

#[derive(Properties, Clone)]
pub struct Props {
    pub children: Children,

    #[props_or_default]
    pub some_string,

    #[props_or_(true)]
    pub some_boolean,
}

impl Component for Container {
    type Properties = Props;

    fn view(&self) -> Html {
       html! {
           <div id="container">
               { self.0.children.render() }
           </div>
       }
    }
}

Components: Event Example

pub struct Container{
    props: Props,
    link: ComponentLink<Self>,
}

#[derive(Properties, Clone)]
pub struct Props { }

#[derive(Clone, PartialEq)]
pub enum Msg {
    Reset,
    HandleClick,
    HandleInput(event)
}

impl Component for Container {
    type Properties = Props;

    fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
        Self {
            props,
            link,
        }
    }

    fn update(&mut self, msg: Self::Message) -> ShouldRender {
        match msg {
            Msg::HandleClick => {
                info!("HANDLING CLICK");
            }
            Msg::HandleInput(input_value) => {
                info!("HANDLING INPUT {:?}", input_value);
            }
        }

        true
    }

    fn view(&self) -> Html {
        html! {
            <div id="container">
                <input
                  oninput=self.link.callback(|event: InputData| Msg::handleInput(event.value))
                />
                <button
                  onclick=self.link.callback(|_| Msg::handleClick)
                >{"Click Me"}</button>
            </div>
        }
    }
}

Components: Refs

// In create
self.node_ref = NodeRef::default();

// In view
html! {
    <div ref=self.node_ref.clone()></div>
}

// In update
let has_attributes = self.node_ref.try_into::<Element>().has_attributes();

Features: Agents

Agents

Similar to Angular Services

Uses web-workers for concurrency

Useful for:

  • pass messages to any other component
  • share state
  • offload heavy computation to worker thread

Agents: Lifecycle

Agents: Types

  • Context (UI Thread Singleton)
  • Job (UI Thread)
  • Public (New Thread Singleton)
  • Private (New Thread)

Agents: Bridges and Dispatchers

  • Bridge: bi-directional communication

    • (component <-> agent and agent <-> agent)
  • Dispatcher: unidirectional

    • sends from component to Agent

Services

Used to abstract Browser APIs

Storage

let storage = StorageService::new(Area::Local).expect("storage was disabled by the user");
let Json(database) = storage.restore(KEY);

Fetch

fn fetch_json(&mut self, binary: AsBinary) -> yew::services::fetch::FetchTask {
        let callback = self.link.callback(
            move |response: Response<Json<Result<DataFromFile, Error>>>| {
                // Handle response
            },
        );
        let request = Request::get("/data.json").body(Nothing).unwrap();

        FetchService::fetch(request, callback).unwrap()
        }
    }

Render (requestAnimationFrame)

let render_frame = self.link.callback(Msg::Render);
let handle = RenderService::request_animation_frame(render_frame);

Interval/Timeout

TimeoutService::spawn(Duration::from_secs(3), self.callback_done.clone());
IntervalService::spawn(Duration::from_secs(1), self.callback_tick.clone());

And More

  • Keyboard
  • Resize
  • WebSocket
  • Dialog
  • Console

Where do devs go to learn about Yew?

A Yew-niversity

Demo!!

Internals

web-sys OR stdweb

  • web-sys <-> wasm-bindgen

  • stdweb <-> cargo-web

Debugging: Panics

console_error_panic: nicer stacktraces

  • not compatible with cargo-web

Debugging: console.log

log: rust standard crate just works

ConsoleService: Yew wrapper service

Yewtil

  • NeqAssign: shallow prop checks

  • PureComponent: uses NeqAssign (memoized based on props)

  • Lrc: linked list reference counted smart pointer

  • Mrc/Irc: Mutable/Immutable reference counted smart pointers

  • History: A history tracking wrapper that uses a VecDeque

Undocumented

  • Component lifecycle state machine

  • VDOM diff algorithm

Optimizing

Optimizing Runtime

  • neq_assign
  • RC
  • (non-native) Pure Components
  • (missing) Function Components (coming soon?)

Optimizing Build Size

  • wee-alloc
  • release compile profile
  • wasm-opt: Loads WebAssembly and runs Binaryen IR passes on it.

Optimizing Build: wee-alloc

wee_alloc: The Wasm-Enabled, Elfin Allocator.

  • Elfin, i.e. small:

    • less than a kilobyte of uncompressed WebAssembly code.
    • Doesn’t pull in the heavy panicking or formatting infrastructure.
  • WebAssembly enabled: Designed for the wasm32-unknown-unknown target and #[no_std].

Optimizing Compilation Time

Cargo Workspaces

  • make your main crate handle routing/page selection

  • move all commonly shared code to another crate

  • make a different crate for each page

What does batman use to climb a tree?

His Yew-tility belt

Notes

Docs need some work

Direct CSS Support

https://github.com/yewstack/yew/issues/533

Testing

wasm-bindgen-test

use wasm_bindgen_test::*;

#[wasm_bindgen_test]
fn pass() {
    assert_eq!(1, 1);
}

External Libs

  • Needed (according to docs site)

    • Bootstrap/MaterialUi/arbitrary css framework component wrappers
  • Yewtify seems promising

Wrapping Up

Other Rust Frameworks

https://github.com/flosse/rust-web-framework-comparison#frontend-frameworks-wasm

Notables

  • Seed
  • Percy
  • Draco
  • Smithy

Other Rust Streamers (Game focused)

  • rhymu8354 - game dev - backend
  • mrhalzy - game dev - rust + godot
  • madhousesteve - game dev - wasm/browser
  • togglebit - game dev - rust + godot
  • ectondev - game engine dev
  • brookzerker - game dev (gg.ez)
  • ferrisstreamsstuff - emulator and hardware dev
  • urouroniwa - Dwarf Fortress clone

Other Rust Streamers (misc)

  • codeshow - en/pt - web backend
  • provod - en/ru - digital art, demoscene
  • museun - variety coding - no mic
  • gamozo - low level (OS, fuzzing, etc)
  • tsoding - variety coding
  • jonhoo - infrequent for now
  • steveklabnik - THE Steve Klabnik
  • erikdotdev - backend focused
  • rustaceans (Team) (managed by codeshow)

Citations

Goodbye React/Angular/etc?

Never gonna give Yew up

Thank you