I continued learning Yew after [this post]({{< ref “rust/yew_tutorial_1.md” >}}).
As of Feb. 12 2022, I’ve followed/referred the “Next” version of the Yew documentation (not only v0.19) because there are lots of changes.
And I found this statement:
You are currently reading about function components - the recommended way to write components when starting with Yew.
But we have to note that there is a more advanced, but less recommended way to write them - Struct components
In this post, I followed the “Struct components” way, so you can skip this post.
There was a template repository in the official tutorial.
https://github.com/yewstack/yew-wasm-pack-minimal
As far as I tried as of Feb 2 2022, the minimal template in documentation won’t be built with version 0.19. (0.17 and 0,18 are OK)
git clone git@github.com:yewstack/yew-wasm-pack-minimal.git
cd yew-wasm-pack-minimal
wasm-pack build --target web
error[E0407]: method `change` is not a member of trait `Component`
error[E0412]: cannot find type `ComponentLink` in this scope
error[E0050]: method `create` has 2 parameters but the declaration in trait `create` has 1
I followed another example in the official document:
https://yew.rs/docs/getting-started/build-a-sample-app
Create a project by cargo new --lib yew-app
.
Cargo.toml
:
[package]
name = "yew-app"
version = "0.1.0"
edition = "2018"
[dependencies]
yew = "^0.19"
wasm-bindgen = "^0.2"
[lib]
crate-type = ["rlib", "cdylib"]
src/lib.rs
:
use yew::prelude::*;
use wasm_bindgen::prelude::*;
enum Msg {
AddOne,
}
struct Model {
value: i64,
}
impl Component for Model {
type Message = Msg;
type Properties = ();
fn create(_ctx: &Context<Self>) -> Self {
Self {
value: 0,
}
}
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
Msg::AddOne => {
self.value += 1;
// the value has changed so we need to
// re-render for it to appear on the page
true
}
}
}
fn view(&self, ctx: &Context<Self>) -> Html {
// This gives us a component's "`Scope`" which allows us to send messages, etc to the component.
let link = ctx.link();
html! {
<div>
<button onclick={link.callback(|_| Msg::AddOne)}>{ "+1" }</button>
<p>{ self.value }</p>
</div>
}
}
}
#[wasm_bindgen(start)]
pub fn run_app() {
yew::start_app::<Model>();
}
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Yew App</title>
</head>
</html>
Build:
wasm-pack build --target web
Result:
➜ tree -I target
.
├── Cargo.lock
├── Cargo.toml
├── index.html
├── pkg
│ ├── package.json
│ ├── yew_app_bg.wasm
│ ├── yew_app_bg.wasm.d.ts
│ ├── yew_app.d.ts
│ └── yew_app.js
└── src
└── lib.rs
You can bundle with Trunk:
trunk serve
Trunk generate bundled app in dist
directory.
And, yes! You can see a click counter on localhost:8080.
create
, update
, view
are called lifecycle methods.
You must look through this GitHub issue. GJ, and thank you @mc1098!
Cited from the issue, this image represent when the view
method will be kicked (when a new viiew would be rendered):
And here is a whole lifecycle of a Component:
crate | view | rendered | update |
---|---|---|---|
When a component is created, it receives properties from its parent component and is stored within the Context<Self> thats passed down to the create method. | The view method allows you to describe how a component should be rendered to the DOM. | The rendered component lifecycle method is called once view has been called and Yew has rendered the results to the DOM, but before the browser refreshes the page. | Communication with components happens primarily through messages which are handled by the update lifecycle method. This allows the component to update itself based on what the message was, and determine if it needs to re-render itself. |
There are two assciated types for Components, Message
and Properties
.
The Message
type is used to send messages to a component after an event has taken place:
enum Msg {
AddOne,
}
//--snip--
impl Component for Model {
type Message = Msg;
Properties
represents the information passed to a component from its parent. This type must implement the Properties
trait (usually by deriving it) and can specify whether certain properties are required or optional.
All component lifecycle methods take a context object. This object provides a reference to component’s scope, which allows sending messages to a component and the props passed to the component.
link
Context.link
can be used to register callbacks or send messages to the component.
fn view(&self, ctx: &Context<Self>) -> Html {
// This gives us a component's "`Scope`" which allows us to send messages, etc to the component.
let link = ctx.link();
html! {
<div>
<button onclick={link.callback(|_| Msg::AddOne)}>{ "+1" }</button>
<p>{ self.value }</p>
</div>
}
}
https://doc.rust-lang.org/reference/linkage.html
rlib
: “Rust library” file will be produced. This is used as an intermediate artifact and can be thought of as a “static Rust library”. Theserlib
files, unlike staticlib files, are interpreted by the compiler in future linkage.
cdylib
: A dynamic system library will be produced. This is used when compiling a dynamic library to be loaded from another language. This output type will create*.so
files on Linux,*.dylib
files on macOS, and*.dll
files on Windows.
If you don’t include these two, wasm-pack
returns error as follows:
➜ wasm-pack build --target web
Error: crate-type must be cdylib to compile to wasm32-unknown-unknown. Add the following to your Cargo.toml file:
[lib]
crate-type = ["cdylib", "rlib"]
localhost:8080
index.html
import index-***.js
and index-***.wasm
.body
injected.With the two example, you could get an very high overview how Yew works.
It’s time to read Concepts section of the official document.
https://blog.logrocket.com/rust-webassembly-frontend-web-app-yew/
v0.18, but good example.
Design in Yew is still in discussion, but SCSS/tailwind will be mainstream?