rs-yew-crate
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseYew Framework (v0.22)
Yew框架(v0.22)
Yew is a modern Rust framework for creating multi-threaded frontend web applications compiled to WebAssembly. It uses a virtual DOM, component-based architecture, and a JSX-like macro. MSRV: Rust 1.84.0.
html!Yew是一款现代Rust框架,用于创建可编译为WebAssembly的多线程前端Web应用。它采用虚拟DOM、基于组件的架构,以及类JSX的宏。最低支持Rust版本(MSRV):1.84.0。
html!Project Setup
项目搭建
Prerequisites
前置条件
bash
rustup target add wasm32-unknown-unknown
cargo install --locked trunkbash
rustup target add wasm32-unknown-unknown
cargo install --locked trunkNew Project (manual)
手动创建新项目
bash
cargo new yew-app && cd yew-appCargo.toml:
toml
[package]
name = "yew-app"
version = "0.1.0"
edition = "2021"
[dependencies]
yew = { version = "0.22", features = ["csr"] }Featureenablescsrand client-side rendering code. Omit for library crates. UseRendererfor server-side rendering. Usessrfor serde integration onserde.AttrValue
index.html (project root):
html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Yew App</title>
</head>
<body></body>
</html>src/main.rs:
rust
use yew::prelude::*;
#[component]
fn App() -> Html {
let counter = use_state(|| 0);
let onclick = {
let counter = counter.clone();
move |_| { counter.set(*counter + 1); }
};
html! {
<div>
<button {onclick}>{ "+1" }</button>
<p>{ *counter }</p>
</div>
}
}
fn main() {
yew::Renderer::<App>::new().render();
}bash
trunk serve --openbash
cargo new yew-app && cd yew-appCargo.tom:
toml
[package]
name = "yew-app"
version = "0.1.0"
edition = "2021"
[dependencies]
yew = { version = "0.22", features = ["csr"] }特性启用csr和客户端渲染代码。库 crate 可省略该特性。使用Renderer启用服务端渲染。使用ssr可在serde上集成序列化功能。AttrValue
index.html(项目根目录):
html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Yew App</title>
</head>
<body></body>
</html>src/main.rs:
rust
use yew::prelude::*;
#[component]
fn App() -> Html {
let counter = use_state(|| 0);
let onclick = {
let counter = counter.clone();
move |_| { counter.set(*counter + 1); }
};
html! {
<div>
<button {onclick}>{ "+1" }</button>
<p>{ *counter }</p>
</div>
}
}
fn main() {
yew::Renderer::<App>::new().render();
}bash
trunk serve --openStarter Template
启动模板
bash
cargo generate --git https://github.com/yewstack/yew-trunk-minimal-template
trunk servebash
cargo generate --git https://github.com/yewstack/yew-trunk-minimal-template
trunk serveTrunk Configuration (Trunk.toml)
Trunk配置(Trunk.toml)
toml
[serve]
address = "127.0.0.1"
port = 8080toml
[serve]
address = "127.0.0.1"
port = 8080Function Components
函数组件
The recommended component model. Annotate with , name in PascalCase, return .
#[component]Htmlrust
use yew::{component, html, Html};
#[component]
fn HelloWorld() -> Html {
html! { "Hello world" }
}
// Usage:
html! { <HelloWorld /> }推荐使用的组件模型。使用注解,组件名采用大驼峰命名,返回类型。
#[component]Htmlrust
use yew::{component, html, Html};
#[component]
fn HelloWorld() -> Html {
html! { "Hello world" }
}
// 用法:
html! { <HelloWorld /> }Properties (Props)
属性(Props)
Derive . Use instead of for attribute values (cheap to clone).
Properties + PartialEqAttrValueStringrust
use yew::{component, html, Html, Properties};
#[derive(Properties, PartialEq)]
pub struct Props {
pub name: AttrValue,
#[prop_or_default]
pub is_loading: bool,
#[prop_or(AttrValue::Static("default"))]
pub label: AttrValue,
#[prop_or_else(|| AttrValue::from("computed"))]
pub dynamic: AttrValue,
}
#[component]
fn Greeting(&Props { ref name, is_loading, .. }: &Props) -> Html {
if is_loading { return html! { "Loading" }; }
html! { <p>{"Hello, "}{name}</p> }
}
// Usage:
html! { <Greeting name="Alice" is_loading=false /> }Children: Use field name in props to receive nested content.
children: Htmlrust
#[derive(Properties, PartialEq)]
pub struct ContainerProps {
pub children: Html,
}
#[component]
fn Container(props: &ContainerProps) -> Html {
html! { <div class="wrapper">{ props.children.clone() }</div> }
}Auto-props ( crate): Generate struct from function args.
yew-autopropsPropertiesrust
use yew_autoprops::autoprops;
// #[autoprops] must appear BEFORE #[component]
#[autoprops]
#[component]
fn Greetings(#[prop_or_default] is_loading: bool, message: &AttrValue) -> Html {
html! { <>{message}</> }
}派生。属性值使用而非(克隆成本低)。
Properties + PartialEqAttrValueStringrust
use yew::{component, html, Html, Properties};
#[derive(Properties, PartialEq)]
pub struct Props {
pub name: AttrValue,
#[prop_or_default]
pub is_loading: bool,
#[prop_or(AttrValue::Static("default"))]
pub label: AttrValue,
#[prop_or_else(|| AttrValue::from("computed"))]
pub dynamic: AttrValue,
}
#[component]
fn Greeting(&Props { ref name, is_loading, .. }: &Props) -> Html {
if is_loading { return html! { "Loading" }; }
html! { <p>{"Hello, "}{name}</p> }
}
// 用法:
html! { <Greeting name="Alice" is_loading=false /> }子组件:在Props中使用字段接收嵌套内容。
children: Htmlrust
#[derive(Properties, PartialEq)]
pub struct ContainerProps {
pub children: Html,
}
#[component]
fn Container(props: &ContainerProps) -> Html {
html! { <div class="wrapper">{ props.children.clone() }</div> }
}自动Props( crate):从函数参数自动生成结构体。
yew-autopropsPropertiesrust
use yew_autoprops::autoprops;
// #[autoprops]必须出现在#[component]之前
#[autoprops]
#[component]
fn Greetings(#[prop_or_default] is_loading: bool, message: &AttrValue) -> Html {
html! { <>{message}</> }
}Callbacks
回调函数
Callback<IN, OUT>FnRcCallback::from(|e| ...)rust
// Passing callbacks as props
#[derive(Properties, PartialEq)]
pub struct ButtonProps {
pub on_click: Callback<Video>,
}
// Creating callbacks with state
let selected = use_state(|| None);
let on_select = {
let selected = selected.clone();
Callback::from(move |video: Video| selected.set(Some(video)))
};Callback<IN, OUT>FnRcCallback::from(|e| ...)rust
// 将回调作为Props传递
#[derive(Properties, PartialEq)]
pub struct ButtonProps {
pub on_click: Callback<Video>,
}
// 结合状态创建回调
let selected = use_state(|| None);
let on_select = {
let selected = selected.clone();
Callback::from(move |video: Video| selected.set(Some(video)))
};Hooks
Hooks
Rules: (1) name starts with , (2) call only at top-level of component/hook, (3) same call order every render.
use_Pre-defined hooks:
- /
use_state— reactive local stateuse_state_eq - /
use_reducer— complex state via reducer patternuse_reducer_eq - /
use_effect— side effects (not run during SSR)use_effect_with - — memoized computation
use_memo - — memoized callback
use_callback - — consume context value
use_context - /
use_ref— persistent mutable referenceuse_mut_ref - — DOM node reference
use_node_ref - — manual re-render trigger
use_force_update
rust
// State
let counter = use_state(|| 0);
counter.set(*counter + 1);
// Effect with deps (runs when deps change)
{
let data = data.clone();
use_effect_with(deps, move |_| {
// setup logic
|| () // cleanup
});
}
// Reducer
use yew::prelude::*;
enum Action { Increment, Decrement }
struct State { count: i32 }
impl Reducible for State {
type Action = Action;
fn reduce(self: Rc<Self>, action: Action) -> Rc<Self> {
match action {
Action::Increment => Self { count: self.count + 1 }.into(),
Action::Decrement => Self { count: self.count - 1 }.into(),
}
}
}
let state = use_reducer(|| State { count: 0 });
state.dispatch(Action::Increment);Custom hooks: Annotate with , compose from built-in hooks.
#[hook]rust
use yew::prelude::*;
#[hook]
pub fn use_toggle(initial: bool) -> (bool, Callback<()>) {
let state = use_state(move || initial);
let toggle = {
let state = state.clone();
Callback::from(move |_| state.set(!*state))
};
(*state, toggle)
}规则:(1)名称以开头;(2)仅在组件/Hook的顶层调用;(3)每次渲染的调用顺序一致。
use_预定义Hooks:
- /
use_state— 响应式本地状态use_state_eq - /
use_reducer— 通过reducer模式管理复杂状态use_reducer_eq - /
use_effect— 副作用(SSR期间不执行)use_effect_with - — 记忆化计算
use_memo - — 记忆化回调
use_callback - — 消费上下文值
use_context - /
use_ref— 持久化可变引用use_mut_ref - — DOM节点引用
use_node_ref - — 手动触发重新渲染
use_force_update
rust
// 状态
let counter = use_state(|| 0);
counter.set(*counter + 1);
// 带依赖的Effect(依赖变化时执行)
{
let data = data.clone();
use_effect_with(deps, move |_| {
// 初始化逻辑
|| () // 清理逻辑
});
}
// Reducer
use yew::prelude::*;
enum Action { Increment, Decrement }
struct State { count: i32 }
impl Reducible for State {
type Action = Action;
fn reduce(self: Rc<Self>, action: Action) -> Rc<Self> {
match action {
Action::Increment => Self { count: self.count + 1 }.into(),
Action::Decrement => Self { count: self.count - 1 }.into(),
}
}
}
let state = use_reducer(|| State { count: 0 });
state.dispatch(Action::Increment);自定义Hooks:使用注解,基于内置Hooks组合实现。
#[hook]rust
use yew::prelude::*;
#[hook]
pub fn use_toggle(initial: bool) -> (bool, Callback<()>) {
let state = use_state(move || initial);
let toggle = {
let state = state.clone();
Callback::from(move |_| state.set(!*state))
};
(*state, toggle)
}html! Macro
html!宏
For detailed syntax rules, element attributes, conditional rendering, fragments, and lists, see references/html_and_events.md.
Key rules:
- Single root node required (use fragments for multiple)
<>...</> - String literals must be quoted and wrapped in braces:
{ "text" } - Tags must self-close () or have matching close tags
<br /> - Rust expressions go in
{ ... } - /
iffor conditional renderingif let - for lists (add
for item in iter { ... }prop)key
rust
html! {
<>
<h1>{ "Title" }</h1>
if show_subtitle {
<p>{ "Subtitle" }</p>
}
for item in &items {
<li key={item.id}>{ &item.name }</li>
}
</>
}关于详细的语法规则、元素属性、条件渲染、片段和列表的使用,请参考references/html_and_events.md。
核心规则:
- 必须有单个根节点(使用片段包裹多个节点)
<>...</> - 字符串字面量必须加引号并放在大括号中:
{ "text" } - 标签必须自闭合()或有匹配的闭合标签
<br /> - Rust表达式放在中
{ ... } - 使用/
if实现条件渲染if let - 使用渲染列表(添加
for item in iter { ... }属性)key
rust
html! {
<>
<h1>{ "Title" }</h1>
if show_subtitle {
<p>{ "Subtitle" }</p>
}
for item in &items {
<li key={item.id}>{ &item.name }</li>
}
</>
}Routing
路由
Add crate. See references/routing.md for nested routers, basename, and query parameters.
yew-routerrust
use yew::prelude::*;
use yew_router::prelude::*;
#[derive(Clone, Routable, PartialEq)]
enum Route {
#[at("/")]
Home,
#[at("/post/:id")]
Post { id: String },
#[not_found]
#[at("/404")]
NotFound,
}
fn switch(route: Route) -> Html {
match route {
Route::Home => html! { <h1>{"Home"}</h1> },
Route::Post { id } => html! { <p>{format!("Post {}", id)}</p> },
Route::NotFound => html! { <h1>{"404"}</h1> },
}
}
#[component]
fn App() -> Html {
html! {
<BrowserRouter>
<Switch<Route> render={switch} />
</BrowserRouter>
}
}Navigation: or hook.
<Link<Route> to={Route::Home}>{"Home"}</Link<Route>>use_navigator()添加 crate。关于嵌套路由、基准路径和查询参数的使用,请参考references/routing.md。
yew-routerrust
use yew::prelude::*;
use yew_router::prelude::*;
#[derive(Clone, Routable, PartialEq)]
enum Route {
#[at("/")]
Home,
#[at("/post/:id")]
Post { id: String },
#[not_found]
#[at("/404")]
NotFound,
}
fn switch(route: Route) -> Html {
match route {
Route::Home => html! { <h1>{"Home"}</h1> },
Route::Post { id } => html! { <p>{format!("Post {}", id)}</p> },
Route::NotFound => html! { <h1>{"404"}</h1> },
}
}
#[component]
fn App() -> Html {
html! {
<BrowserRouter>
<Switch<Route> render={switch} />
</BrowserRouter>
}
}导航:使用或 Hook。
<Link<Route> to={Route::Home}>{"Home"}</Link<Route>>use_navigator()Contexts
上下文
Provide shared state without prop drilling. See references/contexts_and_state.md.
rust
#[derive(Clone, PartialEq)]
struct Theme { foreground: String, background: String }
// Provider
#[component]
fn App() -> Html {
let theme = Theme { foreground: "#000".into(), background: "#fff".into() };
html! {
<ContextProvider<Theme> context={theme}>
<ThemedButton />
</ContextProvider<Theme>>
}
}
// Consumer
#[component]
fn ThemedButton() -> Html {
let theme = use_context::<Theme>().expect("no theme context");
html! { <button style={format!("color: {}", theme.foreground)}>{"Click"}</button> }
}无需Props透传即可共享状态。关于详细使用,请参考references/contexts_and_state.md。
rust
#[derive(Clone, PartialEq)]
struct Theme { foreground: String, background: String }
// 提供者
#[component]
fn App() -> Html {
let theme = Theme { foreground: "#000".into(), background: "#fff".into() };
html! {
<ContextProvider<Theme> context={theme}>
<ThemedButton />
</ContextProvider<Theme>>
}
}
// 消费者
#[component]
fn ThemedButton() -> Html {
let theme = use_context::<Theme>().expect("no theme context");
html! { <button style={format!("color: {}", theme.foreground)}>{"Click"}</button> }
}Data Fetching
数据获取
Use for HTTP, for deserialization, for async.
gloo-netserdewasm-bindgen-futurestoml
[dependencies]
yew = { version = "0.22", features = ["csr", "serde"] }
gloo-net = "0.6"
serde = { version = "1.0", features = ["derive"] }
wasm-bindgen-futures = "0.4"rust
use gloo_net::http::Request;
use yew::prelude::*;
#[component]
fn App() -> Html {
let data = use_state(Vec::new);
{
let data = data.clone();
use_effect_with((), move |_| {
let data = data.clone();
wasm_bindgen_futures::spawn_local(async move {
let resp: Vec<Item> = Request::get("/api/items")
.send().await.unwrap()
.json().await.unwrap();
data.set(resp);
});
|| ()
});
}
// render data...
html! {}
}使用处理HTTP请求,进行反序列化,处理异步操作。
gloo-netserdewasm-bindgen-futurestoml
[dependencies]
yew = { version = "0.22", features = ["csr", "serde"] }
gloo-net = "0.6"
serde = { version = "1.0", features = ["derive"] }
wasm-bindgen-futures = "0.4"rust
use gloo_net::http::Request;
use yew::prelude::*;
#[component]
fn App() -> Html {
let data = use_state(Vec::new);
{
let data = data.clone();
use_effect_with((), move |_| {
let data = data.clone();
wasm_bindgen_futures::spawn_local(async move {
let resp: Vec<Item> = Request::get("/api/items")
.send().await.unwrap()
.json().await.unwrap();
data.set(resp);
});
|| ()
});
}
// 渲染数据...
html! {}
}Events and DOM Interaction
事件与DOM交互
See references/html_and_events.md for full event type table and typed event targets.
rust
// Input handling with TargetCast
use web_sys::HtmlInputElement;
use yew::prelude::*;
#[component]
fn TextInput() -> Html {
let value = use_state(String::default);
let oninput = {
let value = value.clone();
Callback::from(move |e: InputEvent| {
let input: HtmlInputElement = e.target_unchecked_into();
value.set(input.value());
})
};
html! { <input type="text" {oninput} value={(*value).clone()} /> }
}完整的事件类型表和类型化事件目标,请参考references/html_and_events.md。
rust
// 使用TargetCast处理输入
use web_sys::HtmlInputElement;
use yew::prelude::*;
#[component]
fn TextInput() -> Html {
let value = use_state(String::default);
let oninput = {
let value = value.clone();
Callback::from(move |e: InputEvent| {
let input: HtmlInputElement = e.target_unchecked_into();
value.set(input.value());
})
};
html! { <input type="text" {oninput} value={(*value).clone()} /> }
}Server-Side Rendering
服务端渲染
See references/ssr.md for SSR setup, hydration, Suspense integration, and component lifecycle during SSR.
关于SSR搭建、Hydration、Suspense集成以及SSR期间的组件生命周期,请参考references/ssr.md。
Agents (Web Workers)
Agents(Web Workers)
Offload tasks to web workers for concurrent processing. Use crate. Agents support Public (singleton) and Private (per-bridge) modes. Communication via bridges (bidirectional) and dispatchers (unidirectional). Messages serialized with .
yew-agentbincode将任务卸载到Web Workers进行并发处理。使用 crate。Agents支持Public(单例)和Private(每个桥接实例一个)模式。通过桥接(双向)和调度器(单向)进行通信。消息通过序列化。
yew-agentbincodeAnti-Patterns to Avoid
需避免的反模式
- in props — Use
String(cheap clone viaAttrValue)Rc<str> - in props — Use
Vec<T>fromIArray<T>implicit-clone - Interior mutability (,
RefCell) — Yew can't detect changes; use hooksMutex - Side effects in render — Move to /
use_effectuse_effect_with - Missing keys on lists — Always add to iterated elements
key - Accessing Web APIs during SSR — Isolate in (not run server-side)
use_effect
- Props中使用— 改用
String(通过AttrValue实现低成本克隆)Rc<str> - Props中使用— 改用
Vec<T>中的implicit-cloneIArray<T> - 内部可变性(、
RefCell)— Yew无法检测到变化;使用Hooks替代Mutex - 渲染期间执行副作用 — 移至/
use_effect中use_effect_with - 列表缺少key属性 — 始终为迭代元素添加
key - SSR期间访问Web API — 隔离到中(服务端不执行)
use_effect
Ecosystem & Common Dependencies
生态系统与常用依赖
| Crate | Purpose |
|---|---|
| Client-side routing |
| Ergonomic web APIs (net, timers, storage, events, dialogs) |
| HTTP requests (fetch) |
| Rust ↔ JS interop |
| Async/await in WASM |
| Raw Web API bindings |
| JavaScript standard built-in bindings |
| Serialization |
| CSS-in-Rust |
| Redux-like state management |
| Community hooks collection |
| Auto-generate props structs |
| State management (Redux/Recoil-inspired) |
| Cheap-clone types ( |
| Crate | 用途 |
|---|---|
| 客户端路由 |
| 易用的Web API封装(网络、定时器、存储、事件、对话框) |
| HTTP请求(fetch) |
| Rust ↔ JS 互操作 |
| WASM中的Async/await支持 |
| 原始Web API绑定 |
| JavaScript标准内置对象绑定 |
| 序列化 |
| Rust中编写CSS |
| 类Redux的状态管理 |
| 社区Hooks集合 |
| 自动生成Props结构体 |
| 状态管理(灵感来自Redux/Recoil) |
| 低成本克隆类型( |
References
参考资料
- HTML macro, events, and DOM interaction: references/html_and_events.md
- Routing (yew-router) patterns: references/routing.md
- Contexts and state management: references/contexts_and_state.md
- Server-side rendering and hydration: references/ssr.md
- HTML宏、事件与DOM交互:references/html_and_events.md
- 路由(yew-router)模式:references/routing.md
- 上下文与状态管理:references/contexts_and_state.md
- 服务端渲染与Hydration:references/ssr.md