Yew is a modern Rust framework inspired by Elm and ReactJS.
This framework is designed to be compiled into modern browsers' runtimes: wasm, asm.js, emscripten.
To prepare the developments environment use installation instruction here: wasm-and-rust
Yew implements strict application state management based on message passing and updates:
src/main.rs
```rust
extern crate yew; use yew::prelude::*;
type Context = ();
struct Model { }
enum Msg { DoIt, }
impl Component
type Message = Msg;
type Properties = ();
fn create(_: Self::Properties, _: &mut Env<Context, Self>) -> Self {
Model { }
}
fn update(&mut self, msg: Self::Message, _: &mut Env<Context, Self>) -> ShouldRender {
match msg {
Msg::DoIt => {
// Update your model on events
true
}
}
}
}
impl Renderable
fn main() { yew::initialize(); let app: App<_, Model> = App::new(()); app.mounttobody(); yew::run_loop(); } ```
Predictable mutability and lifetimes (thanks Rust) make it possible to reuse a single instance of the model without needing to create a fresh one every update. It helps reduce memory allocations.
html! macroFeel free to put pure Rust code into HTML tags with all the compiler's and borrow checker benefits.
rust
html! {
<section class="todoapp",>
<header class="header",>
<h1>{ "todos" }</h1>
{ view_input(&model) }
</header>
<section class="main",>
<input class="toggle-all",
type="checkbox",
checked=model.is_all_completed(),
onclick=|_| Msg::ToggleAll, />
{ view_entries(&model) }
</section>
</section>
}
Yew supports components! You can create a new one by implementing a Component trait
and including it directly into the html! template:
rust
html! {
<nav class="menu",>
<MyButton: title="First Button",/>
<MyButton: title="Second Button",/>
</nav>
}
Components lives in Angular-like scopes with parent-to-child (properties) and child-to-parent (events) interaction.
Properties also are pure Rust types with strict checking during compilation.
rust
html! {
<nav class="menu",>
<MyButton: color=Color::Red,/>
<MyButton: onclick=|_| ParentMsg::DoIt,/>
</nav>
}
Yew supports fragments: elements without a parent which could be attached somewhere later.
rust
html! {
<>
<tr><td>{ "Row" }</td></tr>
<tr><td>{ "Row" }</td></tr>
<tr><td>{ "Row" }</td></tr>
</>
}
Yew framework uses its own virtual-dom representation. It updates the browser's DOM
with tiny patches when properties of elements had changed. Every component lives
in its own independent loop, interacts with the environment (Scope) by messages passing
and supports fine control of rendering.
The ShouldRender return value informs the loop when the component should be re-rendered:
rust
fn update(&mut self, msg: Self::Message, _: &mut Env<Context, Self>) -> ShouldRender {
match msg {
Msg::UpdateValue(value) => {
self.value = value;
true
}
Msg::Ignore => {
false
}
}
}
It's more effective than comparing the model after every update, because not every model change leads to a view update. It lets us skip model comparison checks entirely. You can control updates very accurately.
Use single-line or multi-line Rust comments inside html-templates.
rust
html! {
<section>
/* Write some ideas
* in multiline comments
*/
<p>{ "and tags could be placed between comments!" }</p>
// <li>{ "or single-line comments" }</li>
</section>
}
You can use external crates and put values from them into the template:
```rust extern crate chrono; use chrono::prelude::*;
impl Renderable { Local::now() }
Some crates don't support the true wasm target (
wasm32-unknown-unknown) yet.
Yew has implemented pluggable services that allow you to call external APIs, such as: JavaScript alerts, timeout, storage, fetches and websockets. It's a handy alternative to subscriptions.
Implemented:
* IntervalService
* TimeoutService
* StorageService
* DialogService
* FetchService
* WebSocketService
```rust use yew::services::console::ConsoleService; use yew::services::timeout::TimeoutService;
struct Context {
console: ConsoleService,
timeout: TimeoutService
impl Component
Can't find an essential service? Want to use library from npm?
You can reuse JavaScript libraries with stdweb capabilities and create
your own service implementation. Here's an example below of how to wrap the
ccxt library:
```rust
pub struct CcxtService(Option
impl CcxtService { pub fn new() -> Self { let lib = js! { return ccxt; }; CcxtService(Some(lib)) }
pub fn exchanges(&mut self) -> Vec<String> {
let lib = self.0.as_ref().expect("ccxt library object lost");
let v: Value = js! {
var ccxt = @{lib};
console.log(ccxt.exchanges);
return ccxt.exchanges;
};
let v: Vec<String> = v.try_into().expect("can't extract exchanges");
v
}
// Wrap more methods here!
} ```
Yew allows for serialization (store/send and restore/recieve) formats.
Implemented: JSON, TOML, YAML, MSGPACK, CBOR
In development: BSON, XML
```rust use yew::format::Json;
struct Client { firstname: String, lastname: String, }
struct Model {
clients: Vec
impl Component
By default only Json format available, but you can activate more with features in
Cargo.toml of your project:
toml
[dependencies]
yew = { git = "https://github.com/DenisKolodin/yew", features = ["toml", "yaml", "msgpack", "cbor"] }
Clone or download this repository.
Add necessary targets to your compiler:
$ rustup target add wasm32-unknown-emscripten
We used
wasm32-unknown-emscriptentarget here, because not every crate could be compiled to the purewasm32-unknown-unknowntarget. But the crates still improving and you can do it soon.
To build this project you need to have [cargo-web] installed:
$ cargo install cargo-web
Add
--forceoption to ensure the latest version.
$ cargo web build
$ ./ci/run_tests.sh
There are many examples that show how the framework works: [counter], [crm], [customcomponents], [dashboard], [fragments], [gameoflife], [mountpoint], [npmandrest], [timer], [todomvc], [two_apps].
To start an example enter its directory and start it with [cargo-web]:
$ cargo web start
To run an optimised build instead of a debug build use:
$ cargo web start --release
Note: By default cargo-web will use Emscripten to generate asm.js. You can also
compile to WebAssembly if you add either --target=wasm32-unknown-emscripten or
--target=wasm32-unknown-unknown, where the first one will use Emscripten and
the second one will use Rust's native WebAssembly backend (Rust nightly only!).