Prepare for Yew 0.20 (#2973)

* 0.20 Changelog

* Improve changelog generator

* Add blog post

* Add blog post

* Apply suggestions from code review

Co-authored-by: WorldSEnder <WorldSEnder@users.noreply.github.com>
Co-authored-by: Julius Lungys <32368314+voidpumpkin@users.noreply.github.com>

* update Changelog

* update Cargo.toml

* changelog gen compiles

* website version 0.20

* add migration guides

* prettier

* i18n

Co-authored-by: WorldSEnder <WorldSEnder@users.noreply.github.com>
Co-authored-by: Julius Lungys <32368314+voidpumpkin@users.noreply.github.com>
This commit is contained in:
Muhammad Hamza 2022-11-25 15:19:07 +05:00 committed by GitHub
parent 5355b65ff5
commit f0209c786f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
126 changed files with 11865 additions and 4 deletions

View File

@ -1,5 +1,132 @@
# Changelog # Changelog
## ✨ yew **0.20.0** *(2022-11-xx)*
#### Changelog
- #### 🛠 Fixes
- Fix onsubmit event type in docs. [[@Allan](https://github.com/Allan), [#2926](https://github.com/yewstack/yew/pull/2926)]
- Fix issues with tuples in closing tag. [[@Muhammad Hamza](https://github.com/Muhammad Hamza), [#2886](https://github.com/yewstack/yew/pull/2886)]
- Fix checked property being reset. [[@WorldSEnder](https://github.com/WorldSEnder), [#2907](https://github.com/yewstack/yew/pull/2907)]
- Fix VList Stream in SSR. [[@Kaede Hoshikawa](https://github.com/Kaede Hoshikawa), [#2801](https://github.com/yewstack/yew/pull/2801)]
- Fixed `NodeRef` not being implicitly cloned with components. [[@wdcocq](https://github.com/wdcocq), [#2775](https://github.com/yewstack/yew/pull/2775)]
- Attributes: Fix apply_diff_index_maps. [[@Dietmar Maurer](https://github.com/Dietmar Maurer), [#2653](https://github.com/yewstack/yew/pull/2653)]
- Fix bubbling of events originating in shadow dom. [[@WorldSEnder](https://github.com/WorldSEnder), [#2627](https://github.com/yewstack/yew/pull/2627)]
- Fix some Hook edge cases. [[@Kaede Hoshikawa](https://github.com/Kaede Hoshikawa), [#2592](https://github.com/yewstack/yew/pull/2592)]
- Fix issue with node refs and hydration. [[@WorldSEnder](https://github.com/WorldSEnder), [#2597](https://github.com/yewstack/yew/pull/2597)]
- Fix macro hygiene issues. [[@Muhammad Hamza](https://github.com/Muhammad Hamza), [#2585](https://github.com/yewstack/yew/pull/2585)]
- Fix casing of dynamic tags. [[@WorldSEnder](https://github.com/WorldSEnder), [#2578](https://github.com/yewstack/yew/pull/2578)]
- Automatically convert closure to callback for component properties. [[@Finn Bear](https://github.com/Finn Bear), [#2554](https://github.com/yewstack/yew/pull/2554)]
- Fix a problem with NodeRefs and VTags, ref. [[@WorldSEnder](https://github.com/WorldSEnder), [#2279](https://github.com/yewstack/yew/pull/2279)]
- Fix defaulted type parameter.. [[@Kaede Hoshikawa](https://github.com/Kaede Hoshikawa), [#2284](https://github.com/yewstack/yew/pull/2284)]
- Use Ref::filter_map if rustc is later than 1.63. [[@Kaede Hoshikawa](https://github.com/Kaede Hoshikawa), [#2904](https://github.com/yewstack/yew/pull/2904)]
- Evaluate props in the order they're defined. [[@Muhammad Hamza](https://github.com/Muhammad Hamza), [#2887](https://github.com/yewstack/yew/pull/2887)]
- Context: Avoid storing a copy of children. [[@Dietmar Maurer](https://github.com/Dietmar Maurer), [#2885](https://github.com/yewstack/yew/pull/2885)]
- Various improvements to Classes, oriented around reducing allocations. [[@Nathan West](https://github.com/Nathan West), [#2870](https://github.com/yewstack/yew/pull/2870)]
- Resume Suspension upon unmount. [[@Kaede Hoshikawa](https://github.com/Kaede Hoshikawa), [#2874](https://github.com/yewstack/yew/pull/2874)]
- Make fn update() re-render the component by default. [[@Cecile Tonglet](https://github.com/Cecile Tonglet), [#2786](https://github.com/yewstack/yew/pull/2786)]
- Do not detach child elements if parent element is about to be detached. [[@Kaede Hoshikawa](https://github.com/Kaede Hoshikawa), [#2420](https://github.com/yewstack/yew/pull/2420)]
- remove some unsafes by using atomics. [[@WorldSEnder](https://github.com/WorldSEnder), [#2186](https://github.com/yewstack/yew/pull/2186)]
- `use_prepared_state` & `use_transitive_state`. [[@Kaede Hoshikawa](https://github.com/Kaede Hoshikawa), [#2650](https://github.com/yewstack/yew/pull/2650)]
- Silence some warnings from derive(Properties). [[@WorldSEnder](https://github.com/WorldSEnder), [#2266](https://github.com/yewstack/yew/pull/2266)]
- onsubmit should be a SubmitEvent. [[@Muhammad Hamza](https://github.com/Muhammad Hamza), [#2816](https://github.com/yewstack/yew/pull/2816)]
- #### ⚡️ Features
- Add VNode::from_html_unchecked. [[@Muhammad Hamza](https://github.com/Muhammad Hamza), [#2842](https://github.com/yewstack/yew/pull/2842)]
- Make Yew lints opt-in. [[@Muhammad Hamza](https://github.com/Muhammad Hamza), [#2882](https://github.com/yewstack/yew/pull/2882)]
- Allow skipping a callback when reforming. [[@Jens Reimann](https://github.com/Jens Reimann), [#2864](https://github.com/yewstack/yew/pull/2864)]
- Polled SSR Stream. [[@Kaede Hoshikawa](https://github.com/Kaede Hoshikawa), [#2824](https://github.com/yewstack/yew/pull/2824)]
- Add send_stream method for Scope. [[@laizy](https://github.com/laizy), [#2619](https://github.com/yewstack/yew/pull/2619)]
- Allow functions returning unit in `use_effect`. [[@Muhammad Hamza](https://github.com/Muhammad Hamza), [#2849](https://github.com/yewstack/yew/pull/2849)]
- Configurable Runtime. [[@Kaede Hoshikawa](https://github.com/Kaede Hoshikawa), [#2772](https://github.com/yewstack/yew/pull/2772)]
- Pinned Channels. [[@Kaede Hoshikawa](https://github.com/Kaede Hoshikawa), [#2811](https://github.com/yewstack/yew/pull/2811)]
- Bind to properties instead of attributes by default. [[@Muhammad Hamza](https://github.com/Muhammad Hamza), [#2819](https://github.com/yewstack/yew/pull/2819)]
- Convert nightly from a feature flag to a compiler flag. [[@Muhammad Hamza](https://github.com/Muhammad Hamza), [#2827](https://github.com/yewstack/yew/pull/2827)]
- Reduce SSR Buffers in VList. [[@Kaede Hoshikawa](https://github.com/Kaede Hoshikawa), [#2826](https://github.com/yewstack/yew/pull/2826)]
- Allow keywords after dash in element and attribute names. [[@Muhammad Hamza](https://github.com/Muhammad Hamza), [#2820](https://github.com/yewstack/yew/pull/2820)]
- Replace custom logging by tracing. [[@WorldSEnder](https://github.com/WorldSEnder), [#2814](https://github.com/yewstack/yew/pull/2814)]
- Implement sleep and interval for Yew Platform. [[@Kaede Hoshikawa](https://github.com/Kaede Hoshikawa), [#2784](https://github.com/yewstack/yew/pull/2784)]
- Remove component NodeRef. [[@Muhammad Hamza](https://github.com/Muhammad Hamza), [#2783](https://github.com/yewstack/yew/pull/2783)]
- Prepared States dependency should be Reference Counted. [[@Kaede Hoshikawa](https://github.com/Kaede Hoshikawa), [#2769](https://github.com/yewstack/yew/pull/2769)]
- Document features automatically.. [[@Kaede Hoshikawa](https://github.com/Kaede Hoshikawa), [#2780](https://github.com/yewstack/yew/pull/2780)]
- Streamed SSR Response. [[@Kaede Hoshikawa](https://github.com/Kaede Hoshikawa), [#2697](https://github.com/yewstack/yew/pull/2697)]
- Nightly features. [[@Muhammad Hamza](https://github.com/Muhammad Hamza), [#2743](https://github.com/yewstack/yew/pull/2743)]
- Allow VNode props to be converted to Children.. [[@Kaede Hoshikawa](https://github.com/Kaede Hoshikawa), [#2749](https://github.com/yewstack/yew/pull/2749)]
- Redo derive(Properties), take 2. [[@WorldSEnder](https://github.com/WorldSEnder), [#2729](https://github.com/yewstack/yew/pull/2729)]
- `Callback::reform()` should return `Callback<T, OUT>`. [[@orzogc](https://github.com/orzogc), [#2719](https://github.com/yewstack/yew/pull/2719)]
- Span hygiene and editor UX. [[@WorldSEnder](https://github.com/WorldSEnder), [#2702](https://github.com/yewstack/yew/pull/2702)]
- Block props update during hydration. [[@Kaede Hoshikawa](https://github.com/Kaede Hoshikawa), [#2665](https://github.com/yewstack/yew/pull/2665)]
- Point to `callback_future` in `callback` docs. [[@Shadlock0133](https://github.com/Shadlock0133), [#2674](https://github.com/yewstack/yew/pull/2674)]
- Change access to VList children to a wrapper. [[@WorldSEnder](https://github.com/WorldSEnder), [#2673](https://github.com/yewstack/yew/pull/2673)]
- Partially undo #2673, different approach for the DerefMut impl of VList. [[@WorldSEnder](https://github.com/WorldSEnder), [#2692](https://github.com/yewstack/yew/pull/2692)]
- Rework a bunch of cfg(feature) flags to be more principled. [[@WorldSEnder](https://github.com/WorldSEnder), [#2666](https://github.com/yewstack/yew/pull/2666)]
- Delay Hydration second render until all assistive nodes have been removed. [[@Kaede Hoshikawa](https://github.com/Kaede Hoshikawa), [#2629](https://github.com/yewstack/yew/pull/2629)]
- Allow to consume deps in use_callback. [[@Jet Li](https://github.com/Jet Li), [#2617](https://github.com/yewstack/yew/pull/2617)]
- Add `use_future` hook to make consuming futures as suspense easier. [[@Muhammad Hamza](https://github.com/Muhammad Hamza), [#2609](https://github.com/yewstack/yew/pull/2609)]
- Add the ability to use non-literal string as attribute names. [[@Muhammad Hamza](https://github.com/Muhammad Hamza), [#2593](https://github.com/yewstack/yew/pull/2593)]
- Introduce a dedicated use_force_update hook. [[@WorldSEnder](https://github.com/WorldSEnder), [#2586](https://github.com/yewstack/yew/pull/2586)]
- Impl ImplicitClone for Rc<T> where T: Sized. [[@Nano](https://github.com/Nano), [#2594](https://github.com/yewstack/yew/pull/2594)]
- SSR Hydration. [[@Kaede Hoshikawa](https://github.com/Kaede Hoshikawa), [#2552](https://github.com/yewstack/yew/pull/2552)]
- Add use_callback hook. [[@Jet Li](https://github.com/Jet Li), [#2566](https://github.com/yewstack/yew/pull/2566)]
- Introduce additional information in SSR artifact to facilitate Hydration. [[@Kaede Hoshikawa](https://github.com/Kaede Hoshikawa), [#2540](https://github.com/yewstack/yew/pull/2540)]
- Scoped event handlers. [[@WorldSEnder](https://github.com/WorldSEnder), [#2510](https://github.com/yewstack/yew/pull/2510)]
- An ever Increasing Component ID. [[@Kaede Hoshikawa](https://github.com/Kaede Hoshikawa), [#2537](https://github.com/yewstack/yew/pull/2537)]
- Prevents Fallback UI from becoming suspended. [[@Kaede Hoshikawa](https://github.com/Kaede Hoshikawa), [#2532](https://github.com/yewstack/yew/pull/2532)]
- `#[cfg(feature = "render")]` and `yew::Renderer`. [[@Kaede Hoshikawa](https://github.com/Kaede Hoshikawa), [#2498](https://github.com/yewstack/yew/pull/2498)]
- Introduce explicit internal datastructures modeling dom state. [[@WorldSEnder](https://github.com/WorldSEnder), [#2330](https://github.com/yewstack/yew/pull/2330)]
- Improve AnyScope API. [[@Aaron Erhardt](https://github.com/Aaron Erhardt), [#2445](https://github.com/yewstack/yew/pull/2445)]
- Automatic Message Batching. [[@Kaede Hoshikawa](https://github.com/Kaede Hoshikawa), [#2421](https://github.com/yewstack/yew/pull/2421)]
- Add Other variant to the ListenerKind. [[@Alexander Mescheryakov](https://github.com/Alexander Mescheryakov), [#2417](https://github.com/yewstack/yew/pull/2417)]
- Function Components & Hooks V2. [[@Kaede Hoshikawa](https://github.com/Kaede Hoshikawa), [#2401](https://github.com/yewstack/yew/pull/2401)]
- Add ContextHandle in yew::prelude. [[@Anuvrat Singh](https://github.com/Anuvrat Singh), [#2372](https://github.com/yewstack/yew/pull/2372)]
- Separate scheduler rendered call from create and render. [[@Kaede Hoshikawa](https://github.com/Kaede Hoshikawa), [#2374](https://github.com/yewstack/yew/pull/2374)]
- Update to edition 2021. [[@Muhammad Hamza](https://github.com/Muhammad Hamza), [#2354](https://github.com/yewstack/yew/pull/2354)]
- Server-side Rendering (without hydration). [[@Kaede Hoshikawa](https://github.com/Kaede Hoshikawa), [#2335](https://github.com/yewstack/yew/pull/2335)]
- Make BaseComponent Sealed.. [[@Kaede Hoshikawa](https://github.com/Kaede Hoshikawa), [#2359](https://github.com/yewstack/yew/pull/2359)]
- Remove start_app_as_body.. [[@Kaede Hoshikawa](https://github.com/Kaede Hoshikawa), [#2346](https://github.com/yewstack/yew/pull/2346)]
- Bump minimal supported rust version (MSRV) to 1.56. [[@Muhammad Hamza](https://github.com/Muhammad Hamza), [#2334](https://github.com/yewstack/yew/pull/2334)]
- Suspense Support. [[@Kaede Hoshikawa](https://github.com/Kaede Hoshikawa), [#2212](https://github.com/yewstack/yew/pull/2212)]
- make layout testing code public. [[@Muhammad Hamza](https://github.com/Muhammad Hamza), [#2310](https://github.com/yewstack/yew/pull/2310)]
- Refactor and simplify `Callback`. [[@Muhammad Hamza](https://github.com/Muhammad Hamza), [#2301](https://github.com/yewstack/yew/pull/2301)]
- Add pending event listener on the VTag. [[@Alexander Mescheryakov](https://github.com/Alexander Mescheryakov), [#2300](https://github.com/yewstack/yew/pull/2300)]
- constify VList::new. [[@Alexander Mescheryakov](https://github.com/Alexander Mescheryakov), [#2293](https://github.com/yewstack/yew/pull/2293)]
- Allow `function_component` creation based on function name. [[@Muhammad Hamza](https://github.com/Muhammad Hamza), [#2292](https://github.com/yewstack/yew/pull/2292)]
- Implement IntoPropValue for Rc<str>. [[@Zachary Stewart](https://github.com/Zachary Stewart), [#2285](https://github.com/yewstack/yew/pull/2285)]
- Raw field names in property structs. [[@WorldSEnder](https://github.com/WorldSEnder), [#2273](https://github.com/yewstack/yew/pull/2273)]
## ✨ yew-router **0.17.0** *(2022-11-xx)*
#### Changelog
- #### 🛠 Fixes
- Fix basename handling in router. [[@Muhammad Hamza](https://github.com/Muhammad Hamza), [#2297](https://github.com/yewstack/yew/pull/2297)]
- #### ⚡️ Features
- Simple `NodeRef` passing to `<Link>` for yew-router. [[@Athan Clark](https://github.com/Athan Clark), [#2877](https://github.com/yewstack/yew/pull/2877)]
- Make Switch to accept a closure as render function directly. [[@Kaede Hoshikawa](https://github.com/Kaede Hoshikawa), [#2659](https://github.com/yewstack/yew/pull/2659)]
- `#[cfg(feature = "render")]` and `yew::Renderer`. [[@Kaede Hoshikawa](https://github.com/Kaede Hoshikawa), [#2498](https://github.com/yewstack/yew/pull/2498)]
- Includes query parameters in rendered Link component. [[@Yuki Kodama](https://github.com/Yuki Kodama), [#2464](https://github.com/yewstack/yew/pull/2464)]
- Update to edition 2021. [[@Muhammad Hamza](https://github.com/Muhammad Hamza), [#2354](https://github.com/yewstack/yew/pull/2354)]
- Support named wildcards when deriving Routable.. [[@Jonathan Bailey](https://github.com/Jonathan Bailey), [#2345](https://github.com/yewstack/yew/pull/2345)]
- Add HashRouter, basename and use gloo-history. [[@Kaede Hoshikawa](https://github.com/Kaede Hoshikawa), [#2239](https://github.com/yewstack/yew/pull/2239)]
## ✨ yew-agent **0.2.0** *(2022-11-xx)*
#### Changelog
- #### ⚡️ Features
- add `use_bridge` docs. [[@Shrey Sudhir](https://github.com/Shrey Sudhir), [#2722](https://github.com/yewstack/yew/pull/2722)]
- Update to edition 2021. [[@Muhammad Hamza](https://github.com/Muhammad Hamza), [#2354](https://github.com/yewstack/yew/pull/2354)]
- Move yew-agent to gloo. [[@Muhammad Hamza](https://github.com/Muhammad Hamza), [#2326](https://github.com/yewstack/yew/pull/2326)]
- Implement PrivateAgent. [[@Kaede Hoshikawa](https://github.com/Kaede Hoshikawa), [#2318](https://github.com/yewstack/yew/pull/2318)]
- Remove context & job agent. [[@Kaede Hoshikawa](https://github.com/Kaede Hoshikawa), [#2295](https://github.com/yewstack/yew/pull/2295)]
## ✨ yew **0.19.0** *(2021-11-26)* ## ✨ yew **0.19.0** *(2021-11-26)*
#### Changelog #### Changelog

View File

@ -7,12 +7,12 @@ authors = [
"Justin Starry <justin@yew.rs>", "Justin Starry <justin@yew.rs>",
] ]
repository = "https://github.com/yewstack/yew" repository = "https://github.com/yewstack/yew"
homepage = "https://github.com/yewstack/yew" homepage = "https://yew.rs"
documentation = "https://docs.rs/yew/" documentation = "https://docs.rs/yew/"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
keywords = ["web", "webasm", "javascript"] keywords = ["web", "webasm", "javascript"]
categories = ["gui", "wasm", "web-programming"] categories = ["gui", "wasm", "web-programming"]
description = "A framework for making client-side single-page apps" description = "A framework for creating reliable and efficient web applications"
readme = "../../README.md" readme = "../../README.md"
rust-version = "1.60.0" rust-version = "1.60.0"

View File

@ -74,11 +74,19 @@ pub fn create_log_line(
.with_context(|| format!("Could not find GitHub labels for issue: {}", issue_id))?; .with_context(|| format!("Could not find GitHub labels for issue: {}", issue_id))?;
let is_issue_for_this_package = issue_labels let is_issue_for_this_package = issue_labels
.into_iter() .iter()
.any(|label| package_labels.contains(&label.as_str())); .any(|label| package_labels.contains(&label.as_str()));
if !is_issue_for_this_package { if !is_issue_for_this_package {
println!("Issue {issue_id} is not for {package_labels:?} packages"); println!("Issue {issue_id} is not for {package_labels:?} packages");
let leftovers = issue_labels.iter().filter(|label| {
!(label.starts_with("A-") || *label == "documentation" || *label == "meta")
});
let count = leftovers.count();
if count > 0 {
println!("Potentially invalidly labeled issue: {issue_id}. Neither A-* (area), documentation nor meta labels found. \
inspect/re-tag at https://github.com/yewstack/yew/issues/{issue_id}");
}
return Ok(None); return Ok(None);
} }

View File

@ -0,0 +1,33 @@
---
title: Releasing Yew 0.20
authors: [hamza] tags: [release]
---
The Yew team is happy to announce a new, long overdue, version of Yew: v0.20.
Yew is a framework for creating reliable and efficient web applications.
<!--truncate-->
## What's new
This release comes with new features aimed at improving the user experience, such as server-rendering and render-as-you-fetch approach for data-fetching.
### SSR
Yew now fully supports rendering on the server. Rendering on the server means users will get a rendered HTML and will not have to wait to be able to see anything until the entire WebAssembly bundle is downloaded and initial render has completed. With SSR, the page will be visible instantly, and interactable as soon as hydration finishes.
Learn more at [Server-side rendering](/docs/advanced-topics/server-side-rendering)
### Data fetching
With SSR comes new ways of data-fetching. The newly added [`use_prepared_state!`](https://api.yew.rs/next/yew/functional/macro.use_prepared_state.html) hook can be used to fetch data while rendering on the and seemlessly use it in the component.
For client-side fetching, Yew now supports render-as-you-fetch approach with [Suspense](/docs/concepts/suspense).
## How to upgrade
There have been breaking changes in this release. Our [migration guides](/docs/migration-guides/yew/from-0_19_0-to-0_20_0) go over how to upgrade each over of the new crates.
## Thanks!
Many people came together to help make this release happen. We couldn't have done it without all of you. Thanks!

View File

@ -0,0 +1,86 @@
{
"version.label": {
"message": "0.20",
"description": "The label for version 0.20"
},
"sidebar.docs.category.Getting Started": {
"message": "Getting Started",
"description": "The label for category Getting Started in sidebar docs"
},
"sidebar.docs.category.Concepts": {
"message": "Concepts",
"description": "The label for category Concepts in sidebar docs"
},
"sidebar.docs.category.Concepts.link.generated-index.title": {
"message": "Yew concepts",
"description": "The generated-index page title for category Concepts in sidebar docs"
},
"sidebar.docs.category.Concepts.link.generated-index.description": {
"message": "Learn about the important Yew concepts!",
"description": "The generated-index page description for category Concepts in sidebar docs"
},
"sidebar.docs.category.Using Basic Web Technologies In Yew": {
"message": "Using Basic Web Technologies In Yew",
"description": "The label for category Using Basic Web Technologies In Yew in sidebar docs"
},
"sidebar.docs.category.Using Basic Web Technologies In Yew.link.generated-index.title": {
"message": "Yew's Take on Basic Web Technologies",
"description": "The generated-index page title for category Using Basic Web Technologies In Yew in sidebar docs"
},
"sidebar.docs.category.Using Basic Web Technologies In Yew.link.generated-index.description": {
"message": "Yew centrally operates on the idea of keeping everything that a reusable piece of UI may needin one place - rust files, while also keeping the underlying technology accessible where necessary. Explore further to fully grasp what we mean by these statements:",
"description": "The generated-index page description for category Using Basic Web Technologies In Yew in sidebar docs"
},
"sidebar.docs.category.Components": {
"message": "Components",
"description": "The label for category Components in sidebar docs"
},
"sidebar.docs.category.Hooks": {
"message": "Hooks",
"description": "The label for category Hooks in sidebar docs"
},
"sidebar.docs.category.HTML": {
"message": "HTML",
"description": "The label for category HTML in sidebar docs"
},
"sidebar.docs.category.Advanced topics": {
"message": "Advanced topics",
"description": "The label for category Advanced topics in sidebar docs"
},
"sidebar.docs.category.Advanced topics.link.generated-index.title": {
"message": "Advanced topics",
"description": "The generated-index page title for category Advanced topics in sidebar docs"
},
"sidebar.docs.category.Advanced topics.link.generated-index.description": {
"message": "Learn about the advanced topics and inner workings of Yew!",
"description": "The generated-index page description for category Advanced topics in sidebar docs"
},
"sidebar.docs.category.Struct Components": {
"message": "Struct Components",
"description": "The label for category Struct Components in sidebar docs"
},
"sidebar.docs.category.More": {
"message": "More",
"description": "The label for category More in sidebar docs"
},
"sidebar.docs.category.More.link.generated-index.title": {
"message": "Miscellaneous",
"description": "The generated-index page title for category More in sidebar docs"
},
"sidebar.docs.category.Migration guides": {
"message": "Migration guides",
"description": "The label for category Migration guides in sidebar docs"
},
"sidebar.docs.category.yew": {
"message": "yew",
"description": "The label for category yew in sidebar docs"
},
"sidebar.docs.category.yew-agent": {
"message": "yew-agent",
"description": "The label for category yew-agent in sidebar docs"
},
"sidebar.docs.category.yew-router": {
"message": "yew-router",
"description": "The label for category yew-router in sidebar docs"
}
}

View File

@ -0,0 +1,8 @@
---
title: How it works
description: Low level details about the framework
---
# 低レベルなライブラリの中身
コンポーネントのライフサイクルの状態機械、VDOM の異なるアルゴリズム

View File

@ -0,0 +1,171 @@
---
title: Optimizations
description: Make your app faster
---
# 最適化とベストプラクティス
## neq_assign
親コンポーネントから props を受け取った際、`change`メソッドが呼ばれます。
これはコンポーネントの状態を更新することができるのに加え、コンポーネントが props が変わった際に再レンダリングするかどうかを決める
`ShouldRender`という真偽値を返すことができます。
再レンダリングはコストがかかるもので、もし避けられるのであれば避けるべきです。
一般的なルールとして props が実際に変化した際にのみ再レンダリングすれば良いでしょう。
以下のコードブロックはこのルールを表しており、props が前と変わったときに`true`を返します。
```rust
use yew::ShouldRender;
#[derive(PartialEq)]
struct ExampleProps;
struct Example {
props: ExampleProps,
};
impl Example {
fn change(&mut self, props: ExampleProps) -> ShouldRender {
if self.props != props {
self.props = props;
true
} else {
false
}
}
}
```
しかし我々は先に進んでいけます!
この 6 行のボイラープレードは`PartialEq`を実装したものにトレイトとブランケットを用いることで 1 行のコードへと落とし込むことができます。
[こちら](https://docs.rs/yewtil/*/yewtil/trait.NeqAssign.html)にて`yewtil`クレートの`NewAssign`トレイトを見てみてください。
## 効果的にスマートポインタを使う
**注意: このセクションで使われている用語がわからなければ Rust book は
[スマートポインタについての章](https://doc.rust-lang.org/book/ch15-00-smart-pointers.html)
があり、非常に有用です。**
再レンダリングの際に props を作るデータを大量にコピーしないために、スマートポインタを用いてデータ自体ではなくデータへの参照だけを
コピーできます。
props や子コンポーネントで関連するデータに実データではなく参照を渡すと、子コンポーネントでデータを変更する必要がなければ
データのコピーを避けることができます。
その際、`Rc::make_mut`によって変更したいデータの変更可能な参照を得ることができます。
これにより、props が変更されたときにコンポーネントが再レンダリングされるかどうかを決めるかで`Component::change`に更なる恩恵があります。
なぜなら、データの値を比較する代わりに元々のポインタのアドレス (つまりデータが保管されている機械のメモリの場所) を比較できるためです。
2 つのポインターが同じデータを指す場合、それらのデータの値は同じでなければならないのです。
ただし、その逆は必ずしも成り立たないことに注意してください!
もし 2 つのポインタが異なるのであれば、そのデータは同じである可能性があります。
この場合はデータを比較するべきでしょう。
この比較においては`PartialEq`ではなく`Rc::ptr_eq`を使う必要があります。
`PartialEq`は等価演算子`==`を使う際に自動的に使われます。
Rust のドキュメントには[`Rc::ptr_eq`についてより詳しく書いてあります](https://doc.rust-lang.org/stable/std/rc/struct.Rc.html#method.ptr_eq)。
この最適化は`Copy`を実装していないデータの型に対して極めて有効なものです。
もしデータを簡単に変更できるのであれば、スマートポインタに取り換える必要はありません。
しかし`Vec`や`HashMap`、`String`などのような重たいデータの構造体に対してはスマートポインタを使うことで
パフォーマンスを改善することができるでしょう。
この最適化は値がまだ一度も子によって更新されていない場合に極めて有効で、親からほとんど更新されない場合においてもかなり有効です。
これにより、`Rc<_>s`が純粋なコンポーネントに対してプロパティの値をラップする良い一手となります。
## View 関数
コードの可読性の理由から`html!`の部分を関数へと移植するのは意味があります。
これは、インデントを減らすのでコードを読みやすくするだけでなく、良いデザインパターンを産むことにも繋がるのです。
これらの関数は複数箇所で呼ばれて書くべきコード量を減らせるため、分解可能なアプリケーションを作ることができるのです。
## 純粋なコンポーネント
純粋なコンポーネントは状態を変化せず、ただ中身を表示してメッセージを普通の変更可能なコンポーネントへ渡すコンポーネントのことです。
View 関数との違いとして、純粋なコンポーネントは式の構文\(`{some_view_function()}`\)ではなく
コンポーネントの構文\(`<SomePureComponent />`\)を使うことで`html!`マクロの中で呼ばれる点、
そして実装次第で記憶され (つまり、一度関数が呼ばれれば値は"保存"され、
同じ引数でもう一度呼ばれても値を再計算する必要がなく最初に関数が呼ばれたときの保存された値を返すことができる)、
先述の`neq_assign`ロジックを使う別々の props で再レンダリングを避けられる点があります。
Yew は純粋な関数やコンポーネントをサポートしていませんが、外部のクレートを用いることで実現できます。
## 関数型コンポーネント (a.k.a フック)
関数型コンポーネントはまだ開発中です!
開発状況については[プロジェクトボード](https://github.com/yewstack/yew/projects/3)に詳しく書いてあります。
## キー付き DOM ノード
## ワークスペースでコンパイル時間を減らす
間違いなく Yew を使う上での最大の欠点はコンパイルに時間がかかる点です。
プロジェクトのコンパイルにかかる時間は`html!`マクロに渡されるコードの量に関係しています。
これは小さなプロジェクトにはそこまで問題ないようですが、大きなアプリではコードを複数クレートに分割することでアプリに変更が加られた際に
コンパイラの作業量を減らすのが有効です。
一つ可能なやり方として、ルーティングとページ洗濯を担当するメインのクレートを作り、それぞれのページに対して別のクレートを作ることです。
そうして各ページは異なるコンポーネントか、`Html`を生成する大きな関数となります。
アプリの異なる部分を含むクレート同士で共有されるコードはプロジェクト全体で依存する分離したクレートに保存されます。
理想的には 1 回のコンパイルでコード全てを再ビルドせずメインのクレートかどれかのページのクレートを再ビルドするだけにすることです。
最悪なのは、"共通"のクレートを編集して、はじめに戻ってくることです:
共有のクレートに依存している全てのコード、恐らく全てのコードをコンパイルすることです。
もしメインのクレートが重たすぎる、もしくは深くネストしたページ (例えば別ページのトップでレンダリングされるページ)
で速く繰り返したい場合、クレートの例を用いてメインページの実装をシンプルにしたりトップで動かしているコンポーネントをレンダリングできます。
## バイナリサイズを小さくする
- Rust のコードを最適化する
- `cargo.toml` \( release profile を定義 \)
- `wasm-opt`を用いて wasm のコードを最適化する
**注意: バイナリサイズを小さくするのについては[Rust Wasm Book](https://rustwasm.github.io/book/reference/code-size.html#optimizing-builds-for-code-size)に詳しく書いてあります。**
### Cargo.toml
`Cargo.toml`で`[profile.release]`のセクションに設定を書き込むことでリリースビルドを小さくすることが可能です。
```text
[profile.release]
# バイナリに含むコードを少なくする
panic = 'abort'
# コードベース全体での最適化 ( 良い最適化だがビルドが遅くなる)
codegen-units = 1
# サイズの最適化( よりアグレッシブに )
opt-level = 'z'
# サイズの最適化
# opt-level = 's'
# プログラム全体の分析によるリンク時最適化
lto = true
```
### wasm-opt
更に`wasm`のコードのサイズを最適化することができます。
The Rust Wasm Book には Wasm バイナリのサイズを小さくすることについてのセクションがあります:
[Shrinking .wasm size](https://rustwasm.github.io/book/game-of-life/code-size.html)
- `wasm-pack`でデフォルトの`wasm`のコードをリリースビルド時に最適化する
- `wasm-opt`によって直接`wasm`ファイルを最適化する
```text
wasm-opt wasm_bg.wasm -Os -o wasm_bg_opt.wasm
```
#### yew/examples/にある例を小さなサイズでビルドする
注意: `wasm-pack`は Rust と Wasm のコードへの最適化を組み合わせます。`wasm-bindgen`はこの例では Rust のサイズ最適化を用いていません。
| 使用したツール | サイズ |
| :-------------------------- | :----- |
| wasm-bindgen | 158KB |
| wasm-bindgen + wasm-opt -Os | 116KB |
| wasm-pack | 99 KB |
## 参考文献:
- [The Rust Book のスマートポインタに関する章](https://doc.rust-lang.org/book/ch15-00-smart-pointers.html)
- [the Rust Wasm Book でのバイナリサイズを小さくすることについて](https://rustwasm.github.io/book/reference/code-size.html#optimizing-builds-for-code-size)
- [Rust profiles についてのドキュメント](https://doc.rust-lang.org/cargo/reference/profiles.html)
- [binaryen プロジェクト](https://github.com/WebAssembly/binaryen)

View File

@ -0,0 +1,35 @@
---
title: Callbacks
description: ComponentLink and Callbacks
---
”リンク”コンポーネントはコンポーネントがコールバックを登録できて自身を更新することができるメカニズムです。
## ComponentLink API
### callback
実行時にコンポーネントの更新メカニズムにメッセージを送信するコールバックを登録します。
これは、渡されたクロージャから返されるメッセージで `send_self` を呼び出します。
`Fn(IN) -> Vec<COMP::Message>`が渡され、`Callback<IN>`が返されます。
### send_message
現在のループが終了した直後にコンポーネントにメッセージを送信し、別の更新ループを開始します。
### send_message_batch
実行時に一度に多数のメッセージを一括して送信するコールバックを登録します。
メッセージによってコンポーネントが再レンダリングされる場合、バッチ内のすべてのメッセージが処理された後、コンポーネントは再レンダリングされます。
`Fn(IN) -> COMP::Message`が渡され、`Callback<IN>`が返されます。
## コールバック
_\(This might need its own short page.\)_
コールバックは、Yew 内のサービス、エージェント、親コンポーネントとの通信に使われます。
これらは単に `Fn` を `Rc` でラップしただけであり、クローンを作成できるようにするためのものです。
これらの関数には `emit` 関数があり、`<IN>` 型を引数に取り、それをアドレスが欲しいメッセージに変換します。
親からのコールバックが子コンポーネントに props で提供されている場合、子は `update` ライフサイクルフックで `emit` をコールバックに呼び出して親にメッセージを返すことができます。
マクロ内で props として提供されたクロージャや関数は自動的にコールバックに変換されます。

View File

@ -0,0 +1,198 @@
---
title: Introduction
description: Components and their lifecycle hooks
---
## コンポーネントとは?
コンポーネントは Yew を構成するブロックです。
コンポーネントは状態を管理し、自身を DOM へレンダリングすることができます。
コンポーネントはライフサイクルの機能がある`Component`トレイトを実装することによって作られます。
## ライフサイクル
:::important contribute
`Contribute to our docs:` [Add a diagram of the component lifecycle](https://github.com/yewstack/docs/issues/22)
:::
## ライフサイクルのメソッド
### Create
コンポーネントが作られると、`ComponentLink`と同様に親コンポーネントからプロパティを受け取ります。
プロパティはコンポーネントの状態を初期化するのに使われ、"link"はコールバックを登録したりコンポーネントにメッセージを送るのに使われます。
props と link をコンポーネント構造体に格納するのが一般的です。
例えば:
```rust
pub struct MyComponent {
props: Props,
link: ComponentLink<Self>,
}
impl Component for MyComponent {
type Properties = Props;
// ...
fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
MyComponent { props, link }
}
// ...
}
```
### View
コンポーネントは`view()`メソッドによってレイアウトを宣言します。
Yew は`html!`マクロによって HTML と SVG ノード、リスナー、子コンポーネントを宣言できます。
マクロは React の JSX のような動きをしますが、JavaScript の代わりに Rust の式を用います。
```rust
impl Component for MyComponent {
// ...
fn view(&self) -> Html {
let onclick = self.link.callback(|_| Msg::Click);
html! {
<button {onclick}>{ self.props.button_text }</button>
}
}
}
```
使い方については[`html!`ガイド](concepts/html/introduction.mdx)をご確認ください。
### Rendered
`rendered()`コンポーネントのライフサイクルのメソッドは`view()`が処理されたて Yew がコンポーネントをレンダリングした後、
ブラウザがページを更新する前に呼ばれます。
コンポーネントは、コンポーネントが要素をレンダリングした後にのみ実行できるアクションを実行するため、このメソッドを実装したい場合があります。
コンポーネントが初めてレンダリングされたかどうかは `first_render` パラメータで確認できます。
```rust
use stdweb::web::html_element::InputElement;
use stdweb::web::IHtmlElement;
use yew::prelude::*;
pub struct MyComponent {
node_ref: NodeRef,
}
impl Component for MyComponent {
// ...
fn view(&self) -> Html {
html! {
<input ref={self.node_ref.clone()} type="text" />
}
}
fn rendered(&mut self, first_render: bool) {
if first_render {
if let Some(input) = self.node_ref.try_into::<InputElement>() {
input.focus();
}
}
}
}
```
:::tip note
ライフサイクルメソッドは実装の必要がなく、デフォルトでは何もしません。
:::
### Update
コンポーネントは動的で、非同期メッセージを受信するために登録することができます。
ライフサイクルメソッド `update()` はメッセージごとに呼び出されます。
これにより、コンポーネントはメッセージが何であったかに基づいて自身を更新し、自身を再レンダリングする必要があるかどうかを判断することができます。
メッセージは、HTML 要素リスナーによってトリガーされたり、子コンポーネント、エージェント、サービス、または Futures によって送信されたりします。
`update()`がどのようなのかについての例は以下の通りです:
```rust
pub enum Msg {
SetInputEnabled(bool)
}
impl Component for MyComponent {
type Message = Msg;
// ...
fn update(&mut self, msg: Self::Message) -> ShouldRender {
match msg {
Msg::SetInputEnabled(enabled) => {
if self.input_enabled != enabled {
self.input_enabled = enabled;
true // Re-render
} else {
false
}
}
}
}
}
```
### Change
コンポーネントは親によって再レンダリングされることがあります。
このような場合、新しいプロパティを受け取り、再レンダリングを選択する可能性があります。
この設計では、プロパティを変更することで、親から子へのコンポーネントの通信が容易になります。
典型的な実装例は以下の通りです:
```rust
impl Component for MyComponent {
// ...
fn change(&mut self, props: Self::Properties) -> ShouldRender {
if self.props != props {
self.props = props;
true
} else {
false
}
}
}
```
### Destroy
コンポーネントが DOM からアンマウントされた後、Yew は `destroy()` ライフサイクルメソッドを呼び出し、必要なクリーンアップ操作をサポートします。
このメソッドはオプションで、デフォルトでは何もしません。
## Associated Types
`Component`トレイトは 2 つの関連型があります: `Message`と`Properties`です。
```rust
impl Component for MyComponent {
type Message = Msg;
type Properties = Props;
// ...
}
```
`Message`はコンポーネントによって処理され、何らかの副作用を引き起こすことができるさまざまなメッセージを表します。
例えば、API リクエストをトリガーしたり、UI コンポーネントの外観を切り替えたりする `Click` メッセージがあります。
コンポーネントのモジュールで `Msg` という名前の列挙型を作成し、それをコンポーネントのメッセージ型として使用するのが一般的です。
"message"を"msg"と省略するのも一般的です。
```rust
enum Msg {
Click,
}
```
`Properties`は、親からコンポーネントに渡される情報を表します。
この型は Properties trait を実装していなければならず\(通常はこれを派生させることで\)、特定のプロパティが必須かオプションかを指定することができます。
この型は、コンポーネントの作成・更新時に使用されます。
コンポーネントのモジュール内に `Props` という構造体を作成し、それをコンポーネントの `Properties` 型として使用するのが一般的です。
”Properties”を"props"に短縮するのが一般的です。
Props は親コンポーネントから継承されるので、アプリケーションのルートコンポーネントは通常`()`型の`Properties`を持ちます。
ルートコンポーネントのプロパティを指定したい場合は、`App::mount_with_props`メソッドを利用します。

View File

@ -0,0 +1,84 @@
---
title: Properties
description: Parent to child communication
---
プロパティは、子コンポーネントと親コンポーネントが互いに通信できるようにします。
## マクロの継承
`Properties`を自分で実装しようとせず、代わりに`#[derive(Properties)]`を使ってください。
:::note
`Properties`を継承した型は`Clone`を実装していなければいけません。
これは`#[derive(Properties, Clone)`か`Clone`を手で実装することで可能です。
:::
### 必要な属性
デフォルトでは、`Properties` を導出する構造体内のフィールドは必須です。
フィールドが欠落していて `html!` マクロでコンポーネントが作成された場合、コンパイラエラーが返されます。
オプションのプロパティを持つフィールドについては、`#[prop_or_default]` 属性を使用して、prop が指定されていない場合はその型のデフォルト値を使用します。
値を指定するには `#[prop_or(value)]` 属性を用います。
ここで value はプロパティのデフォルト値、あるいは代わりに `#[prop_or_else(function)]` を使用して、`function` はデフォルト値を返します。
例えば、ブール値のデフォルトを `true` とするには、属性 `#[prop_or(true)]` を使用します。オプションのプロパティでは、デフォルト値 `None` を持つ `Option` 列挙型を使うのが一般的です。
### PartialEq
もし可能なら props で `PartialEq` を継承するのが良いかもしれません。
`PartialEq`を使うことで、不必要な再レンダリングを避けることができます
(これについては、**最適化とベストプラクティス**のセクションで説明しています)。
## プロパティを使用する際のメモリと速度のオーバーヘッド
`Compoenent::view`ではコンポーネントの状態への参照を取り、それを使って `Html` を作成します。
しかし、プロパティは自身の値です。
つまり、それらを作成して子コンポーネントに渡すためには、`view` 関数で提供される参照を所有する必要があるのです。
これは所有する値を取得するためにコンポーネントに渡される参照を暗黙のうちにクローンすることで行われます。
これは、各コンポーネントが親から受け継いだ状態の独自のコピーを持っていることを意味し、コンポーネントを再レンダリングするときはいつでも、再レンダリングしたコンポーネントのすべての子コンポーネントの props がクローンされなければならないことを意味します。
このことの意味するところは、もしそうでなければ*大量の*データ \(10KB もあるような文字列\) を props として渡してしまうのであれば、子コンポーネントを親が呼び出す `Html` を返す関数にすることを考えた方がいいかもしれないということです。
props を介して渡されたデータを変更する必要がない場合は、実際のデータそのものではなく、データへの参照カウントされたポインタのみが複製されるように `Rc` でラップすることができます。
## 例
```rust
use std::rc::Rc;
use yew::Properties;
#[derive(Clone, PartialEq)]
pub enum LinkColor {
Blue,
Red,
Green,
Black,
Purple,
}
impl Default for LinkColor {
fn default() -> Self {
// The link color will be blue unless otherwise specified.
LinkColor::Blue
}
}
#[derive(Properties, Clone, PartialEq)]
pub struct LinkProps {
/// The link must have a target.
href: String,
/// If the link text is huge, this will make copying the string much cheaper.
/// This isn't usually recommended unless performance is known to be a problem.
text: Rc<String>,
/// Color of the link.
#[prop_or_default]
color: LinkColor,
/// The view function will not specify a size if this is None.
#[prop_or_default]
size: Option<u32>,
/// When the view function doesn't specify active, it defaults to true.
#[prop_or(true)]
active: bool,
}
```

View File

@ -0,0 +1,24 @@
---
title: Refs
description: Out-of-band DOM access
---
`ref`は、任意の HTML 要素やコンポーネントの内部で、割り当てられている DOM`Element`を取得するために使用することができます。
これは、`view` ライフサイクルメソッドの外で DOM に変更を加えるために使用できます。
これは、キャンバスの要素を取得したり、ページの異なるセクションにスクロールしたりするのに便利です。
構文は以下の通りです:
```rust
// 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();
```

View File

@ -0,0 +1,56 @@
---
title: Agents
description: Yew's Actor System
---
エージェントは Angular の[サービス](https://angular.io/guide/architecture-services)に似ており\(ただし依存性インジェクションはありません\)、
[アクターモデル](https://en.wikipedia.org/wiki/Actor_model)を提供します。
エージェントはコンポーネント階層のどこに位置するかに関わらず、コンポーネント間でメッセージをルーティングしたり、共有状態を作成したり、UI をレンダリングするメインスレッドから計算量の多いタスクをオフロードするために使用することができます。
また、Yew アプリケーションがタブをまたいで通信できるようにするためのエージェントのサポートも\(将来的には\)計画されています。
エージェントが並行に動くように Yew は[web-workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers)を使用しています。
## ライフサイクル
![エージェントのライフサイクル](https://user-images.githubusercontent.com/42674621/79125224-b6481d80-7d95-11ea-8e6a-ab9b52d1d8ac.png)
## エージェントの種類
### Reaches
- Context - Context エージェントのインスタンスは、常に最大 1 つ存在します。
Bridges は、UI スレッド上で既にスポーンされたエージェントをスポーンするか、接続します。
これは、コンポーネントまたは他のエージェント間の状態を調整するために使用することができます。
このエージェントに Bridges が接続されていない場合、このエージェントは消滅します。
- Job - 新しいブリッジごとに UI スレッド上で新しいエージェントをスポーンします。
これは、ブラウザと通信する共有されているが独立した動作をコンポーネントの外に移動させるのに適しています。
(TODO 確認) タスクが完了すると、エージェントは消えます。
- Public - Context と同じですが、独自の web worker で動作します。
- Private - Job と同じですが、独自の web worker で動作します。
- Global \(WIP\)
## エージェントとコンポーネントのやり取り
### Bridges
Bridge は、エージェントとコンポーネント間の双方向通信を可能にします。
また、Bridge はエージェント同士の通信を可能にします。
### Dispatchers
Dispatcher は、コンポーネントとエージェント間の一方向通信を可能にします。
Dispatcher は、コンポーネントがエージェントにメッセージを送信することを可能にします。
## オーバーヘッド
独自の独立した web worker(プライベートとパブリック)にあるエージェントは、送受信するメッセージにシリアライズするオーバーヘッドが発生します。
他のスレッドとの通信には[bincode](https://github.com/servo/bincode)を使用するので、関数を呼び出すよりもコストはかなり高くなります。
計算コストがメッセージの受け渡しコストを上回る場合を除き、ロジックを UI スレッドエージェント\(Job または Context\)に格納する必要があります。
## 参考資料
- [web_worker_fib](https://github.com/yewstack/yew/tree/master/examples/web_worker_fib)の例でコンポーネントがどのようにエージェントと通信させているかがわかります。

View File

@ -0,0 +1,108 @@
---
title: Components
description: Create complex layouts with component hierarchies
---
## 基本
`Component`を実装しているあらゆる型は`html!`マクロの中で使えます:
```rust
html!{
<>
// No properties
<MyComponent />
// With Properties
<MyComponent prop1="lorem" prop2="ipsum" />
// With the whole set of props provided at once
<MyComponent ..props />
// With Properties from a variable and specific values overridden
<MyComponent prop2="lorem" ..props />
</>
}
```
## ネスト
`children`フィールドが`Properties`の中にある場合はコンポーネントは子に渡されます。
```rust title="parent.rs"
html! {
<Container>
<h4>{ "Hi" }</h4>
<div>{ "Hello" }</div>
</Container>
}
```
```rust title="container.rs"
pub struct Container(Props);
#[derive(Properties, Clone)]
pub struct Props {
pub children: Children,
}
impl Component for Container {
type Properties = Props;
// ...
fn view(&self) -> Html {
html! {
<div id="container">
{ self.0.children.clone() }
</div>
}
}
}
```
:::note
`Properties`を継承した型は`Clone`を実装していなければいけません。
これは`#[derive(Properties, Clone)]`を使うか手で`Clone`を実装すれば良いです。
:::
## Props とネストした子コンポーネント
ネストしたコンポーネントのプロパティは格納しているコンポーネントの型が子である場合はアクセス可能、または変更可能です。
以下の例では`List`コンポーネントは`ListItem`コンポーネントをラップできています。
実際の使用においてこのパターンの例については`yew-router`のソースコードを確認してみてください。
より進んだ例としては Yew のメインのリポジトリにある`nested-list`を確認してみてください。
```rust title="parent.rs"
html! {
<List>
<ListItem value="a" />
<ListItem value="b" />
<ListItem value="c" />
</List>
}
```
```rust title="list.rs"
pub struct List(Props);
#[derive(Properties, Clone)]
pub struct Props {
pub children: ChildrenWithProps<ListItem>,
}
impl Component for List {
type Properties = Props;
// ...
fn view(&self) -> Html {
html!{{
for self.0.children.iter().map(|mut item| {
item.props.value = format!("item-{}", item.props.value);
item
})
}}
}
}
```

View File

@ -0,0 +1,373 @@
---
title: Elements
description: Both HTML and SVG elements are supported
---
## タグ構造
要素のタグは`<... />`のような自己完結タグか、開始タグに対応した終了タグを持っている必要があります。
<!--DOCUSAURUS_CODE_TABS-->
<!--Open - Close-->
```rust
html! {
<div id="my_div"></div>
}
```
<!--Invalid-->
```rust
html! {
<div id="my_div"> // <- MISSING CLOSE TAG
}
```
<!--Self-closing-->
```rust
html! {
<input id="my_input" />
}
```
<!--Invalid-->
```rust
html! {
<input id="my_input"> // <- MISSING SELF-CLOSE
}
```
<!--END_DOCUSAURUS_CODE_TABS-->
:::note
便利さのために、*普通は*終了タグを必要とする要素は自己完結タグとすることが**できます**。
例えば`html! { <div class="placeholder" /> }`と書くのは有効です。
:::
## 子
複雑にネストした HTML や SVG のレイアウトを書くのには以下のようにするのが楽です:
\*\*
<!--DOCUSAURUS_CODE_TABS-->
<!--HTML-->
```rust
html! {
<div>
<div data-key="abc"></div>
<div class="parent">
<span class="child" value="anything"></span>
<label for="first-name">{ "First Name" }</label>
<input type="text" id="first-name" value="placeholder" />
<input type="checkbox" checked=true />
<textarea value="write a story" />
<select name="status">
<option selected=true disabled=false value="">{ "Selected" }</option>
<option selected=false disabled=true value="">{ "Unselected" }</option>
</select>
</div>
</div>
}
```
<!--SVG-->
```rust
html! {
<svg width="149" height="147" viewBox="0 0 149 147" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M60.5776 13.8268L51.8673 42.6431L77.7475 37.331L60.5776 13.8268Z" fill="#DEB819"/>
<path d="M108.361 94.9937L138.708 90.686L115.342 69.8642" stroke="black" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
<g filter="url(#filter0_d)">
<circle cx="75.3326" cy="73.4918" r="55" fill="#FDD630"/>
<circle cx="75.3326" cy="73.4918" r="52.5" stroke="black" stroke-width="5"/>
</g>
<circle cx="71" cy="99" r="5" fill="white" fill-opacity="0.75" stroke="black" stroke-width="3"/>
<defs>
<filter id="filter0_d" x="16.3326" y="18.4918" width="118" height="118" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="2"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
</filter>
</defs>
</svg>
}
```
<!--END_DOCUSAURUS_CODE_TABS-->
## クラス
要素へのクラスを特定する便利なやり方はたくさんあります:
<!--DOCUSAURUS_CODE_TABS-->
<!--Literal-->
```rust
html! {
<div class="container"></div>
}
```
<!--Multiple-->
```rust
html! {
<div class="container center-align"></div>
}
```
<!--Interpolated-->
```rust
html! {
<div class={format!("{}-container", size)}></div>
}
```
<!--Expression-->
```rust
html! {
<div class={self.classes()}></div>
}
```
<!--Tuple-->
```rust
html! {
<div class={("class-1", "class-2")}></div>
}
```
<!--Vector-->
```rust
html! {
<div class={vec!["class-1", "class-2"]}></div>
}
```
<!--END_DOCUSAURUS_CODE_TABS-->
## リスナー
リスナー属性はクロージャのラッパーである`Callback`に渡される必要があります。
コールバックをどのように作るかはアプリをリスナーイベントにどう反応させたいかによります。
<!--DOCUSAURUS_CODE_TABS-->
<!--Component handler-->
```rust
struct MyComponent {
link: ComponentLink<Self>,
}
enum Msg {
Click,
}
impl Component for MyComponent {
type Message = Msg;
type Properties = ();
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
MyComponent { link }
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
match msg {
Msg::Click => {
// Handle Click
}
}
}
fn view(&self) -> Html {
// Create a callback from a component link to handle it in a component
let click_callback = self.link.callback(|_: ClickEvent| Msg::Click);
html! {
<button onclick={click_callback}>
{ "Click me!" }
</button>
}
}
}
```
<!--Agent Handler-->
```rust
struct MyComponent {
worker: Dispatcher<MyWorker>,
}
impl Component for MyComponent {
type Message = ();
type Properties = ();
fn create(_: Self::Properties, _: ComponentLink<Self>) -> Self {
MyComponent {
worker: MyWorker::dispatcher()
}
}
fn update(&mut self, _: Self::Message) -> ShouldRender {
false
}
fn view(&self) -> Html {
// Create a callback from a worker to handle it in another context
let click_callback = self.worker.callback(|_: ClickEvent| WorkerMsg::Process);
html! {
<button onclick={click_callback}>
{ "Click me!" }
</button>
}
}
}
```
<!--Other Cases-->
```rust
struct MyComponent;
impl Component for MyComponent {
type Message = ();
type Properties = ();
fn create(_: Self::Properties, _: ComponentLink<Self>) -> Self {
MyComponent
}
fn update(&mut self, _: Self::Message) -> ShouldRender {
false
}
fn view(&self) -> Html {
// Create an ephemeral callback
let click_callback = Callback::from(|| {
ConsoleService::log("clicked!");
});
html! {
<button onclick={click_callback}>
{ "Click me!" }
</button>
}
}
}
```
<!--END_DOCUSAURUS_CODE_TABS-->
## イベントの型
:::note
以下のテーブルにある全てのイベントの型は`yew::events`で再エクスポートされています。
All the event types mentioned in the following table are re-exported under `yew::events`. Using the types from
`yew::events` makes it easier to ensure version compatibility than if you were to manually include `web-sys`
or `stdweb` as dependencies in your crate because you won't end up using a version which conflicts with
the version Yew specifies.
:::
| イベント名 | `web_sys` イベント型 |
| --------------------------- | ------------------------------------------------------------------------------------- |
| `onabort` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
| `onauxclick` | [MouseEvent](https://docs.rs/web-sys/latest/web_sys/struct.MouseEvent.html) |
| `onblur` | [FocusEvent](https://docs.rs/web-sys/latest/web_sys/struct.FocusEvent.html) |
| `oncancel` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
| `oncanplay` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
| `oncanplaythrough` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
| `onchange` | [ChangeData](https://docs.rs/yew/latest/yew/events/enum.ChangeData.html) |
| `onclick` | [MouseEvent](https://docs.rs/web-sys/latest/web_sys/struct.MouseEvent.html) |
| `onclose` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
| `oncontextmenu` | [MouseEvent](https://docs.rs/web-sys/latest/web_sys/struct.MouseEvent.html) |
| `oncuechange` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
| `ondblclick` | [MouseEvent](https://docs.rs/web-sys/latest/web_sys/struct.MouseEvent.html) |
| `ondrag` | [DragEvent](https://docs.rs/web-sys/latest/web_sys/struct.DragEvent.html) |
| `ondragend` | [DragEvent](https://docs.rs/web-sys/latest/web_sys/struct.DragEvent.html) |
| `ondragenter` | [DragEvent](https://docs.rs/web-sys/latest/web_sys/struct.DragEvent.html) |
| `ondragexit` | [DragEvent](https://docs.rs/web-sys/latest/web_sys/struct.DragEvent.html) |
| `ondragleave` | [DragEvent](https://docs.rs/web-sys/latest/web_sys/struct.DragEvent.htmk) |
| `ondragover` | [DragEvent](https://docs.rs/web-sys/latest/web_sys/struct.DragEvent.html) |
| `ondragstart` | [DragEvent](https://docs.rs/web-sys/latest/web_sys/struct.DragEvent.html) |
| `ondrop` | [DragEvent](https://docs.rs/web-sys/latest/web_sys/struct.DragEvent.html) |
| `ondurationchange` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
| `onemptied` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
| `onended` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
| `onerror` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
| `onfocus` | [FocusEvent](https://docs.rs/web-sys/latest/web_sys/struct.FocusEvent.html) |
| `onformdata` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
| `oninput` | [InputData](https://docs.rs/yew/latest/yew/events/struct.InputData.html) |
| `oninvalid` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
| `onkeydown` | [KeyboardEvent](https://docs.rs/web-sys/latest/web_sys/struct.KeyboardEvent.html) |
| `onkeypress` | [KeyboardEvent](https://docs.rs/web-sys/latest/web_sys/struct.KeyboardEvent.html) |
| `onkeyup` | [KeyboardEvent](https://docs.rs/web-sys/latest/web_sys/struct.KeyboardEvent.html) |
| `onload` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
| `onloadeddata` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
| `onloadedmetadata` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
| `onloadstart` | [ProgressEvent](https://docs.rs/web-sys/latest/web_sys/struct.ProgressEvent.html) |
| `onmousedown` | [MouseEvent](https://docs.rs/web-sys/latest/web_sys/struct.MouseEvent.html) |
| `onmouseenter` | [MouseEvent](https://docs.rs/web-sys/latest/web_sys/struct.MouseEvent.html) |
| `onmouseleave` | [MouseEvent](https://docs.rs/web-sys/latest/web_sys/struct.MouseEvent.html) |
| `onmousemove` | [MouseEvent](https://docs.rs/web-sys/latest/web_sys/struct.MouseEvent.html) |
| `onmouseout` | [MouseEvent](https://docs.rs/web-sys/latest/web_sys/struct.MouseEvent.html) |
| `onmouseover` | [MouseEvent](https://docs.rs/web-sys/latest/web_sys/struct.MouseEvent.html) |
| `onmouseup` | [MouseEvent](https://docs.rs/web-sys/latest/web_sys/struct.MouseEvent.html) |
| `onpause` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
| `onplay` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
| `onplaying` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
| `onprogress` | [ProgressEvent](https://docs.rs/web-sys/latest/web_sys/struct.ProgressEvent.html) |
| `onratechange` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
| `onreset` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
| `onresize` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
| `onscroll` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
| `onsecuritypolicyviolation` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
| `onseeked` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
| `onseeking` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
| `onselect` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
| `onslotchange` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
| `onstalled` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
| `onsubmit` | [FocusEvent](https://docs.rs/web-sys/latest/web_sys/struct.FocusEvent.html) |
| `onsuspend` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
| `ontimeupdate` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
| `ontoggle` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
| `onvolumechange` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
| `onwaiting` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
| `onwheel` | [WheelEvent](https://docs.rs/web-sys/latest/web_sys/struct.WheelEvent.html) |
| `oncopy` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
| `oncut` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
| `onpaste` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
| `onanimationcancel` | [AnimationEvent](https://docs.rs/web-sys/latest/web_sys/struct.AnimationEvent.html) |
| `onanimationend` | [AnimationEvent](https://docs.rs/web-sys/latest/web_sys/struct.AnimationEvent.html) |
| `onanimationiteration` | [AnimationEvent](https://docs.rs/web-sys/latest/web_sys/struct.AnimationEvent.html) |
| `onanimationstart` | [AnimationEvent](https://docs.rs/web-sys/latest/web_sys/struct.AnimationEvent.html) |
| `ongotpointercapture` | [PointerEvent](https://docs.rs/web-sys/latest/web_sys/struct.PointerEvent.html) |
| `onloadend` | [ProgressEvent](https://docs.rs/web-sys/latest/web_sys/struct.ProgressEvent.html) |
| `onlostpointercapture` | [PointerEvent](https://docs.rs/web-sys/latest/web_sys/struct.PointerEvent.html) |
| `onpointercancel` | [PointerEvent](https://docs.rs/web-sys/latest/web_sys/struct.PointerEvent.html) |
| `onpointerdown` | [PointerEvent](https://docs.rs/web-sys/latest/web_sys/struct.PointerEvent.html) |
| `onpointerenter` | [PointerEvent](https://docs.rs/web-sys/latest/web_sys/struct.PointerEvent.html) |
| `onpointerleave` | [PointerEvent](https://docs.rs/web-sys/latest/web_sys/struct.PointerEvent.html) |
| `onpointerlockchange` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
| `onpointerlockerror` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
| `onpointermove` | [PointerEvent](https://docs.rs/web-sys/latest/web_sys/struct.PointerEvent.html) |
| `onpointerout` | [PointerEvent](https://docs.rs/web-sys/latest/web_sys/struct.PointerEvent.html) |
| `onpointerover` | [PointerEvent](https://docs.rs/web-sys/latest/web_sys/struct.PointerEvent.html) |
| `onpointerup` | [PointerEvent](https://docs.rs/web-sys/latest/web_sys/struct.PointerEvent.html) |
| `onselectionchange` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
| `onselectstart` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
| `onshow` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
| `ontouchcancel` | [TouchEvent](https://docs.rs/web-sys/latest/web_sys/struct.TouchEvent.html) |
| `ontouchend` | [TouchEvent](https://docs.rs/web-sys/latest/web_sys/struct.TouchEvent.html) |
| `ontouchmove` | [TouchEvent](https://docs.rs/web-sys/latest/web_sys/struct.TouchEvent.html) |
| `ontouchstart` | [TouchEvent](https://docs.rs/web-sys/latest/web_sys/struct.TouchEvent.html) |
| `ontransitioncancel` | [TransitionEvent](https://docs.rs/web-sys/latest/web_sys/struct.TransitionEvent.html) |
| `ontransitionend` | [TransitionEvent](https://docs.rs/web-sys/latest/web_sys/struct.TransitionEvent.html) |
| `ontransitionrun` | [TransitionEvent](https://docs.rs/web-sys/latest/web_sys/struct.TransitionEvent.html) |
| `ontransitionstart` | [TransitionEvent](https://docs.rs/web-sys/latest/web_sys/struct.TransitionEvent.html) |

View File

@ -0,0 +1,22 @@
---
title: Introduction
description: The procedural macro for generating HTML and SVG
slug: /concepts/html
---
`html!`マクロによって HTML と SVG のコードを宣言的に書くことができます。
JSX \(HTML のようなコードを JavaScript 内部に書くことができる JavaScript の拡張\) に似ています。
**重要な注意**
1. `html!`マクロはルートの HTML ノードのみ受け付けます \([フラグメントかイテレータを使う](./lists.mdx)ことでやり取りできます\)
2. 空の`html! {}`の呼び出しは可能ですが何もレンダリングしません
3. リテラルはクオーテーションがつけられ、ブレースで囲う必要があります: `html! { "Hello, World" }`
:::note
`html!`マクロはコンパイラのデフォルトの再帰の上限に簡単に達してしまいます。
もしコンパイラエラーに遭遇した場合はその値を押し出すといいかもしれません。
クレートのルート\(つまり、`lib.rs`か`main.rs`\)で`#![recursion_limit="1024"]`のような属性を使えば解決します。
詳しくは[公式ドキュメント](https://doc.rust-lang.org/reference/attributes/limits.html#the-recursion_limit-attribute)と[Stack Overflow の質問](https://stackoverflow.com/questions/27454761/what-is-a-crate-attribute-and-where-do-i-add-it)を見てみてください。
:::

View File

@ -0,0 +1,60 @@
---
title: Lists
---
## フラグメント
`html!`マクロは常にルートノードが 1 つであることを要求します。
この制限のために、空のタグを使って内容をラップすると良いでしょう。
<!--DOCUSAURUS_CODE_TABS-->
<!--Valid-->
```rust
html! {
<>
<div></div>
<p></p>
</>
}
```
<!--Invalid-->
```rust
/* error: only one root html element allowed */
html! {
<div></div>
<p></p>
}
```
<!--END_DOCUSAURUS_CODE_TABS-->
## イテレータ
Yew はイテレータから HTML をビルドするのに 2 つの方法をサポートしています。
<!--DOCUSAURUS_CODE_TABS-->
<!--Syntax Type 1-->
```rust
html! {
<ul class="item-list">
{ self.props.items.iter().map(renderItem).collect::<Html>() }
</ul>
}
```
<!--Syntax Type 2-->
```rust
html! {
<ul class="item-list">
{ for self.props.items.iter().map(renderItem) }
</ul>
}
```
<!--END_DOCUSAURUS_CODE_TABS-->

View File

@ -0,0 +1,60 @@
---
title: Literals and Expressions
---
## リテラル
式が`Display`を実装した型を解決する場合、文字列に変換されて DOM に[Text](https://developer.mozilla.org/en-US/docs/Web/API/Text)ノードとして挿入されます。
テキストは式として処理されるため、全ての表示される内容は`{}`ブロックによって囲まれる必要があります。
これは Yew のアプリと通常の HTML の構文で最も異なる点です。
```rust
let text = "lorem ipsum";
html!{
<>
<div>{text}</div>
<div>{"dolor sit"}</div>
<span>{42}</span>
</>
}
```
## 式
HTML に`{}`ブロックを使って式を挿入することができます。
```rust
html! {
<div>
{
if show_link {
html! {
<a href="https://example.com">{"Link"}</a>
}
} else {
html! {}
}
}
</div>
}
```
式を関数やクロージャに分離するのはコードの可読性の観点から有効なことがあります。
```rust
let show_link = true;
let maybe_display_link = move || -> Html {
if show_link {
html! {
<a href="https://example.com">{"Link"}</a>
}
} else {
html! {}
}
};
html! {
<div>{maybe_display_link()}</div>
}
```

View File

@ -0,0 +1,95 @@
---
title: Router
description: Yew's official router
---
[crates.io にあるルータ](https://crates.io/crates/yew-router)
シングルページアプリケーション\(SPA\)におけるルータは URL よってページを出し分けます。
リンクがクリックされたときに異なるリソースを要求するというデフォルトの動作の代わりに、ルータはアプリケーション内の有効なルートを指すように URL をローカルに設定します。
ルータはこの変更を検出してから、何をレンダリングするかを決定します。
## コアとなる要素
### `Route`
URL 内のドメインの後のすべてを表す文字列と、オプションで history API に保存されている状態を含みます。
### `RouteService`
ブラウザとやりとりしてルーティングを決めます。
### `RouteAgent`
RouteService を所有し、ルートが変更された際の更新を調整するために使用します。
### `Switch`
`Switch`トレイトは`Route`をトレイトの実装する側の間で変換するために用いられます。
### `Router`
Router コンポーネントは RouteAgent とやり取りし、エージェントがどうスイッチするか Routes を自動的に解決します。
これは、結果として得られるスイッチがどのように Html に変換されるかを指定できるようにするため、props を介して公開されます。
## ルータをどのように使うか
まず、アプリケーションのすべての状態を表す型を作成します。
これは通常は列挙型ですが、構造体もサポートされており、`Switch` を実装した他のアイテムを内部に入れ子にすることができることに注意してください。
次に、`Switch`を型に継承させなければいけません。
列挙型の場合は全ての variant は`#[to = "/some/route"]`とアノテーションされている必要があり、代わり構造体を用いている場合は構造体宣言が外部から見えるようにしてなければいけません。
```rust
#[derive(Switch)]
enum AppRoute {
#[to="/login"]
Login,
#[to="/register"]
Register,
#[to="/delete_account"]
Delete,
#[to="/posts/{id}"]
ViewPost(i32),
#[to="/posts/view"]
ViewPosts,
#[to="/"]
Home
}
```
:::caution
`Switch`用の派生マクロによって生成された実装は、各 variant を最初から最後までの順にマッチさせようとするので、指定した`to`アノテーションのうち 2 つのルートにマッチする可能性がある場合は、最初のルートがマッチし、2 つ目のルートは試行されないことに注意してください。例えば、以下の`Switch`を定義した場合、マッチするルートは`AppRoute::Home`だけになります。
```rust
#[derive(Switch)]
enum AppRoute {
#[to="/"]
Home,
#[to="/login"]
Login,
#[to="/register"]
Register,
#[to="/delete_account"]
Delete,
#[to="/posts/{id}"]
ViewPost(i32),
#[to="/posts/view"]
ViewPosts,
}
```
:::
また、`#[to = ""]`アノテーションの中で`{}`のバリエーションを使ってセクションをキャプチャすることもできます。
`{}`は、次の区切り文字\(コンテキストに応じて "/", "?", "&", "#" のいずれか\) までのテキストをキャプチャします。
`{*}`は、次の文字が一致するまでテキストをキャプチャすることを意味します。
`{<number>}`は、指定した数の区切り文字が見つかるまでテキストをキャプチャすることを意味します
\(例: `{2}`は区切り文字が 2 つ見つかるまでキャプチャします\)。
名前付きフィールドを持つ構造体や列挙型の場合は、キャプチャグループ内で以下のようにフィールドの名前を指定する必要があります。
`{user_name}` または `{*:age}` のように、キャプチャグループ内でフィールドの名前を指定しなければなりません。
Switch トレイトは文字列よりも構造化されたキャプチャグループで動作します。
`Switch`を実装した任意の型を指定することができます。
そのため、キャプチャグループが `usize` であることを指定することができ、URL のキャプチャ部分がそれに変換できない場合、variant はマッチしません。

View File

@ -0,0 +1,126 @@
---
title: Build a sample app
---
はじめに、Rust の新規ライブラリを作りましょう(**重要:** `--lib`フラグを渡すことで*バイナリ*ではなく*ライブラリ*を作ってください)
```bash
cargo new --lib yew-app && cd yew-app
```
依存ライブラリに`yew`と`wasm-bindgen`を追加してください \(最新バージョンについては[こちら](https://docs.rs/yew)を参照してください\)
```toml title="Cargo.toml"
[package]
name = "yew-app"
version = "0.1.0"
authors = ["Yew App Developer <name@example.com>"]
edition = "2018"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
yew = "0.17"
wasm-bindgen = "0.2"
```
以下のテンプレートを `src/lib.rs`ファイルにコピーしてください:
```rust title="src/lib.rs"
use wasm_bindgen::prelude::*;
use yew::prelude::*;
struct Model {
link: ComponentLink<Self>,
value: i64,
}
enum Msg {
AddOne,
}
impl Component for Model {
type Message = Msg;
type Properties = ();
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
Self {
link,
value: 0,
}
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
match msg {
Msg::AddOne => self.value += 1
}
true
}
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
// Should only return "true" if new properties are different to
// previously received properties.
// This component has no properties so we will always return "false".
false
}
fn view(&self) -> Html {
html! {
<div>
<button onclick={self.link.callback(|_| Msg::AddOne)}>{ "+1" }</button>
<p>{ self.value }</p>
</div>
}
}
}
#[wasm_bindgen(start)]
pub fn run_app() {
App::<Model>::new().mount_to_body();
}
```
このテンプレートはルートに`Component`をセットアップし、`Model`と呼ばれるクリックしたら更新するボタンを作ります。
`main()`の中にある`App::<Model>::new().mount_to_body()`がアプリをスタートしてページの`<body>`タグをマウントすることに特に注意してください。
動的なプロパティでアプリをスタートしたい場合は代わりに`App::<Model>::new().mount_to_body_with_props(..)`を使うことで実現できます。
最後に、アプリの中の`static`という名前のフォルダに`index.html`ファイルを追加してください。
```bash
mkdir static
```
```markup title="index.html"
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Yew Sample App</title>
<script type="module">
import init from "./wasm.js"
init()
</script>
</head>
<body></body>
</html>
```
## アプリを動かす!
[`wasm-pack`](https://rustwasm.github.io/docs/wasm-pack/)を使うのがアプリを動かすのに推奨される方法です。
まだ`wasm-pack`をインストールしていない場合、`cargo install wasm-pack`でインストールして開発サーバーを動かしてみましょう:
```bash
wasm-pack build --target web --out-name wasm --out-dir ./static
```
`wasm-pack`はコンパイルされた WebAssembly と JavaScript ラッパーをまとめたものを`./static`ディレクトリに作り、
アプリの WebAssembly バイナリを読み込んで動かします。
そして、`./static`以下で好きなサーバーをファイルをサーブしてみましょう。
例えば:
```bash
cargo +nightly install miniserve
miniserve ./static --index index.html
```

View File

@ -0,0 +1,14 @@
---
title: Examples
---
Yew のリポジトリは[例](https://github.com/yewstack/yew/tree/v0.17/examples)がたくさんあります
\(メンテナンス状況は様々\)。
様々なフレームワークの機能の使い方を知るのにはそれらの例に取り組むのを勧めます。
プルリクエストや Issue はウェルカムです。
- [**Todo アプリ** ](https://github.com/yewstack/yew/tree/v0.17/examples/todomvc)
- [**カスタムコンポーネント**](https://github.com/yewstack/yew/tree/v0.17/examples/custom_components)
- [**マルチスレッド\(エージェント\)**](https://github.com/yewstack/yew/tree/v0.17/examples/multi_thread)
- [**タイマーサービス**](https://github.com/yewstack/yew/tree/v0.17/examples/timer)
- [**ネストしたコンポーネント**](https://github.com/yewstack/yew/tree/v0.16.0/examples/nested_list)

View File

@ -0,0 +1,18 @@
---
title: CSS
---
# CSS
&lt;TODO&gt;
統合的な CSS サポートについての提案はこちらにあります: [https://github.com/yewstack/yew/issues/533](https://github.com/yewstack/yew/issues/533)
## スタイルフレームワーク:
今のところ、コミュニティメンバーは以下のスタイルフレームワークを開発しています。
- [yew_styles](https://github.com/spielrs/yew_styles) - JavaScript に依存しない Yew のスタイルフレームワーク
- [yew-mdc](https://github.com/Follpvosten/yew-mdc) - マテリアルデザインのコンポーネント
- [muicss-yew](https://github.com/AlephAlpha/muicss-yew) - MUI の CSS コンポーネント
- [Yewtify](https://github.com/yewstack/yewtify) Yew で Vuetify フレームワークで提供されている機能の実装

View File

@ -0,0 +1,58 @@
---
title: Debugging
---
# デバッグ
## パニック
Rust シンボルで良いスタックトレースをするには
[`console_error_panic`](https://github.com/rustwasm/console_error_panic_hook)クレートを使用してください。
注意として、`cargo-web`でビルドされたものとは互換性がありません。
## コンソールでのログ
一般的に、Wasm の Web アプリはブラウザの API と連携することができ、`console.log`の API も例外ではありません。
いつくかの選択肢があります:
### [`wasm-logger`](https://crates.io/crates/wasm-logger)
このクレートは Rust の`log`クレートと親和性があります。
```rust
// セットアップ
fn main() {
wasm_logger::init(wasm_logger::Config::default());
}
// 使用方法
log::info!("Update: {:?}", msg);
```
### [`ConsoleService`](https://docs.rs/yew/latest/yew/services/console/struct.ConsoleService.html)
このサービスは Yew に含まれており、`"services"`の機能が有効化されている場合は利用可能です。
```rust
// 使用方法
ConsoleService::info(format!("Update: {:?}", msg).as_ref());
```
## ソースマップ
今のところは Rust/Wasm の Web アプリにはソースマップへの第一級のサポートがありません。
もちろん、これは変更される可能性があります。これが当てはまらない場合、または進捗が見られる場合は、変更を提案してください!
### 最新情報
\[2019 年 12 月\] [Chrome DevTools update](https://developers.google.com/web/updates/2019/12/webassembly#the_future)
> やらなければいけないことがまだたくさんあります。例えばツール側では Emscripten\(Binaryen\)と wasm-pack\(wasm-bindgen\)がそれらが実行する変換に関する DWARF 情報の更新をまだサポートしていません。
\[2020\] [Rust Wasm デバッグガイド](https://rustwasm.github.io/book/reference/debugging.html#using-a-debugger)
> 残念なことに、WebAssembly のデバッグの物語はまだ未成熟です。ほとんどの Unix のシステムでは[DWARF](http://dwarfstd.org/)は実行中のプログラムをソースレベルで検査するためにデバッガに必要な情報をエンコードするために使用されます。Windows には同様の情報をエンコードする代替形式があります。現在、WebAssembly に相当するものはありません。
\[2019\] [Rust Wasm ロードマップ](https://rustwasm.github.io/rfcs/007-2019-roadmap.html#debugging)
> デバッグはトリッキーです。なぜなら、多くの話はこの活動チームの手の届かないところにあり、WebAssembly の標準化団体とブラウザ開発者ツールを実装している人たちの両方に依存しているからです。

View File

@ -0,0 +1,45 @@
---
title: Roadmap
description: The planned feature roadmap for the Yew framework
---
# ロードマップ
## 優先順位
フレームワークの今後の機能やフォーカスの優先順位は、コミュニティによって決定されます。2020 年の春には、プロジェクトの方向性についてのフィードバックを集めるために開発者アンケートが行われました。その概要は [Yew Wiki](https://github.com/yewstack/yew/wiki/Dev-Survey-%5BSpring-2020%5D) で見ることができます。
:::note
主要な取り組みの状況は、Yew の Github の[Project board](https://github.com/yewstack/yew/projects)で確認できます。
:::
## 焦点
1. Top Requested Features
2. Production Readiness
3. Documentation
4. Pain Points
### Top Requested Features
1. [関数型コンポーネント](https://github.com/yewstack/yew/projects/3)
2. [Component ライブラリ](https://github.com/yewstack/yew/projects/4)
3. より良い状態管理
4. [サーバーサイドでのレンダリング](https://github.com/yewstack/yew/projects/5)
### Production Readiness
- テストカバレッジの向上
- バイナリサイズ
- [ベンチマークのパフォーマンス](https://github.com/yewstack/yew/issues/5)
### Documentation
- チュートリアルを作る
- プロジェクトのセットアップをシンプルにする
### Pain Points
- [Component のボイラープレート](https://github.com/yewstack/yew/issues/830)
- Fetch API
- [エージェント](https://github.com/yewstack/yew/projects/6)

View File

@ -0,0 +1,12 @@
---
title: Testing apps
description: Testing your app
---
# Testing
&lt;TODO&gt;
## wasm_bindgen_test
Rust Wasm ワーキンググループは wasm_bindgen_test というフレームワークをメンテナンスしており、組み込みの #[test] プロシージャルマクロの動作と同様の方法でブラウザでテストを実行することができます。詳細は、[Rust Wasm 活動グループのドキュメント](https://rustwasm.github.io/docs/wasm-bindgen/wasm-bindgen-test/index.html)に記載されています。

View File

@ -0,0 +1,86 @@
{
"version.label": {
"message": "0.20",
"description": "The label for version 0.20"
},
"sidebar.docs.category.Getting Started": {
"message": "Getting Started",
"description": "The label for category Getting Started in sidebar docs"
},
"sidebar.docs.category.Concepts": {
"message": "Concepts",
"description": "The label for category Concepts in sidebar docs"
},
"sidebar.docs.category.Concepts.link.generated-index.title": {
"message": "Yew concepts",
"description": "The generated-index page title for category Concepts in sidebar docs"
},
"sidebar.docs.category.Concepts.link.generated-index.description": {
"message": "Learn about the important Yew concepts!",
"description": "The generated-index page description for category Concepts in sidebar docs"
},
"sidebar.docs.category.Using Basic Web Technologies In Yew": {
"message": "Using Basic Web Technologies In Yew",
"description": "The label for category Using Basic Web Technologies In Yew in sidebar docs"
},
"sidebar.docs.category.Using Basic Web Technologies In Yew.link.generated-index.title": {
"message": "Yew's Take on Basic Web Technologies",
"description": "The generated-index page title for category Using Basic Web Technologies In Yew in sidebar docs"
},
"sidebar.docs.category.Using Basic Web Technologies In Yew.link.generated-index.description": {
"message": "Yew centrally operates on the idea of keeping everything that a reusable piece of UI may needin one place - rust files, while also keeping the underlying technology accessible where necessary. Explore further to fully grasp what we mean by these statements:",
"description": "The generated-index page description for category Using Basic Web Technologies In Yew in sidebar docs"
},
"sidebar.docs.category.Components": {
"message": "Components",
"description": "The label for category Components in sidebar docs"
},
"sidebar.docs.category.Hooks": {
"message": "Hooks",
"description": "The label for category Hooks in sidebar docs"
},
"sidebar.docs.category.HTML": {
"message": "HTML",
"description": "The label for category HTML in sidebar docs"
},
"sidebar.docs.category.Advanced topics": {
"message": "Advanced topics",
"description": "The label for category Advanced topics in sidebar docs"
},
"sidebar.docs.category.Advanced topics.link.generated-index.title": {
"message": "Advanced topics",
"description": "The generated-index page title for category Advanced topics in sidebar docs"
},
"sidebar.docs.category.Advanced topics.link.generated-index.description": {
"message": "Learn about the advanced topics and inner workings of Yew!",
"description": "The generated-index page description for category Advanced topics in sidebar docs"
},
"sidebar.docs.category.Struct Components": {
"message": "Struct Components",
"description": "The label for category Struct Components in sidebar docs"
},
"sidebar.docs.category.More": {
"message": "More",
"description": "The label for category More in sidebar docs"
},
"sidebar.docs.category.More.link.generated-index.title": {
"message": "Miscellaneous",
"description": "The generated-index page title for category More in sidebar docs"
},
"sidebar.docs.category.Migration guides": {
"message": "Migration guides",
"description": "The label for category Migration guides in sidebar docs"
},
"sidebar.docs.category.yew": {
"message": "yew",
"description": "The label for category yew in sidebar docs"
},
"sidebar.docs.category.yew-agent": {
"message": "yew-agent",
"description": "The label for category yew-agent in sidebar docs"
},
"sidebar.docs.category.yew-router": {
"message": "yew-router",
"description": "The label for category yew-router in sidebar docs"
}
}

View File

@ -0,0 +1,7 @@
---
description: 有关框架的底层细节
---
# 底层库的内部细节
组件生命周期状态机,虚拟 dom diff 算法。

View File

@ -0,0 +1,93 @@
---
description: 加速你的应用程序
---
# 性能优化与最佳实践
## neq_assign
当组件从它的父组件接收 props 时,`change` 方法将被调用。除了允许你更新组件的状态,还允许你返回一个布尔类型的值 `ShouldRender` 来指示组件是否应该响应 props 的更改而重新渲染自身。
重新渲染的开销很大,你应该尽量避免。一个通用的法则是,你只应该在 props 实际更改时重新渲染。以下代码块展示了此法则,如果 props 和先前的 props 不同,则返回 `true`
```rust
fn change(&mut self, props: Self::Properties) -> ShouldRender {
if self.props != &props {
*self.props = props;
true
} else {
false
}
}
```
但是我们可以更进一步!对于任何实现了 `PartialEq` 的项,可以使用一个 trait 和一个 blanket implementation 将这六行样板代码减少到一行。
{% code title="neq\_assign.rs" %}
```rust
pub trait NeqAssign {
fn neq_assign(&mut self, new: Self) -> ShouldRender;
}
impl<T: PartialEq> NeqAssign for T {
fn neq_assign(&mut self, new: T) -> ShouldRender {
if self != &new {
*self = new;
true
} else {
false
}
}
}
// ...
fn change(&mut self, props: Self::Properties) -> ShouldRender {
self.props.neq_assign(props)
}
```
{% endcode %}
该 trait 称为 `NeqAssign` 是因为如果目标值和新值不相等,它将赋为新值。
这比简单的实现还要短:
```rust
// 不要这样做,除非你无法避免。
fn change(&mut self, props: Self::Properties) -> ShouldRender {
self.props = props;
true
}
```
你不仅限在 `change` 函数中使用它。通常,在 `update` 函数中执行此操作也是有意义的,尽管性能提升在那里不太明显。
## RC
为了避免在重新渲染时为了创建 props 而克隆大块数据,我们可以使用智能指针来只克隆指针。如果在 props 和子组件中使用 `Rc<_>` 而不是普通未装箱的值,则可以延迟克隆直到需要修改子组件中的数据为止,在该组件中可以使用 `Rc::make_mut` 来对要更改数据进行克隆和获取可变引用。通过在要修改前不进行克隆,子组件可以在几乎没有性能成本的情况下拒绝与它们在 `Component::change` 中拥有状态的 props 相同的 props这与数据本身需要先复制到父级 props 结构体中,然后在子级中进行比较和拒绝的情况相反。
对于不是 `Copy` 类型的数据,这种优化是最有用的。如果你能轻松地拷贝数据,那么将其放入智能指针中可能是不值得的。对于可以包含大量数据的结构,例如 `Vec``HashMap` 和 `String`,这种优化应该是值得的。
如果子组件从不更新组件的值,则这种优化效果最好,如果父组件很少更新组件的值,则效果更好。这使得 `Rc<_>s` 是包装纯组件属性值的不错选择。
## 视图函数
出于代码可读性的原因,将 `html!` 各个部分的代码迁移到他们自己的函数中通常是有意义的,这样就可以避免在深层嵌套的 HTML 中出现代码块向右偏移。
## 纯组件 / 函数式组件
纯组件是不会修改它们状态的组件,它们仅展示内容和向普通可变组件传递消息。它们与视图函数不同之处在于他们可以使用组件语法(`<SomePureComponent />`)而不是表达式语法(`{some_view_function()}`)来在 `html!` 宏中使用,并且根据它们的实现,它们可以被记忆化 - 使用前面提到的 `neq_assign` 逻辑来防止因为相同的 props 而重新渲染。
Yew 没有原生支持纯组件或者函数式组件,但是可以通过外部库获取它们。
函数式组件尚不存在,但是从理论上来讲,可以通过使用 proc 宏和标注函数生成纯组件。
## Keyed DOM nodes when they arrive
## 使用 Cargo Workspaces 进行编译速度优化
可以说,使用 Yew 的最大缺点是编译时间长。编译时间似乎与 `html!` 宏块中的代码量相关。对于较小的项目,这通常不是什么大问题,但是对于跨多个页面的 web 应用程序,将代码拆分为多个 crates 以最大程度地减少编译器要做的工作通常是有意义的。
你应该尝试让主 crate 处理路由和页面选择,将所有公用的代码移动到另一个 crate然后为每一个页面创建一个不同的 crate其中每个页面可能是一个不同的组件或者只是一个产生 `Html` 的大函数。在最好的情况下,你将从重新构建所有代码到只重新构建主 crate 和一个页面的 crate。在最糟糕的情况下当你在“公共” crate 中编辑内容时,你将回到起点:编译所有依赖此公用 crate 的代码,这可能就是除此之外的所有代码。
如果你的主 crate 过于庞大,或者你想在深层嵌套的页面(例如,在另一个页面顶部渲染的页面)中快速迭代,则可以使用一个示例 crate 创建一个更简单的主页面实现并在之上渲染你正在开发的组件。

View File

@ -0,0 +1,27 @@
---
description: ComponentLink 和 Callbacks.
---
# 回调Callbacks
组件“link”是一种机制通过该机制组件可以注册回调并自行更新。
## ComponentLink API
### callback
注册一个回调,该回调将在执行时将消息发送到组件的更新机制。在内部,它将使用提供的闭包返回的消息调用 `send_self`。提供 `Fn(IN) -> Vec<COMP::Message>`,返回 `Callback<IN>`。
### send_message
当前循环结束后立即向组件发送消息,导致另一个更新循环启动。
### send_message_batch
注册一个回调,该回调在执行时立即发送一批消息。如果其中任何一个消息将导致组件重新渲染,那么组件会在该批次所有消息被处理后重新渲染。提供 `Fn(IN) -> COMP::Message`,返回 `Callback<IN>`。
## Callbacks
Callbacks 用于与 Yew 中的 servicesagents 和父组件进行通信。它们仅仅是个 `Fn`,并由 `Rc` 包裹以允许被克隆。
它们有一个 `emit` 函数,该函数将它的 `<IN>` 类型作为参数并将其转换为目标所期望的消息。如果一个回调从父组件中通过 props 提供给子组件,则子组件可以在其 `update` 生命周期钩子中对该回调调用 `emit`,以将消息发送回父组件。在 `html!` 宏内被提供作为 props 的闭包或函数会自动转换为 Callbacks。

View File

@ -0,0 +1,170 @@
---
description: 组件及其生命周期钩子
---
# 组件Components
## 组件是什么?
组件是 Yew 的基石。它们管理自己的状态,并可以渲染为 DOM。组件是通过实现描述组件生命周期的 `Component` trait 来创建的。
## 生命周期
:::note
`为我们的文档做出贡献:`[添加组件的生命周期图示](https://github.com/yewstack/docs/issues/22)
:::
## 生命周期方法
### Create
当一个组件被创建时,它会从其父组件以及一个 `ComponentLink` 接收属性properties。属性properties可用于初始化组件的状态“link”可用于注册回调或向组件发送消息。
通常将 props 和 link 存储在组件的结构体中,如下所示:
```rust
pub struct MyComponent {
props: Props,
link: ComponentLink<Self>,
}
impl Component for MyComponent {
type Properties = Props;
// ...
fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
MyComponent { props, link }
}
// ...
}
```
### View
组件在 `view()` 方法中声明它的布局。Yew 提供了 `html!` 宏来声明 HTML 和 SVG 节点和它们的监听器及其子组件。这个宏的行为很像 React 中的 JSX但是使用的是 Rust 表达式而不是 JavaScript。
```rust
impl Component for MyComponent {
// ...
fn view(&self) -> Html {
let onclick = self.link.callback(|_| Msg::Click);
html! {
<button {onclick}>{ self.props.button_text }</button>
}
}
}
```
有关用法的详细信息,请查看 [`html!` 宏指南](concepts/html/introduction.mdx)]
### Mounted
`mounted()` 组件生命周期方法调用是在 `view()` 被处理并且 Yew 已经把组件挂载到 DOM 上之后,浏览器刷新页面之前。组件通常希望实现此方法以执行只能在组件渲染元素之后才能执行的操作。如果你想在做出一些更改后重新渲染组件,返回 `true` 就可以了。
```rust
use stdweb::web::html_element::InputElement;
use stdweb::web::IHtmlElement;
use yew::prelude::*;
pub struct MyComponent {
node_ref: NodeRef,
}
impl Component for MyComponent {
// ...
fn view(&self) -> Html {
html! {
<input ref={self.node_ref.clone()} type="text" />
}
}
fn mounted(&mut self) -> ShouldRender {
if let Some(input) = self.node_ref.cast::<InputElement>() {
input.focus();
}
false
}
}
```
:::note
请注意,此生命周期方法不要求必须实现,默认情况下不会执行任何操作。
:::
### Update
组件是动态的,可以注册以接收异步信息。`update()` 生命周期方法对于每个消息都会被调用。这使得组件可以根据消息的内容来更新自身,并决定是否需要重新渲染自己。消息可以由 HTML 元素监听器触发或者由子组件AgentsServices 或 Futures 发送。
`update()` 可能看起来像下面这个例子:
```rust
pub enum Msg {
SetInputEnabled(bool)
}
impl Component for MyComponent {
type Message = Msg;
// ...
fn update(&mut self, msg: Self::Message) -> ShouldRender {
match msg {
Msg::SetInputEnabled(enabled) => {
if self.input_enabled != enabled {
self.input_enabled = enabled;
true // 重新渲染
} else {
false
}
}
}
}
}
```
### Change
组件可能被其父节点重新渲染。发生这种情况时它们可以接收新的属性properties并选择重新渲染。这种设计通过更改属性properties来促进父子组件之间的通信。你不是必须实现 `change()`,但是如果想在组件被创建后通过 props 来更新组件,则可能要这么做。
一个原始的实现可能看起来像:
```rust
impl Component for MyComponent {
// ...
fn change(&mut self, props: Self::Properties) -> ShouldRender {
self.props = props;
true // 当提供了新的 props 将始终重新渲染。
}
}
```
### Destroy
组件从 DOM 上被卸载后Yew 调用 `destroy()` 生命周期方法来支持任何必要的清理操作。这个方法是可选的,默认情况下不执行任何操作。
## 关联类型
`Component` trait 有两个关联类型:`Message` 和 `Properties`。
```rust
impl Component for MyComponent {
type Message = Msg;
type Properties = Props;
// ...
}
```
`Message` 表示组件可以处理以触发某些副作用的各种消息。例如,你可能有一条 `Click` 消息,该消息触发 API 请求或者切换 UI 组件的外观。通常的做法是在组件模块中创建一个叫做 `Msg` 的枚举并将其用作组件中的消息类型。通常将“message”缩写为“msg”。
```rust
enum Msg {
Click,
}
```
`Properties` 表示从父级传递到组件的信息。此类型必须实现 `Properties` trait通常通过派生并且可以指定某些属性properties是必需的还是可选的。创建和更新组件时使用此类型。通常的做法是在组件模块中创建一个叫做 `Props` 的结构体并将其用作组件的 `Properties` 类型。通常将“properties”缩写为“props”。由于 props 是从父组件传递下来的,因此应用程序的根组件通常有一个类型为 `()` 的 `Properties`。如果你希望为根组件指定属性properties请使用 `App::mount_with_props` 方法。

View File

@ -0,0 +1,72 @@
---
description: 父组件到子组件的通信
---
# 属性Properties
如“组件Components”页面所述Properties 用于父级到子组件的通信。
## 派生宏
不要尝试自己去实现 `Properties`,而是通过使用 `#[derive(Properties)]` 来派生它。
### 必需属性
默认情况下,实现了 `Properties` 的结构体中的字段是必需的。当缺少了该字段并且在 `html!` 宏中创建了组件时,将返回编译错误。对于具有可选属性的字段,使用 `#[prop_or_default]` 来使用该类型的默认值。要指定一个值,请使用 `#[prop_or_else(value)]`,其中 value 是该属性的默认值。例如,要将一个布尔值的默认值设置为 `true`,请使用属性 `#[prop_or_else(true)]`。可选属性通常使用 `Option`,其默认值为 `None`。
### PartialEq
如果可以的话,在你的 props 上派生 `PartialEq` 通常是很有意义的。这使用了一个**性能优化与最佳实践**部分解释了的技巧,可以更轻松地避免重新渲染。
## Properties 的内存/速度开销
记住组件的 `view` 函数签名:
```rust
fn view(&self) -> Html
```
你对组件的状态取了一个引用,并用来创建 `Html`。但是 properties 是有所有权的值owned values。这意味着为了创造它们并且将它们传递给子组件我们需要获取 `view` 函数里提供的引用的所有权。这是在将引用传递给组件时隐式克隆引用完成的,以获得构成其 props 的有所有权的值。
这意味着每个组件都有从其父级传递来的状态的独特副本,而且,每当你重新渲染一个组件时,该重新渲染组件的所有子组件的 props 都将被克隆。
这意味着如果你将 _大量_ 数据作为 props大小为 10 KB 的字符串)向下传递,则可能需要考虑将子组件转换为在父级运行返回 `Html` 的函数,因为这样就不会被强制克隆你的数据。
另外,如果你不需要修改作为 props 传递的大数据,而只需要显示它,则可以将其包装在 `Rc` 中,以便仅克隆一个引用计数的指针,而不是数据本身。
## 示例
```rust
pub struct LinkColor {
Blue,
Red,
Green,
Black,
Purple,
}
impl Default for LinkColor {
fn default() -> Self {
// 除非另有说明,否则链接的颜色将为蓝色
LinkColor::Blue
}
}
#[derive(Properties, PartialEq)]
pub struct LinkProps {
/// 链接必须有一个目标地址
href: String,
/// 如果链接文本很大,这将使得复制字符串开销更小
/// 除非有性能问题,否则通常不建议这么做
text: Rc<String>,
/// 链接的颜色
#[prop_or_default]
color: LinkColor,
/// 如果为 None则 view 函数将不指定大小
#[prop_or_default]
size: Option<u32>
/// 当 view 函数没有指定 active其默认为 true
#[prop_or_else(true)]
active: bool,
}
```

View File

@ -0,0 +1,23 @@
---
title: Refs
description: 超出界限的 DOM 访问
---
`ref` 关键词可被用在任何 HTML 元素或组件内部以获得该项所附加到的 DOM 元素。这可被用于在 `view` 生命周期方法之外来对 DOM 进行更改。
这对于获取 canvas 元素或者滚动到页面的不同部分是有用的。
语法如下:
```rust
// 在 create 中
self.node_ref = NodeRef::default();
// 在 view 中
html! {
<div ref={self.node_ref.clone()}></div>
}
// 在 update 中
let has_attributes = self.node_ref.cast::<Element>().unwrap().has_attributes();
```

View File

@ -0,0 +1,40 @@
---
title: Agents
description: Yew 的 Actor 系统
---
Agents 和 Angular 的 [Services](https://angular.io/guide/architecture-services) 相似(但没有依赖注入),给 Yew 提供了 [Actor 模型](https://en.wikipedia.org/wiki/Actor_model)。Agents 可以用于在组件之间路由消息,而与它们在组件层次结构中的位置无关,或者可以用于协调全局状态,或者可以用于从主 UI 线程上卸载计算密集型任务,或者在不同的标签页间通信(在未来)。
Agents 使用 [web-workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers) 同时运行来实现并发。
## 生命周期
![Agent lifecycle](https://user-images.githubusercontent.com/42674621/79125224-b6481d80-7d95-11ea-8e6a-ab9b52d1d8ac.png)
## Agents 的类型
#### Reaches
- Job - 在 UI 线程上为每个新的 Bridge 生成一个新的 Agent。这对于将与浏览器通信的共享但独立的行为移出组件是很有用的。待验证任务完成后Agent 将消失。
- Context - Bridges 将生成或连接到 UI 线程上的 agent。这可用于在组件和其它 Agents 之间协调状态。当没有 Bridge 连接到该 Agent 时Agent 将消失。
- Private - 与 Job 相同,但运行在自己的 web worker 中。
- Public - 与 Context 相同,但运行在自己的 web worker 中。
- Global \(WIP\)
## Agent 通信
### Bridges
Bridges 将连接到一个 Agent 并且允许双向通信。
### Dispatchers
Dispatchers 和 Bridges 类似,但是他们只能发送消息给 Agents。
## 开销
Agents 通过使用二进制码 bincode 序列化其消息来进行通信。因此,存在比仅调用函数相比更高的性能消耗。除非计算成本或者在任意组件间协调的需求超过消息传递的成本,否则你应该尽可能地在函数中包含你的应用逻辑。
## Further reading
- The [web_worker_fib](https://github.com/yewstack/yew/tree/master/examples/web_worker_fib) example shows how components can use agents to communicate with each other.

View File

@ -0,0 +1,121 @@
---
title: 自定义钩子Custom Hooks
description: 定义你自己的 Hooks
---
## 定义自定义钩子
组件中与状态有关的逻辑可以通过创建自定义 Hooks 提取到函数中。
假设我们有一个组件它订阅了一个代理agent并且会显示发送给它的消息。
```rust
#[function_component(ShowMessages)]
pub fn show_messages() -> Html {
let (state, set_state) = use_state(|| vec![]);
{
let mut state = Rc::clone(&state);
use_effect(move || {
let producer = EventBus::bridge(Callback::from(move |msg| {
let mut messages = (*state).clone();
messages.push(msg);
set_state(messages)
}));
|| drop(producer)
});
}
let output = state.iter().map(|it| html! { <p>{ it }</p> });
html! { <div>{ for output }</div> }
}
```
这段代码有一个问题:逻辑不能被另一个组件重用。如果我们构建另一个跟踪消息的组件,我们可以将逻辑移动到自定义钩子中,而不是复制代码。
我们将首先创建一个名为`use_subscribe`的新函数。 `use_`前缀通常表示此函数是一个钩子。这个函数将不接受任何参数并返回`Rc<RefCell<Vec<String>>>` 。
```rust
fn use_subscribe() -> Rc<RefCell<Vec<String>>> {
// ...
}
```
钩子的逻辑在`use_hook`的回调中。 `use_hook`指的是自定义 Hook 的处理函数。它接受 2 个参数: `hook_runner`和`initial_state_producer` 。
`hook_runner`中包含了所有钩子的逻辑,它的回调的返回值又会被`use_hook`返回。 `hook_runner`需要 2 个参数:分别是对钩子和`hook_callback`它们两个的内部状态的可变引用。 而`hook_callback`同样也要 2 个参数:一个回调和一个 bool回调接受`internal_state` ,也就是对内部状态实例的可变引用,并且会调执行实际的更改,还会返回表示`ShouldRender`的布尔值,第二个参数 bool 的用处是指示它是否在组件渲染后运行。`use_hook`的第二个参数`initial_state_producer`接受用于创建内部状态实例的回调。这里说的内部状态指的是一个实现了`Hook` trait 的结构体。
现在让我们为`use_subscribe`钩子创建状态state struct
```rust
/// `use_subscribe` internal state
struct UseSubscribeState {
/// holds all the messages received
pub messages: Rc<RefCell<Vec<String>>>,
}
impl Hook for UseSubscribeState {}
```
接下来我们为`use_subscribe`添加实际逻辑。
```rust
fn use_subscribe() -> Rc<RefCell<Vec<String>>> {
use_hook(
// hook's handler. all the logic goes in here
|state: &mut UseSubscribeState, hook_callback| {
// calling other Hooks inside a hook
use_effect(move || {
let producer = EventBus::bridge(Callback::from(move |msg| {
hook_callback(
// where the mutations of state are performed
|state| {
(*state.messages).borrow_mut().deref_mut().push(msg);
true // should re-render
}, false // run post-render
)
}));
|| drop(producer)
});
// return from hook
state.messages.clone()
},
// initial state producer
|| UseSubscribeState { messages: Rc::new(RefCell::new(vec![])) },
)
}
```
现在我们可以使用自定义钩子了:
```rust
#[function_component(ShowMessages)]
pub fn show_messages() -> Html {
let state = use_subscribe();
let output = state.borrow().deref().into_iter().map(|it| html! { <p>{ it }</p> });
html! { <div>{ for output }</div> }
}
```
需要特别注意的是创建自定义钩子时`use_hook`不是必须的,它们只是用来包含其他钩子。通常应避免使用`use_hook`。
```rust
fn use_subscribe() -> Rc<Vec<String>> {
let (state, set_state) = use_state(Vec::new);
use_effect(move || {
let producer = EventBus::bridge(Callback::from(move |msg| {
let mut messages = (*state).clone();
messages.push(msg);
set_state(messages)
}));
|| drop(producer)
});
state
}
```

View File

@ -0,0 +1,27 @@
---
title: 函数式组件
sidebar_label: 简介
description: 介绍函数式组件
slug: /concepts/function-components
---
函数式组件是普通组件的简化版本。它们由一个接收 props 的函数组成,并通过返回`Html`来确定应该呈现什么。基本上,它是一个简化为`view`方法的组件。就其本身而言,这将是相当有限的,因为您只能创建纯组件,而这就是 Hook 大展身手的地方。Hook 允许函数组件无需实现`Component` trait就可以使用状态state和其他 Yew 功能。
## 创建函数式组件
创建函数式组件的最简单方法是在函数前添加`#[function_component]`属性。
```rust
#[function_component(HelloWorld)]
fn hello_world() -> Html {
html! { "Hello world" }
}
```
### 更多细节
函数式组件由两部分组成。首先, `FunctionProvider` trait 与`Component` trait 差不多,但它只有一个名为`run`方法。之后是`FunctionComponent`结构体,它封装了`FunctionProvider`类型并将其转换为实际的`Component` 。 `#[function_component]`属性本质上只是`FunctionProvider`并将其暴露在`FunctionComponent` 。
### 钩子Hooks
钩子Hooks就是让您“钩住”组件的状态state和/或生命周期并执行操作的函数。 除了 Yew 自带的一些预定义的 Hook。您也可以创建自己的。

View File

@ -0,0 +1,114 @@
---
description: 使用具有层次结构的组件来创建复杂的布局
---
# 组件
## 基础
任何实现了 `Component` trait 的类型都可被用在 `html!` 宏中:
```rust
html!{
<>
// 没有属性
<MyComponent />
// 具有属性
<MyComponent prop1="lorem" prop2="ipsum" />
// 同时提供全套的 props
<MyComponent ..props />
</>
}
```
## 嵌套
如果组件的 `Properties` 中有 `children` 字段,则可以被传递子组件。
{% code title="parent.rs" %}
```rust
html! {
<Container>
<h4>{ "Hi" }</h4>
<div>{ "Hello" }</div>
</Container>
}
```
{% endcode %}
{% code title="container.rs" %}
```rust
pub struct Container(Props);
#[derive(Properties)]
pub struct Props {
pub children: Children,
}
impl Component for Container {
type Properties = Props;
// ...
fn view(&self) -> Html {
html! {
<div id="container">
{ self.0.children.clone() }
</div>
}
}
}
```
{% endcode %}
## 拥有 Props 的嵌套子组件
如果包含组件标注了 children 的类型,则可以访问和更改嵌套组件的属性。在下面的示例中,`List` 组件可以包含 `ListItem` 组件。有关此模式的真实示例,请查看 `yew-router` 的源码。有关更高级的示例,请在 yew 主仓库中查看 `nested-list` 示例代码。
{% code title="parent.rs" %}
```rust
html! {
<List>
<ListItem value="a" />
<ListItem value="b" />
<ListItem value="c" />
</List>
}
```
{% endcode %}
{% code title="list.rs" %}
```rust
pub struct List(Props);
#[derive(Properties)]
pub struct Props {
pub children: ChildrenWithProps<ListItem>,
}
impl Component for List {
type Properties = Props;
// ...
fn view(&self) -> Html {
html!{{
for self.0.children.iter().map(|mut item| {
item.props.value = format!("item-{}", item.props.value);
item
})
}}
}
}
```
{% endcode %}

View File

@ -0,0 +1,264 @@
---
description: HTML 和 SVG 元素均受支持
---
# 元素
## 标签结构
元素标签必须是自闭合的 `<... />`,或是每个标签都有一个对应的闭合标签。
<!--DOCUSAURUS_CODE_TABS-->
<!--标签 - 闭合标签-->
```rust
html! {
<div id="my_div"></div>
}
```
<!--无效-->
```rust
html! {
<div id="my_div"> // <- 缺少闭合标签
}
```
<!--自闭合-->
```rust
html! {
<input id="my_input" />
}
```
<!--无效-->
```rust
html! {
<input id="my_input"> // <- 没有自闭合
}
```
<!--END_DOCUSAURUS_CODE_TABS-->
:::note
为方便起见,一些 _通常_ 需要闭合标签的元素是被**允许**自闭合的。例如,`html! { <div class="placeholder" /> }` 这样写是有效的。
:::
## Children
轻松创建复杂的嵌套 HTML 和 SVG 布局:
<!--DOCUSAURUS_CODE_TABS-->
<!--HTML-->
```rust
html! {
<div>
<div data-key="abc"></div>
<div class="parent">
<span class="child" value="anything"></span>
<label for="first-name">{ "First Name" }</label>
<input type="text" id="first-name" value="placeholder" />
<input type="checkbox" checked=true />
<textarea value="write a story" />
<select name="status">
<option selected=true disabled=false value="">{ "Selected" }</option>
<option selected=false disabled=true value="">{ "Unselected" }</option>
</select>
</div>
</div>
}
```
<!--SVG-->
```rust
html! {
<svg width="149" height="147" viewBox="0 0 149 147" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M60.5776 13.8268L51.8673 42.6431L77.7475 37.331L60.5776 13.8268Z" fill="#DEB819"/>
<path d="M108.361 94.9937L138.708 90.686L115.342 69.8642" stroke="black" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
<g filter="url(#filter0_d)">
<circle cx="75.3326" cy="73.4918" r="55" fill="#FDD630"/>
<circle cx="75.3326" cy="73.4918" r="52.5" stroke="black" stroke-width="5"/>
</g>
<circle cx="71" cy="99" r="5" fill="white" fill-opacity="0.75" stroke="black" stroke-width="3"/>
<defs>
<filter id="filter0_d" x="16.3326" y="18.4918" width="118" height="118" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="2"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
</filter>
</defs>
</svg>
}
```
<!--END_DOCUSAURUS_CODE_TABS-->
## Classes
有许多方便的选项可用于元素指定 classes
<!--DOCUSAURUS_CODE_TABS-->
<!--常量-->
```rust
html! {
<div class="container"></div>
}
```
<!--多个属性-->
```rust
html! {
<div class="container center-align"></div>
}
```
<!--插值-->
```rust
html! {
<div class={format!("{}-container", size)}></div>
}
```
<!--表达式-->
```rust
html! {
<div class={self.classes()}></div>
}
```
<!--元组-->
```rust
html! {
<div class={("class-1", "class-2")}></div>
}
```
<!--Vector-->
```rust
html! {
<div class={vec!["class-1", "class-2"]}></div>
}
```
<!--END_DOCUSAURUS_CODE_TABS-->
## 监听器
监听器属性需要传递一个由闭包包裹的 `Callback`。创建回调的方式取决于你希望你的应用程序如何响应监听器事件:
<!--DOCUSAURUS_CODE_TABS-->
<!--Component 处理器-->
```rust
struct MyComponent {
link: ComponentLink<Self>,
}
enum Msg {
Click,
}
impl Component for MyComponent {
type Message = Msg;
type Properties = ();
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
MyComponent { link }
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
match msg {
Msg::Click => {
// 处理 Click
}
}
}
fn view(&self) -> Html {
// 从组件 link 中创建回调来在组件中处理它
let click_callback = self.link.callback(|_: ClickEvent| Msg::Click);
html! {
<button onclick={click_callback}>
{ "Click me!" }
</button>
}
}
}
```
<!--Agent 处理器-->
```rust
struct MyComponent {
worker: Dispatcher<MyWorker>,
}
impl Component for MyComponent {
type Message = ();
type Properties = ();
fn create(_: Self::Properties, _: ComponentLink<Self>) -> Self {
MyComponent {
worker: MyWorker::dispatcher()
}
}
fn update(&mut self, _: Self::Message) -> ShouldRender {
false
}
fn view(&self) -> Html {
// 从 worker 中创建回调来在另一个上下文中处理它
let click_callback = self.worker.callback(|_: ClickEvent| WorkerMsg::Process);
html! {
<button onclick={click_callback}>
{ "Click me!" }
</button>
}
}
}
```
<!--其他情况-->
```rust
struct MyComponent;
impl Component for MyComponent {
type Message = ();
type Properties = ();
fn create(_: Self::Properties, _: ComponentLink<Self>) -> Self {
MyComponent
}
fn update(&mut self, _: Self::Message) -> ShouldRender {
false
}
fn view(&self) -> Html {
// 创建一个短暂的回调
let click_callback = Callback::from(|| {
ConsoleService::log("clicked!");
});
html! {
<button onclick={click_callback}>
{ "Click me!" }
</button>
}
}
}
```
<!--END_DOCUSAURUS_CODE_TABS-->

View File

@ -0,0 +1,14 @@
---
description: 用于生成 HTML 和 SVG 的宏程序
slug: /concepts/html
---
# 使用 html! 宏
`html!` 宏允许你为组件编写声明式的 HTML 和 SVG。如果你使用过 React 的 JSX将会感觉到非常熟悉。
**重要提示**
1. `html!` 宏调用中只能有一个根节点
2. 空的 `html! {}` 宏调用是有效的但不会渲染任何内容
3. 常量必须始终被引号括起来并被包含在大括号里:`html! { "Hello, World" }`

View File

@ -0,0 +1,57 @@
# 列表
## Fragments
`html!` 宏总是要求一个单一的根节点。为了绕开这个限制,把内容包裹在一个空标签内是有效的:
<!--DOCUSAURUS_CODE_TABS-->
<!--有效-->
```rust
html! {
<>
<div></div>
<p></p>
</>
}
```
<!--无效-->
```rust
/* 错误:只允许一个 html 根元素 */
html! {
<div></div>
<p></p>
}
```
<!--END_DOCUSAURUS_CODE_TABS-->
## 迭代器
Yew 支持两种从迭代器构建 html 的语法:
<!--DOCUSAURUS_CODE_TABS-->
<!--语法类型 1-->
```rust
html! {
<ul class="item-list">
{ self.props.items.iter().map(renderItem).collect::<Html>() }
</ul>
}
```
<!--语法类型 2-->
```rust
html! {
<ul class="item-list">
{ for self.props.items.iter().map(renderItem) }
</ul>
}
```
<!--END_DOCUSAURUS_CODE_TABS-->

View File

@ -0,0 +1,57 @@
# 常量和表达式
## 常量
如果一个表达式的类型本身实现了 `Display` (一个标准库中的 Trait他们将会被转化成字符串并且作为一个 [Text](https://developer.mozilla.org/en-US/docs/Web/API/Text) 节点插入 DOM 中。
所有的需要显示的文本必须被 `{}` 块包含,因为这些文本会被当做一个 Rust 表达式来处理。这一点上Yew 中使用 HTML 的方式和正常 HTML 语法有巨大的区别。
```rust
let text = "lorem ipsum";
html!{
<>
<div>{text}</div>
<div>{"dolor sit"}</div>
<span>{42}</span>
</>
}
```
## 表达式
你可以在 HTML 中使用 `{}` 块来插入 Rust 表达式,只要这些表达式最终可以被解析成 `Html`
```rust
html! {
<div>
{
if show_link {
html! {
<a href="https://example.com">{"Link"}</a>
}
} else {
html! {}
}
}
</div>
}
```
通常我们会把这些表达式写进函数或者闭包中来增加可读性:
```rust
let show_link = true;
let maybe_display_link = move || -> Html {
if show_link {
html! {
<a href="https://example.com">{"Link"}</a>
}
} else {
html! {}
}
};
html! {
<div>{maybe_display_link()}</div>
}
```

View File

@ -0,0 +1,45 @@
---
description: Yew 的官方 Router
---
# Router
[https://crates.io/crates/yew-router](https://crates.io/crates/yew-router)
Routers 在单页应用SPA中根据 URL 的不同显示不同的页面。当点击一个链接时Router 在本地设置 URL 以指向应用程序中有效的路由,而不是默认请求一个不同的远程资源。然后 Router 检测到此更改后决定要渲染的内容。
## 核心元素
### Route
包含一个字符串,该字符串表示网址中域名之后的所有内容,还可以选择表示存储在 history api 中的状态。
### RouteService
与浏览器通信以获取和设置路由。
### RouteAgent
拥有一个 RouteService并用于当路由改变时协调更新无论更新是来自应用程序自身逻辑还是来自浏览器触发的事件。
### Switch
`Switch` trait 用于在该 trait 的实现者之间转换 `Route`。
### Router
Router 组件同 `RouterAgent` 进行通信,并将自动把它从 Agent 那里获得的 Routes 解析为 Switches并通过 `render` 属性暴露该 Switch该属性允许指定将生成的 Switch 转换为 `HTML` 的方式。
## 如何使用 Router
首先,你要创建一个表征你的应用程序所有状态的类型。请注意,虽然这通常是一个枚举,但也支持结构体,并且你可以在内部嵌套实现了 `Switch` trait 的其他项。
然后你应该为了你创建的类型派生 `Switch`。对于枚举,每一个成员都必须用 `#[to = "/some/route"]` 进行标注,如果你使用结构体,则标注必须出现在结构体声明之外。
请注意,由派生宏为 `Switch` 生成的实现,将尝试从头到尾依次创建每个成员,因此,如果任何路由可能与你指定的两个 `to` 标注相匹配,那么第一个会被匹配,第二个将永远不会被尝试。
你还可以在 `#[to = ""]` 标注中使用 `{}` 的变体来捕获片段。`{}` 表示捕获文本直到下一个分隔符(根据上下文可能是"/""?""&" 或 "\#")。`{*}` 表示捕获文本直到后续字符匹配为止,如果不存在任何字符,则它将匹配任何内容。`{<number>}` 表示捕获文本直到遇到指定数目的分隔符为止(例如:`{2}` 将一直捕获文本直到遇到两个分隔符为止)。
对于具有命名字段的结构体和枚举,你必须在捕获组中指定字段的名称,例如:`{user_name}` 或 `{*:age}`。
Switch trait 适用于比字符串更结构化的捕获组。你可以指定实现了 `Switch` trait 的任何类型。因此,你可以指定捕获组为 `usize`,并且如果 URL 的捕获部分无法转换为它,则该成员不会被匹配。

View File

@ -0,0 +1,87 @@
# 第一个简单的 App
首先创建一个二进制项目:
```bash
cargo new --bin yew-app && cd yew-app
```
添加 `yew` 到你的依赖库中([这里](https://docs.rs/yew) 可以查看最新版本的 Yew
{% code title="Cargo.toml" %}
```text
[package]
name = "yew-app"
version = "0.1.0"
authors = ["Yew App Developer <name@example.com>"]
edition = "2018"
[dependencies]
yew = { version = "0.14.3", features = ["std_web"] }
```
{% endcode %}
将这份代码复制到你的 `src/main.rs` 文件中:
{% code title="src/main.rs" %}
```rust
use yew::prelude::*;
struct Model {
link: ComponentLink<Self>,
value: i64,
}
enum Msg {
AddOne,
}
impl Component for Model {
type Message = Msg;
type Properties = ();
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
Self {
link,
value: 0,
}
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
match msg {
Msg::AddOne => self.value += 1
}
true // 指示组件应该重新渲染
}
fn view(&self) -> Html {
html! {
<div>
<button onclick={self.link.callback(|_| Msg::AddOne)}>{ "+1" }</button>
<p>{ self.value }</p>
</div>
}
}
}
fn main() {
yew::initialize();
App::<Model>::new().mount_to_body();
}
```
{% endcode %}
这份代码将构建你的称为 `Model` 的 `Component` 根组件,它会显示一个按钮,当你点击它时,`Model` 将会更新自己的状态。特别注意 `main()` 中的 `App::<Model>::new().mount_to_body()`,它会启动你的应用并将其挂载到页面的 `<body>` 标签中。如果你想使用任何动态属性来启动应用程序,则可以使用 `App::<Model>::new().mount_to_body_with_props(..)`。
## 运行你的应用程序!
启动并运行你的应用的最快方式就是使用 [`cargo-web`](https://github.com/koute/cargo-web)。如果你还没有的话,请用 `cargo install cargo-web` 命令来安装这个工具然后通过运行下述命令来构建和启动一个开发服务器:
```bash
cargo web start
```
`cargo-web` 将会自动为你添加 `wasm32-unknown-unknown` 作为目标代码,然后构建你的应用,你的应用将默认在 [http://\[::1\]:8000](http://[::1]:8000) 被访问。可以通过 `cargo web start --help` 命令来获取更多选项和帮助。

View File

@ -0,0 +1,11 @@
# 通过例子学习
Yew 的 github 项目中就包含了各种各样的示例(这些项目在不同程度的维护中)。我们建议仔细地学习它们, 了解如何使用不同的框架特性. 我们在书中有纰漏和错误的时候也欢迎 pull-requests 和提交 issues ♥️
- [**Todo App代办事项\(stdweb\)**](https://github.com/yewstack/yew/tree/v0.14.0/examples/std_web/todomvc)
- [**Todo App代办事项\(web_sys\)**](https://github.com/yewstack/yew/tree/v0.14.0/examples/web_sys/todomvc)
- [**Custom Components自定义 Component 组件)**](https://github.com/yewstack/yew/tree/v0.14.0/examples/custom_components)
- [**Multi-threading \(Agents\)(多线程 Agents\(stdweb\)**](https://github.com/yewstack/yew/tree/v0.14.0/examples/std_web/multi_thread)
- [**Multi-threading \(Agents\)(多线程 Agents\(web_sys\)**](https://github.com/yewstack/yew/tree/v0.14.0/examples/web_sys/multi_thread)
- [**Timer Service计时器**](https://github.com/yewstack/yew/tree/v0.14.0/examples/timer)
- [**Nested Components嵌套 Component 组件)**](https://github.com/yewstack/yew/tree/v0.14.0/examples/nested_list)

View File

@ -0,0 +1,5 @@
# CSS
&lt;TODO&gt;
对适当的 CSS 支持的提案可以在这里找到:[https://github.com/yewstack/yew/issues/533](https://github.com/yewstack/yew/issues/533)

View File

@ -0,0 +1,50 @@
# Debugging
## Panics
Please use the [`console_error_panic`](https://github.com/rustwasm/console_error_panic_hook) crate for nicer stacktraces with Rust symbols. Note, that it is not compatible with apps built with `cargo-web`.
## Console Logging
In general, Wasm web apps are able to interact with Browser APIs, and the `console.log` api is no exception. There are a few options available:
### [`wasm-logger`](https://crates.io/crates/wasm-logger)
This crate integrates with the familiar Rust `log` crate:
```rust
// setup
fn main() {
wasm_logger::init(wasm_logger::Config::default());
}
// usage
log::info!("Update: {:?}", msg);
```
### **\`\`**[**`ConsoleService`**](https://docs.rs/yew/0.13.2/yew/services/console/struct.ConsoleService.html)**\`\`**
This service is included within yew and is available when the `"services"` feature is enabled:
```rust
// usage
ConsoleService::info(format!("Update: {:?}", msg).as_ref());
```
## Source Maps
There is currently no first-class support for source maps for Rust / Wasm web apps. This, of course, is subject to change. If this is no longer true or if progress is made, please suggest a change!
### Latest Info
\[Dec 2019\] [Chrome DevTools update](https://developers.google.com/web/updates/2019/12/webassembly#the_future)
> There is still quite a bit of work to do though. For example, on the tooling side, Emscripten \(Binaryen\) and wasm-pack \(wasm-bindgen\) dont support updating DWARF information on transformations they perform yet.
\[2020\] [Rust Wasm debugging guide](https://rustwasm.github.io/book/reference/debugging.html#using-a-debugger)
> Unfortunately, the debugging story for WebAssembly is still immature. On most Unix systems, [DWARF](http://dwarfstd.org/) is used to encode the information that a debugger needs to provide source-level inspection of a running program. There is an alternative format that encodes similar information on Windows. Currently, there is no equivalent for WebAssembly.
\[2019\] [Rust Wasm roadmap](https://rustwasm.github.io/rfcs/007-2019-roadmap.html#debugging)
> Debugging is tricky because much of the story is out of this working group's hands, and depends on both the WebAssembly standardization bodies and the folks implementing browser developer tools instead.

View File

@ -0,0 +1,39 @@
---
description: Yew 框架规划功能的路线图
---
# 路线图
## `v1.0.0`
### 规划中的功能
- 标记 key 的列表项:[https://github.com/yewstack/yew/issues/479](https://github.com/yewstack/yew/issues/479)
- 路由:[https://github.com/yewstack/yew_router](https://github.com/yewstack/yew_router)
### 生产环境准备
- 浏览器兼容性
- 提高 Yew 框架的测试覆盖率
- 增加性能基准测试:[https://github.com/yewstack/yew/issues/5](https://github.com/yewstack/yew/issues/5)
### 指南
- 最佳实践:[https://yew.rs/optimizations](https://yew.rs/optimizations)
- 端到端教程
- Futures / 并发
- CSS / 样式
- 测试
- 状态管理
## 未来
### 潜在功能
- 服务端渲染:[https://github.com/yewstack/yew/issues/41](https://github.com/yewstack/yew/issues/41)
- 组件库:[https://github.com/yewstrap/yewstrap](https://github.com/yewstrap/yewstrap)
- 代码分割:[https://github.com/yewstack/yew/issues/599](https://github.com/yewstack/yew/issues/599)
- 允许不同的虚拟 DOM 后端:[https://github.com/yewstack/yew/issues/482](https://github.com/yewstack/yew/issues/482)
- 反思 Services[https://github.com/yewstack/yew/issues/364](https://github.com/yewstack/yew/issues/364)
- 成熟的工具包:[https://github.com/yewstack/yewtil](https://github.com/yewstack/yewtil)
- HTML 模板备选方案:[https://github.com/yewstack/yew/issues/438](https://github.com/yewstack/yew/issues/438)

View File

@ -0,0 +1,11 @@
---
description: 测试你的应用程序
---
# 测试
&lt;TODO&gt;
## Rust WebDriving
使用 Rust 以编程方式驱动 UI 集成测试,[fantoccini](https://crates.io/crates/fantoccini) 是一个推荐的选择。它允许你通过使用 CSS 选择器来查找特定的元素,然后对它们执行特定的操作,例如输入文本,点击按钮,或等待特定时间以使客户端代码执行(例如等待一个网络请求完成并导致 UI 改变),来测试你的网站。

View File

@ -0,0 +1,86 @@
{
"version.label": {
"message": "0.20",
"description": "The label for version 0.20"
},
"sidebar.docs.category.Getting Started": {
"message": "Getting Started",
"description": "The label for category Getting Started in sidebar docs"
},
"sidebar.docs.category.Concepts": {
"message": "Concepts",
"description": "The label for category Concepts in sidebar docs"
},
"sidebar.docs.category.Concepts.link.generated-index.title": {
"message": "Yew concepts",
"description": "The generated-index page title for category Concepts in sidebar docs"
},
"sidebar.docs.category.Concepts.link.generated-index.description": {
"message": "Learn about the important Yew concepts!",
"description": "The generated-index page description for category Concepts in sidebar docs"
},
"sidebar.docs.category.Using Basic Web Technologies In Yew": {
"message": "Using Basic Web Technologies In Yew",
"description": "The label for category Using Basic Web Technologies In Yew in sidebar docs"
},
"sidebar.docs.category.Using Basic Web Technologies In Yew.link.generated-index.title": {
"message": "Yew's Take on Basic Web Technologies",
"description": "The generated-index page title for category Using Basic Web Technologies In Yew in sidebar docs"
},
"sidebar.docs.category.Using Basic Web Technologies In Yew.link.generated-index.description": {
"message": "Yew centrally operates on the idea of keeping everything that a reusable piece of UI may needin one place - rust files, while also keeping the underlying technology accessible where necessary. Explore further to fully grasp what we mean by these statements:",
"description": "The generated-index page description for category Using Basic Web Technologies In Yew in sidebar docs"
},
"sidebar.docs.category.Components": {
"message": "Components",
"description": "The label for category Components in sidebar docs"
},
"sidebar.docs.category.Hooks": {
"message": "Hooks",
"description": "The label for category Hooks in sidebar docs"
},
"sidebar.docs.category.HTML": {
"message": "HTML",
"description": "The label for category HTML in sidebar docs"
},
"sidebar.docs.category.Advanced topics": {
"message": "Advanced topics",
"description": "The label for category Advanced topics in sidebar docs"
},
"sidebar.docs.category.Advanced topics.link.generated-index.title": {
"message": "Advanced topics",
"description": "The generated-index page title for category Advanced topics in sidebar docs"
},
"sidebar.docs.category.Advanced topics.link.generated-index.description": {
"message": "Learn about the advanced topics and inner workings of Yew!",
"description": "The generated-index page description for category Advanced topics in sidebar docs"
},
"sidebar.docs.category.Struct Components": {
"message": "Struct Components",
"description": "The label for category Struct Components in sidebar docs"
},
"sidebar.docs.category.More": {
"message": "More",
"description": "The label for category More in sidebar docs"
},
"sidebar.docs.category.More.link.generated-index.title": {
"message": "Miscellaneous",
"description": "The generated-index page title for category More in sidebar docs"
},
"sidebar.docs.category.Migration guides": {
"message": "Migration guides",
"description": "The label for category Migration guides in sidebar docs"
},
"sidebar.docs.category.yew": {
"message": "yew",
"description": "The label for category yew in sidebar docs"
},
"sidebar.docs.category.yew-agent": {
"message": "yew-agent",
"description": "The label for category yew-agent in sidebar docs"
},
"sidebar.docs.category.yew-router": {
"message": "yew-router",
"description": "The label for category yew-router in sidebar docs"
}
}

View File

@ -0,0 +1,7 @@
---
description: 關於框架的底層細節
---
# 內部底層的 library
元件生命周期的狀態機與 vdom diff 演算法

View File

@ -0,0 +1,118 @@
---
description: 加速你的專案
---
# 優化與最佳實例
## neq_assign
當元件從父元件接收到屬性時, `change` 的方法就會被呼叫。除了讓你更新元件的狀態,也讓你回傳,決定元件是否要在屬性改變時,重新渲染自己的布林值 `ShouldRender` 。
重新渲染是很浪費效能的,儘可能避免這麼做。一般來說,只有在屬性真的改變時,才重新渲染。下面的程式碼是體現這個原則的例子,當屬性改變時,才回傳 `true`
```rust
use yew::ShouldRender;
#[derive(PartialEq)]
struct ExampleProps;
struct Example {
props: ExampleProps,
};
impl Example {
fn change(&mut self, props: ExampleProps) -> ShouldRender {
if self.props != props {
self.props = props;
true
} else {
false
}
}
}
```
但我們可以走的更遠!這六行的模板,使用一個 trait 和一個 實作了 `PartialEq` 的 blanket implementation ,可以被縮短至一行。請參考[這裡](https://docs.rs/yewtil/*/yewtil/trait.NeqAssign.html) `yewtil` 的 crate 裡的 `NeqAssign` trait。
## RC
為了避免重新渲染時,複製大量的資料來建立屬性,我們可以使用智慧指針來讓程式只複製指針。如果你使用 `Rc<_>` 來封裝你的屬性,而不是未封裝的值,你可以使用 `Rc::make_mut`,去複製與存取你想要改變的資料的可變參考,這做到了延遲複製,直到你需要更動子元件的資料。透過避免複製直到有值改變,子元件可以在 `Component::change` 拒絕與他狀態中的屬性相同值的屬性,而且這樣不會有任何效能成本。另外,這個方式,資料必須在與子元件比較與被拒絕之前,被複製進父元件的屬性中。
這個優化最那些無法 `Copy` 的資料型別最有用。如果你可以輕易複製你的資料,那把資料放進智慧指針裡面似乎就沒有這麼值得。對於那些包含很多像是 `Vec` 、 `HashMap` 與 `String` 的結構,這個優化對他們會更值得。
如果子元件幾乎不會更新值,那這個優化效果會很好,甚至如果父元件也很少更新,那效果會更好。上面的情況,使在純元件中使用 `Rc<_>s` 是一個封裝屬性值很好的選擇。
## View 方法
出於程式碼的可讀性,通常會寫方法包裝複雜的 `html!`,這樣你可以避免巢狀的 HTML 造成過多的向右縮排。
## 純元件/函數式元件
純元件 是一種不會改變自己狀態的元件,他們只單純顯示內容或是向普通可變的元件傳送訊息。他們和 view 方法不同的地方在於們可以在 `html!` 巨集中使用,語法會像(`<SomePureComponent />`),而不是表達式語法(`{some_view_function()}`),而且根據他們的實作方式,他們可以被 memoized這樣可以套用前面所述的 `neq_assign` 的邏輯避免重新渲染。
Yew 本身不支援純元件或是函數式元件,但是你可以透過 external crates 使用。
函數式元件還不存在,但是理論上純元件可以透過巨集與宣告方法產生。
## Keyed DOM nodes when they arrive
## 使用 Cargo Workspaces 加速編譯
Yew 最大的缺點就是花太多時間在編譯上了。編譯時間似乎和 `html!` 巨集中的程式碼質量相同。 對於小專案來說,這應該不是什麼大問題,但是對於有很多頁面的大型網頁應用程式來說,就必須要將程式碼封裝成很多 crates 以減少編譯所花的時間。
你應該將路由與頁面區塊封裝成一個 main crate然後將共用的程式碼與元件封裝成另一個 crate將每個頁面會用到的不同的元件各自封裝到不同的 crate 中,或是只產生 `Html` 的大方法中。最好的狀況,你只需要重新編譯你 main crate 與修改的頁面的 crate 的程式碼;而最壞的情況,你編輯了共用的 crate你就必須重新編譯所有依賴這個共用 crate 的程式碼。
如果你的 main crate 太過龐大,或是你希望快速迭代深層巢狀的頁面(一個頁面渲染另一個頁面的頂層),你可以使用範例的 crate ,在一個簡單的主頁面上編輯你未完成的元件。
## 編譯大小的優化 <a id="build-size-optimization"></a>
- 優化 Rust 的程式碼
- `cargo.toml` (定義釋出的設定檔)
- 使用 `wasm-opt` 優化 wasm 程式碼
更多關於程式碼大小的資訊,請參考: [rustwasm book](https://rustwasm.github.io/book/reference/code-size.html#optimizing-builds-for-code-size)
### Cargo.toml <a id="cargo-toml"></a>
你可以設定你的發行版本更小的檔案大小,透過設定 `Cargo.toml` 的 `[profile.release]` 。
[Rust profiles documentation](https://doc.rust-lang.org/cargo/reference/profiles.html)
```rust
[profile.release]
# less code to include into binary
panic = 'abort'
# optimization over all codebase ( better optimization, slower build )
codegen-units = 1
# optimization for size ( more aggresive )
opt-level = 'z'
# optimization for size
# opt-level = 's'
# link time optimization using using whole-program analysis
lto = true
```
### wasm-opt <a id="wasm-opt"></a>
更多優化 `wasm` 程式碼大小的方法。
wasm-opt 資訊: [binaryen project](https://github.com/WebAssembly/binaryen)
Rust Wasm 中有一個關於減少 Wasm 二進位檔大小的章節:[Shrinking .wasm size](https://rustwasm.github.io/book/game-of-life/code-size.html)
- 使用`wasm-pack` 預設在發行版本編譯時優化 `wasm` 程式碼
- 直接在 wasm 檔案上使用 `wasm-opt` 。
```rust
wasm-opt wasm_bg.wasm -Os -o wasm_bg_opt.wasm
```
#### 編譯 yew/examples/ 中 最小的例子 <a id="build-size-of-minimal-example-in-yew-examples"></a>
注意: `wasm-pack` 包含對 Rust 與 wasm 程式碼的優化。而`wasm-bindgen` 只是一個單純的例子,沒有對 `Rust` 做任何優化。
| used tool | size |
| :-------------------------- | :---- |
| wasm-bindgen | 158KB |
| wasm-binggen + wasm-opt -Os | 116KB |
| wasm-pack | 99KB |

View File

@ -0,0 +1,33 @@
---
description: ComponentLink 與 Callbacks.
---
# Callbacks
元件的「link」是一個讓元件註冊 callbacks 並自我更新的機制。
## ComponentLink API
### callback
註冊一個 callback 後,當這個 callback 被執行時,會發送一個訊息給元件的更新機制。在生命周期的勾子下,他會呼叫 `send_self` 並將被閉包回傳的訊息帶給他。
提供一個 `Fn(IN) -> Vec<COMP::Message>` 並回傳一個 `Callback<IN>` 。
### send_message
當現在的迴圈結束後,向元件發送訊息,並且開啟另一個迴圈。
### send_message_batch
註冊一個 callback當這個 callback 被執行時,這個 callback 會一次送很多訊息。如果有任何一個訊息導致元件被重新渲染,元件會在所有批次送來的訊息都被處理完後,再重新渲染。
提供一個 `Fn(IN) -> COMP::Message` 並回傳一個 `Callback<IN>` 。
## Callbacks
_他可能需要一個獨立的短頁來介紹_
Callbacks 被用來當作 services 、 agents 與父元件跟 Yew 溝通的方式。他們只是一個被 `Rc` 包裹著的 `Fn`,好讓他們可以被複製。
他們有一個 `emit` 方法,這個方法拿他們的 `<IN>` 型別當作參數,並且轉換他作為目的地所期望的訊息。如果一個從父元件來的 callback 被提供作為子元件的屬性,子元件可以在他的 update 生命周期中,呼叫 callback 中的 emit 以傳遞訊息回給父元件。 在 `html!` 巨集中的閉包與方法如果被當作屬性傳遞,會被自動轉為 Callbacks。

View File

@ -0,0 +1,175 @@
---
description: 元件,以及生命周期鉤子
---
# 元件
## 什麼是元件?
元件是 Yew 的基石。他們管理自己的狀態,可以渲染自己成為 DOM。元件可以透過實作描述元件生命周期的 `Component` trait 來建立。
## 生命周期
:::note
`歡迎來貢獻我們的文件:` [Add a diagram of the component lifecycle](https://github.com/yewstack/docs/issues/22)
:::
## 生命周期的方法
### Create
當一個元件被建立,他會接收從父元件,也就是 `ComponentLink` ,傳下來的屬性。 這些屬性用來初始化元件的狀態此外「link」可以用來註冊回調函式或傳訊息給元件。
通常,你的元件 struct 會儲存 props 與 link就像下面的例子
```rust
pub struct MyComponent {
props: Props,
link: ComponentLink<Self>,
}
impl Component for MyComponent {
type Properties = Props;
// ...
fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
MyComponent { props, link }
}
// ...
}
```
### View
元件會在 `view()` 方法中宣告佈局。Yew 提供 `html!` 巨集來宣告 HTML 合 SVG 的結點,包含他們的監聽事件與子結點。這個巨集扮演像是 React 的 JSX 的角色,但是是使用 Rust 的表達式,而不是 JavaScript 的。
```rust
impl Component for MyComponent {
// ...
fn view(&self) -> Html {
let onclick = self.link.callback(|_| Msg::Click);
html! {
<button {onclick}>{ self.props.button_text }</button>
}
}
}
```
更多使用細節,請參考 [`html!` 教學](concepts/html/introduction.mdx)。
### Rendered
`rendered()` 生命周期的方法會,在 `view()` 處理完並且 Yew 渲染完你的元件之後,與瀏覽器刷新頁面之前,被呼叫。一個元件可能希望實作這個方法,去執行只能在元件被渲染完元素才能做的事情。 你可以透過 `first_render` 變數來確認這個元件是不是第一次被渲染。
```rust
use stdweb::web::html_element::InputElement;
use stdweb::web::IHtmlElement;
use yew::prelude::*;
pub struct MyComponent {
node_ref: NodeRef,
}
impl Component for MyComponent {
// ...
fn view(&self) -> Html {
html! {
<input ref={self.node_ref.clone()} type="text" />
}
}
fn rendered(&mut self, first_render: bool) {
if first_render {
if let Some(input) = self.node_ref.cast::<InputElement>() {
input.focus();
}
}
}
}
```
:::note
注意,這個生命周期方法,不是一定要被實作,預設的行為是不做任何事情。
:::
### Update
元件是可動態更新且可以註冊接收非同步的訊息。 `update()` 生命周期方法會被每個訊息呼叫。他基於訊息是什麼,來允許元件更新自己,且會決定是否需要重新渲染。 訊息可以被 HTML 元素的監聽器觸發或被子元件、Agents、Services 或 Futures 傳送。
`update()` 應用範例:
```rust
pub enum Msg {
SetInputEnabled(bool)
}
impl Component for MyComponent {
type Message = Msg;
// ...
fn update(&mut self, msg: Self::Message) -> ShouldRender {
match msg {
Msg::SetInputEnabled(enabled) => {
if self.input_enabled != enabled {
self.input_enabled = enabled;
true // 重新渲染
} else {
false
}
}
}
}
}
```
### Change
元件可能會被他的父元件重新渲染。當他被父元件重新渲染時,他會收到新的屬性,然後決定要不要再渲染一次。 這設計是讓父元件透過便於跟子元件溝通。
一個簡單的實作方式像:
```rust
impl Component for MyComponent {
// ...
fn change(&mut self, props: Self::Properties) -> ShouldRender {
if self.props != props {
self.props = props;
true
} else {
false
}
}
}
```
### Destroy
當元件從 DOM 上被解除掛載Yew 會呼叫 `destroy()` 生命周期方法以提供任何需要清理的操作。這個方法是不一定要被實作的,預設不會做設任何事。
## 相關的型別
`Component` trait 有兩個相關的型別:`Message` 與 `Properties`。
```rust
impl Component for MyComponent {
type Message = Msg;
type Properties = Props;
// ...
}
```
`Message` 負責各式各樣的訊息,他可能被元件處理去觸發各種影響。舉例來說,你可能有一個 `Click` 的訊息,他會觸發 API 請求,或是切換 UI 元件的樣貌。下面是一個常見的實作,在你的元件模組中,創建一個叫作 `Msg` 的 enum然後把他當作元件裡的 Message 型別。通常 message 會縮寫成 msg。
```rust
enum Msg {
Click,
}
```
`Properties` 代表要從父員件傳遞到子元件的資訊。這個型別必須實作 `Properties` trait (通常會 deriving 他)並且可以決定某個屬性是必要的屬性,或是可選的屬性。這個型別會在創建元件的時候,或是更新元件的時候被使用到。常見的實作會在你的元件模組中,建立一個叫作 `Props` 的 struct然後把他當作元件的`Properties` 型別。通常 properties 或縮寫成 props。因為屬性是從父原件被傳下來的所以應用程式中的根元件的 `Properties` 原則上都是 `()`。如果你希望你的根元件有特定的屬性,可以使用 `App::mount_with_props` 的方法。

View File

@ -0,0 +1,70 @@
---
description: 父元件與子元件的溝通橋樑
---
# Properties
屬性讓子元件與父元件可以互相溝通。
## Derive macro
不要嘗試自己實作 `Properties`,而是用`#[derive(Properties)]`derive 他。
### 必填的欄位
預設所有在 `Properties` struct 裡的欄位都是必填的。當必填的欄位沒有值,而元件在 `html!` 巨集中又被建立,編譯器就會報錯。如果希望欄位是可選的,可以使用 `#[prop_or_default]` 來讓該欄位有預設值。如果希望欄位預設特定值,可以使用 `#[prop_or_else(value)]` ,裡面的 value 就會是這個欄位的預設值。舉例來說,希望預設值是 `true`可以在欄位宣告上面這樣寫 `#[prop_or_else(true)]`. 通常可選的屬性,會用 `Option` ,且預設值為`None`。
### PartialEq
如果可以,最好在你的屬性上面 derive `PartialEq` 。他可以避免畫面多餘的渲染,更細節的內容請參考,**優化與最佳實例**的區塊。
## 屬性的記憶體與速度的開銷
在 `Component::view`,裡,你可以拿到元件狀態的參考,且用他來建立 `Html` 。但是屬性是有所有權的。這代表著為了建立屬性,並且將他們傳遞給子元件,我們必須取得被 `view` 方法拿走的所有權。 當將參考傳給元件時,可以透過隱式的複製來做到得到所有權。
這意味著,每個元件,都有從父元件傳遞下來的獨有的狀態複本,且每當你重新渲染一次元件,被重新渲染的元件的所有的子元件的屬性就會被重新複製一次。
代表如果你要在屬性中傳遞*大量*的資料(大於 10 KB 的字串之類的),你可能需要考慮將你的子元件變成一個回傳 `Html` 的方法,讓父元件呼叫,以避免資料被複製。
如果你不需要改變傳下去的資料,你可以用 `Rc` 將資料包裝起來,這樣就會只複製參考的指針,而不是資料本身。
## 範例
```rust
use std::rc::Rc;
use yew::Properties;
#[derive(Clone, PartialEq)]
pub enum LinkColor {
Blue,
Red,
Green,
Black,
Purple,
}
impl Default for LinkColor {
fn default() -> Self {
// 除非有指定,否則預設是藍色
LinkColor::Blue
}
}
#[derive(Properties, Clone, PartialEq)]
pub struct LinkProps {
/// 連結必須要有一個目標
href: String,
/// 如果連結文字很大,複製字串的參考可以減少記憶體的開銷
/// 但除非效能已經成為嚴重的問題,否則通常不建議這麼做
text: Rc<String>,
/// 連結的顏色
#[prop_or_default]
color: LinkColor,
/// 如果為 None那 view 方法將不會指定 size
#[prop_or_default]
size: Option<u32>,
/// 當沒有指定 active預設為 true
#[prop_or(true)]
active: bool,
}
```

View File

@ -0,0 +1,26 @@
---
description: 外帶 DOM 的存取
---
# Refs
## Refs
`ref` 關鍵字可以被使用在任何 HTML 的元素或是元件,用來得到那個物件附加的 DOM `Element`。這個可以在 view 生命周期方法之外,改變 DOM。
對於要存取 canvas 元素,或滾動到頁面不同的區塊,很有幫助。
語法可以這樣使用:
```rust
// 建立
self.node_ref = NodeRef::default();
// 在 view 裡
html! {
<div ref={self.node_ref.clone()}></div>
}
// 更新
let has_attributes = self.node_ref.cast::<Element>().unwrap().has_attributes();
```

View File

@ -0,0 +1,41 @@
---
description: Yew 的 Actor 系統
---
# Agents
Agents 類似於 Angular 的 [Services](https://angular.io/guide/architecture-services) (但沒有依賴注入)而且提供 Tew 一個 [Actor Model](https://en.wikipedia.org/wiki/Actor_model). Agents 可以用來作為兩個元件間的路由訊息,而且與他們在元件間的層級關係獨立出來,所以他也可以用來作為一個全域的狀態,甚至可以用來減輕用來渲染 UI 畫面的主執行緒的大量運算任務。 未來,我們還規劃要讓 agents 幫忙 Yew 專案可以跨頁籤溝通。
為了讓 agents 可以並行, Yew 使用了 [web-workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers)。
## 生命周期
![Agent 生命周期](https://user-images.githubusercontent.com/42674621/79125224-b6481d80-7d95-11ea-8e6a-ab9b52d1d8ac.png)
## Agents 的型別
#### 範圍
- Job - 在 UI 執行緒上,為每一個 bridge新增一個 agent。這對於將「共享但獨立的行為」移出元件很有用。待驗證當工作結束agent 會消失。
- Context - Bridges 會建立並連接上 UI 執行緒上的 agent。這可以用來協調元件與其他 agent 之間的狀態。當沒有任何 bridge 連接上這個 agent這個 agnet 就會消失。
- Private - 與 Job 相同,但是是在自己的 web worker 上執行的。
- Public - 與 Context 相同,但是是在自己的 web worker 上執行的。
- Global (編寫中)
## 在 Agents 與元件之間溝通
### Bridges
bridge 允許 agent 與元件進行雙向的溝通。bridge 也允許 agents 之間互相溝通。
### Dispatchers
dispatcher 允許元件與 agnet 進行單向的溝通。dispatcher 也允許元件向 agnet 發送訊息。
## 開銷
Agents 透過使用 [bincode](https://github.com/servo/bincode) 序列化他們的訊息,來溝通。所以比起呼叫方法,他的效能花費比較高。除非計算的成本,或是跨元件計算的成本,比傳遞訊息的成本要高,否則 agnet 的方法儘量只有包含單純的邏輯運算。
## 延伸閱讀
- [web_worker_fib](https://github.com/yewstack/yew/tree/master/examples/web_worker_fib) 範例顯示了如何在 agnets 之間溝通。

View File

@ -0,0 +1,114 @@
---
description: 建立複雜元件層級與佈局
---
# Components
## 基本
任何實作 `Component` 的型別,都可以在 `html!` 的巨集中使用:
```rust
html!{
<>
// 沒有屬性
<MyComponent />
// 有屬性
<MyComponent prop1="lorem" prop2="ipsum" />
// 一次提供很多屬性
<MyComponent ..props />
</>
}
```
## 巢狀
只要元件的 `Properties` 中有 `children`,就可以傳遞子結點給元件。
{% code title="parent.rs" %}
```rust
html! {
<Container>
<h4>{ "Hi" }</h4>
<div>{ "Hello" }</div>
</Container>
}
```
{% endcode %}
{% code title="container.rs" %}
```rust
pub struct Container(Props);
#[derive(Properties)]
pub struct Props {
pub children: Children,
}
impl Component for Container {
type Properties = Props;
// ...
fn view(&self) -> Html {
html! {
<div id="container">
{ self.0.children.clone() }
</div>
}
}
}
```
{% endcode %}
## 指定子結點的型別
如果指定了子結點的型別,就可以使用或改變巢狀元件的屬性。下面的範例就是, `List` 元件包裹 `ListItem` 元件。另一個真實的範例是 `yew-router` 的原始碼。還有一個更進階的範例,請參考 Yew GitHub repo 中的 `nested-list` 範例。
{% code title="parent.rs" %}
```rust
html! {
<List>
<ListItem value="a" />
<ListItem value="b" />
<ListItem value="c" />
</List>
}
```
{% endcode %}
{% code title="list.rs" %}
```rust
pub struct List(Props);
#[derive(Properties)]
pub struct Props {
pub children: ChildrenWithProps<ListItem>,
}
impl Component for List {
type Properties = Props;
// ...
fn view(&self) -> Html {
html!{{
for self.0.children.iter().map(|mut item| {
item.props.value = format!("item-{}", item.props.value);
item
})
}}
}
}
```
{% endcode %}

View File

@ -0,0 +1,264 @@
---
description: HTML 與 SVG 元件都支援
---
# Elements
## 標籤結構
元件標籤都必須要是自封閉的標籤 `<... />` 或是跟開啟標籤對應的關閉標籤。
<!--DOCUSAURUS_CODE_TABS-->
<!--Open - Close-->
```rust
html! {
<div id="my_div"></div>
}
```
<!--INVALID-->
```rust
html! {
<div id="my_div"> // <- 缺少關閉標籤
}
```
<!--Self-Closing-->
```rust
html! {
<input id="my_input" />
}
```
<!--INVALID-->
```rust
html! {
<input id="my_input"> // <- 缺少自封閉標籤語法
}
```
<!--END_DOCUSAURUS_CODE_TABS-->
:::note
為了方便起見,通常需要關閉標籤的元件,也都可以用自封閉標籤表示。例如,寫 `html! { <div class="placeholder" /> }` 是合法的。
:::
## 子結點
輕鬆寫出複雜巢狀的 HTML 與 SVG 架構:
<!--DOCUSAURUS_CODE_TABS-->
<!--HTML-->
```rust
html! {
<div>
<div data-key="abc"></div>
<div class="parent">
<span class="child" value="anything"></span>
<label for="first-name">{ "First Name" }</label>
<input type="text" id="first-name" value="placeholder" />
<input type="checkbox" checked=true />
<textarea value="write a story" />
<select name="status">
<option selected=true disabled=false value="">{ "Selected" }</option>
<option selected=false disabled=true value="">{ "Unselected" }</option>
</select>
</div>
</div>
}
```
<!--SVG-->
```rust
html! {
<svg width="149" height="147" viewBox="0 0 149 147" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M60.5776 13.8268L51.8673 42.6431L77.7475 37.331L60.5776 13.8268Z" fill="#DEB819"/>
<path d="M108.361 94.9937L138.708 90.686L115.342 69.8642" stroke="black" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
<g filter="url(#filter0_d)">
<circle cx="75.3326" cy="73.4918" r="55" fill="#FDD630"/>
<circle cx="75.3326" cy="73.4918" r="52.5" stroke="black" stroke-width="5"/>
</g>
<circle cx="71" cy="99" r="5" fill="white" fill-opacity="0.75" stroke="black" stroke-width="3"/>
<defs>
<filter id="filter0_d" x="16.3326" y="18.4918" width="118" height="118" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="2"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
</filter>
</defs>
</svg>
}
```
<!--END_DOCUSAURUS_CODE_TABS-->
## Classes
你很多方便的選項可以寫元件裡的 class
<!--DOCUSAURUS_CODE_TABS-->
<!--Literal-->
```rust
html! {
<div class="container"></div>
}
```
<!--Multiple-->
```rust
html! {
<div class="container center-align"></div>
}
```
<!--Interpolated-->
```rust
html! {
<div class={format!("{}-container", size)}></div>
}
```
<!--Expression-->
```rust
html! {
<div class={self.classes()}></div>
}
```
<!--Tuple-->
```rust
html! {
<div class={("class-1", "class-2")}></div>
}
```
<!--Vector-->
```rust
html! {
<div class={vec!["class-1", "class-2"]}></div>
}
```
<!--END_DOCUSAURUS_CODE_TABS-->
## 監聽
監聽器的屬性必須要傳入一個 `Callback` 他封裝了閉包。callback 的內容取決於,當觸發監聽事件時,你希望應用程式有什麼反應:
<!--DOCUSAURUS_CODE_TABS-->
<!--Component Handler-->
```rust
struct MyComponent {
link: ComponentLink<Self>,
}
enum Msg {
Click,
}
impl Component for MyComponent {
type Message = Msg;
type Properties = ();
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
MyComponent { link }
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
match msg {
Msg::Click => {
// 處理點擊事件
}
}
}
fn view(&self) -> Html {
// 從一個元件連結中,建立一個 callback 並在元件中處理他
let click_callback = self.link.callback(|_: ClickEvent| Msg::Click);
html! {
<button onclick={click_callback}>
{ "Click me!" }
</button>
}
}
}
```
<!--Agent Handler-->
```rust
struct MyComponent {
worker: Dispatcher<MyWorker>,
}
impl Component for MyComponent {
type Message = ();
type Properties = ();
fn create(_: Self::Properties, _: ComponentLink<Self>) -> Self {
MyComponent {
worker: MyWorker::dispatcher()
}
}
fn update(&mut self, _: Self::Message) -> ShouldRender {
false
}
fn view(&self) -> Html {
// 從一個 worker 中建立一個 callback並在其他的 context 中處理他
let click_callback = self.worker.callback(|_: ClickEvent| WorkerMsg::Process);
html! {
<button onclick={click_callback}>
{ "Click me!" }
</button>
}
}
}
```
<!--Other Cases-->
```rust
struct MyComponent;
impl Component for MyComponent {
type Message = ();
type Properties = ();
fn create(_: Self::Properties, _: ComponentLink<Self>) -> Self {
MyComponent
}
fn update(&mut self, _: Self::Message) -> ShouldRender {
false
}
fn view(&self) -> Html {
// 建立一個臨時的 callback
let click_callback = Callback::from(|| {
ConsoleService::log("clicked!");
});
html! {
<button onclick={click_callback}>
{ "Click me!" }
</button>
}
}
}
```
<!--END_DOCUSAURUS_CODE_TABS-->

View File

@ -0,0 +1,14 @@
---
description: 用來產生 HTML 與 SVG 的巨集
slug: /concepts/html
---
# 使用 html!
`html!` 巨集可以讓你用 HTML 與 SVG 寫元件。如果你寫過 React 的 JSX一種 JavaScript 的擴展,可以讓你在 JavaScript 中寫 HTML應該會覺得這兩者十分相似。
**重要提示**
1. 在 `html!` 裡,只能有一個根結點(但你可以用 [Fragment 或是 Iterators](https://yew.rs/concepts/html/lists) 來繞過這個限制。)
2. 空的 `html! {}` 是合法的,且他不會渲染任何東西在畫面上
3. 字串必須被雙引號與大括號包裹住:`html! { "Hello, World" }`

View File

@ -0,0 +1,57 @@
# Lists
## Fragments
`html!` 巨集裡必須只有一個根結點。為了可以繞過這個限制,將兩個以上的結點,用空的標籤包裹起來,是合法的:
<!--DOCUSAURUS_CODE_TABS-->
<!--Valid-->
```rust
html! {
<>
<div></div>
<p></p>
</>
}
```
<!--Invalid-->
```rust
/* error: only one root html element allowed */
html! {
<div></div>
<p></p>
}
```
<!--END_DOCUSAURUS_CODE_TABS-->
## Iterators
Yew 支援兩種不同的方式,從 iterator 建構 html
<!--DOCUSAURUS_CODE_TABS-->
<!--Syntax Type 1-->
```rust
html! {
<ul class="item-list">
{ self.props.items.iter().map(renderItem).collect::<Html>() }
</ul>
}
```
<!--Syntax Type 2-->
```rust
html! {
<ul class="item-list">
{ for self.props.items.iter().map(renderItem) }
</ul>
}
```
<!--END_DOCUSAURUS_CODE_TABS-->

View File

@ -0,0 +1,57 @@
# Literals & Expressions
## Literals
如果表達式中的型別有實作 `Display` ,他們會被轉換成字串,並在 DOM 中作為 [Text](https://developer.mozilla.org/en-US/docs/Web/API/Text) (文字)結點。
所有的文字都必須用 `{}` 括起來,因為文字是被當作表達式處理。這是 HTML 語法與 Yew 的語法中,最大的不同。
```rust
let text = "lorem ipsum";
html!{
<>
<div>{text}</div>
<div>{"dolor sit"}</div>
<span>{42}</span>
</>
}
```
## Expressions
只要可以回傳 `Html`,你都可以在你的 HTML 中用 `{}` 插入表達式。
```rust
html! {
<div>
{
if show_link {
html! {
<a href="https://example.com">{"Link"}</a>
}
} else {
html! {}
}
}
</div>
}
```
通常把這些表達式與包裝成方法或閉包會比較好,可以提升可讀性:
```rust
let show_link = true;
let maybe_display_link = move || -> Html {
if show_link {
html! {
<a href="https://example.com">{"Link"}</a>
}
} else {
html! {}
}
};
html! {
<div>{maybe_display_link()}</div>
}
```

View File

@ -0,0 +1,81 @@
---
description: Yew 的官方路由器
---
# 路由器
[https://crates.io/crates/yew-router](https://crates.io/crates/yew-router)
單頁應用程式SPA中的路由器會依據 URL 來顯示不同的畫面。當連結被點擊後,路由器沒有預設要請求遠端的資源, 而是將 URL 設定導向應用程式中的有效路由。路由器會偵測 URL 被更改,然後決定要渲染什麼畫面。
## 核心元素
### Route
包含一個字串,這個字串是網域名後的那串文字,並且可以選擇要不要將狀態存入 history api。
### RouteService
與瀏覽器溝通,存取路由。
### RouteAgent
擁有 RouteService 並且協調與更新,從應用程式邏輯造成的,或是從瀏覽器事件中造成的,路由的改變。
### Switch
`Switch` trait 用於讓 Route 在實作的 `trait` 之間來回轉換。
### Router
Router 元件會與 `RouteAgent` 溝通,並且自動解析從 agent 到 switch 的 RoutesRoutes 會在 render 的 屬性中被揭露,這個屬性會決定 switch 的結果如何被轉換成 `Html`。
## 如何使用路由器
首先,你要建立一個代表你的應用程式所有狀態的型別。特別注意,這個型別可以是 enum、struct 都可以,而且你可以透過在裡面實作 `Switch` 來巢狀其他項目
然後你必須為你的型別 derive `Switch` 。對 enums 來說,每一個變數都必須宣告 `#[to = "/some/route"]`,或是如果你用 struct那就要 struct 的外部宣告。
```rust
#[derive(Switch)]
enum AppRoute {
#[to="/login"]
Login,
#[to="/register"]
Register,
#[to="/delete_account"]
Delete,
#[to="/posts/{id}"]
ViewPost(i32),
#[to="/posts/view"]
ViewPosts,
#[to="/"]
Home
}
```
特別注意,這個巨集會試著依序配對每個變數,所以如果有任何路由可能配對到兩著不同的 `to` 宣告,那會配對到第一個,而第二個就永遠不會被配對到。舉例來說,如果你定義以下的 `Switch` ,那路由將永遠只會配對到 `AppRoute::Home`。
```rust
#[derive(Switch)]
enum AppRoute {
#[to="/"]
Home,
#[to="/login"]
Login,
#[to="/register"]
Register,
#[to="/delete_account"]
Delete,
#[to="/posts/{id}"]
ViewPost(i32),
#[to="/posts/view"]
ViewPosts,
}
```
你還可以拿到 url 中的參數,透過在`#[to = ""]` 中宣告 `{}`。`{}` 代表下一個分隔符號("/"、"?"、"&"、"\#" )之前, url 中的參數。`{*}` 表示取得直到後續字符匹配為止之間的變數,如果不存在任何字串,則它將匹配任何內容。 `{<number>}` 表示取得特定數量的的分隔符號之前的變數。(例如: `{2}` 會取得兩個分隔符號之前的變數。)
對於有命名欄位的 struct 與 enum你必須給出變數的名字像是 `{user_name}` 或是 `{*:age}`。
Switch trait 可以協助取得比起字串要更有結構的變數。你可以實作 `Switch`,這樣你就可以得到特定結構的變數,而他會是一個 `unsize`。但如果這個 URL 無法被轉換,就會被視為沒有匹配。

View File

@ -0,0 +1,127 @@
# 第一個簡單的 App
首先,先建立一個新的 binary 專案:
```bash
cargo new --bin yew-app && cd yew-app
```
在依賴庫裡加入 `yew` 與 `wasm-bindgen`(最新的版號,請參考[這裡](https://docs.rs/yew)
{% code title="Cargo.toml" %}
```text
[package]
name = "yew-app"
version = "0.1.0"
authors = ["Yew App Developer <name@example.com>"]
edition = "2018"
[dependencies]
yew = "0.16"
wasm-bindgen = "0.2"
```
{% endcode %}
將下面的模板複製進你的 `src/lib.rs` 檔案:
{% code title="src/lib.rs" %}
```rust
use wasm_bindgen::prelude::*;
use yew::prelude::*;
struct Model {
link: ComponentLink<Self>,
value: i64,
}
enum Msg {
AddOne,
}
impl Component for Model {
type Message = Msg;
type Properties = ();
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
Self {
link,
value: 0,
}
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
match msg {
Msg::AddOne => self.value += 1
}
true
}
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
// 如果有新的不同屬性,應該只能回傳 true
// 若是這個元件沒有任何屬性,那就可以只回傳 false
false
}
fn view(&self) -> Html {
html! {
<div>
<button onclick={self.link.callback(|_| Msg::AddOne)}>{ "+1" }</button>
<p>{ self.value }</p>
</div>
}
}
}
#[wasm_bindgen(start)]
pub fn run_app() {
App::<Model>::new().mount_to_body();
}
```
{% endcode %}
模板會建置名叫 `Model` 的根元件 `Component`Model 會顯示一個按鈕,當你按下按鈕時, `Model` 會更新自己的狀態。需要特別注意的是,在 `main()` 裡的 `App::<Model>::new().mount_to_body()`,他會啟動你的 app 並且掛載 `Model` 裡的 HTML 到 `<body>` 標籤中。如果你想要在啟動應用程式時,帶入動態的屬性,你可以改用 `App::<Model>::new().mount_to_body_with_props(..)`。
最後,在你的專案,新增 `static` 資料夾,並新增 `index.html` 檔案到 static 裡。
```bash
mkdir static
```
{% code title="index.html" %}
```bash
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Yew Sample App</title>
<script type="module">
import init from "./wasm.js"
init()
</script>
</head>
<body></body>
</html>
```
{% endcode %}
## 執行你的 App
使用 [`wasm-pack`](https://rustwasm.github.io/docs/wasm-pack/) 來執行專案是比較好的選擇。如果你還沒有做任何準備,先用`cargo install wasm-pack`安裝 `wasm-pack` ,然後用下面的指令,建置與開啟開發用伺服器:
```bash
wasm-pack build --target web --out-name wasm --out-dir ./static
```
`wasm-pack` 會在 `./static` 裡產生一個 bundle裡面包含專案編成的 WebAssembly以及 JavaScript 的包裹器,這些東西都會在你的專案執行時被載入。
最後,用你最喜歡的網頁伺服器,去啟動在`./static` 底下的檔案。範例:
```bash
cargo +nightly install miniserve
miniserve ./static --index index.html
```

View File

@ -0,0 +1,11 @@
# 透過範例學習
我們有各種範例(都持續在維護中),建議你可以仔細閱讀他們,以了解如何使用各種不同的框架功能。當遇到問題或需要幫忙時,我們也很歡迎大家 pull-requests 或開 issues ♥️
- [**Todo App \(stdweb\)**](https://github.com/yewstack/yew/tree/v0.14.0/examples/std_web/todomvc)
- [**Todo App \(web_sys\)**](https://github.com/yewstack/yew/tree/v0.14.0/examples/web_sys/todomvc)
- [**Custom Components**](https://github.com/yewstack/yew/tree/v0.14.0/examples/custom_components)
- [**Multi-threading \(Agents\) \(stdweb\)**](https://github.com/yewstack/yew/tree/v0.14.0/examples/std_web/multi_thread)
- [**Multi-threading \(Agents\) \(web_sys\)**](https://github.com/yewstack/yew/tree/v0.14.0/examples/web_sys/multi_thread)
- [**Timer Service**](https://github.com/yewstack/yew/tree/v0.14.0/examples/timer)
- [**Nested Components**](https://github.com/yewstack/yew/tree/v0.14.0/examples/nested_list)

View File

@ -0,0 +1,5 @@
# CSS
&lt;TODO&gt;
有關 CSS 的支援與建議可以在這裡找到: [https://github.com/yewstack/yew/issues/533](https://github.com/yewstack/yew/issues/533)

View File

@ -0,0 +1,50 @@
# 除錯
## Panics
請使用 [`console_error_panic`](https://github.com/rustwasm/console_error_panic_hook) crate ,他會用 Rust symbols 來做 stacktraces。注意他跟 `cargo-web` 不相容。
## Console Logging
通常Wasm 的網頁應用程式可以跟瀏覽器的 API 互操作,所以 `console.log` 這個 api 也不例外,你可以使用以下幾種方法:
### [`wasm-logger`](https://crates.io/crates/wasm-logger)
這個 crate 整合了令人熟悉的 Rust `log` crate
```rust
// 設定
fn main() {
wasm_logger::init(wasm_logger::Config::default());
}
// 使用
log::info!("Update: {:?}", msg);
```
### **\`\`**[**`ConsoleService`**](https://docs.rs/yew/0.13.2/yew/services/console/struct.ConsoleService.html)**\`\`**
Yew 包含了這個 service而且如果 `"services"` 這個 feaure 有被打開的話,你可以直接使用他:
```rust
// 使用
ConsoleService::info(format!("Update: {:?}", msg).as_ref());
```
## Source Maps
目前 Rust/Wasm 網頁應用程式,不對 source maps 第一線支援。當然,這件事在未來可能會改變,如果這裡寫的資訊不正確,或是事情有所變化,請建議我們修改這篇文件!
### 最新資訊
\[2019 12 月\] [Chrome DevTools update](https://developers.google.com/web/updates/2019/12/webassembly#the_future)
> 但還是有大量的工作要做。舉例還說在工具方面Emscripten \(Binaryen\) 與 wasm-pack \(wasm-bindgen\),還不支援更新轉換他們的行為的 DWARF 資訊。
\[2020\] [Rust Wasm 除錯指南](https://rustwasm.github.io/book/reference/debugging.html#using-a-debugger)
> 不幸地WebAssembly 的除錯還不夠完善。在大部分的 Unix 系統中,[DWARF](http://dwarfstd.org/) 被用來編碼除錯器需要提供的程式碼等級的資訊。還有一種在 Windows 上的編碼資訊。但現在還沒有跟 WebAssembly 等價。
\[2019\] [Rust Wasm roadmap](https://rustwasm.github.io/rfcs/007-2019-roadmap.html#debugging)
> 除錯是一件棘手的事情,因為大部分的事情都不是掌握在這個工作群組中,而是依賴 WebAssembly 的標準,與瀏覽器的開發者工具如何實作。

View File

@ -0,0 +1,44 @@
---
description: Yew 的版本紀錄
---
# Roadmap
### 優先順序
即將推出的新功能和重點開發方向的優先順序將由社群決定。在 2020 的春季,我們會發出一個開發者調查,收集專案方向的回饋。你可以在 [Yew Wiki](https://github.com/yewstack/yew/wiki/Dev-Survey-%5BSpring-2020%5D) 中找到結果。
:::note
你可以在 Yew GitHub 追蹤我們主要的開發方向 [Project board](https://github.com/yewstack/yew/projects)
:::
### 重點 <a id="focuses"></a>
1. 需求最多的功能
2. 產品準備
3. 文件
4. 痛點
#### 需求最多的功能 <a id="top-requested-features"></a>
1. [函數式元件](https://github.com/yewstack/yew/projects/3)
2. [元件函式庫](https://github.com/yewstack/yew/projects/4)
3. 更好的狀態管理器
4. [Server side rendering](https://github.com/yewstack/yew/projects/5)
#### 產品準備
- 提升 Yew 的測試覆蓋率
- 減少二進位檔的大小
- [Benchmark performance](https://github.com/yewstack/yew/issues/5)
#### 文件
- 建立教學文件
- 簡化專案設定
#### 痛點
- [元件模板](https://github.com/yewstack/yew/issues/830)
- Fetch API
- Agents

View File

@ -0,0 +1,11 @@
---
description: 測試你的專案
---
# 測試
&lt;TODO&gt;
## wasm_bindgen_test <a id="wasm_bindgen_test"></a>
Rust Wasm 工作群組有維護一個 crate 叫作 [`wasm_bindgen_test`](https://rustwasm.github.io/docs/wasm-bindgen/wasm-bindgen-test/index.html) ,他讓你可以在瀏覽器裡跑類似於用內建的巨集`#[test]`測試流程。 更多資訊可以參考 [Rust Wasm working group's documentation](https://rustwasm.github.io/docs/wasm-bindgen/wasm-bindgen-test/index.html)。

View File

@ -0,0 +1,255 @@
---
title: 'Children'
---
## General usage
_Most of the time,_ when allowing a component to have children, you don't care
what type of children the component has. In such cases, the below example will
suffice.
```rust
use yew::{html, Children, Component, Context, Html, Properties};
#[derive(Properties, PartialEq)]
pub struct ListProps {
#[prop_or_default]
pub children: Children,
}
pub struct List;
impl Component for List {
type Message = ();
type Properties = ListProps;
fn create(_ctx: &Context<Self>) -> Self {
Self
}
fn view(&self, ctx: &Context<Self>) -> Html {
html! {
<div class="list">
{ for ctx.props().children.iter() }
</div>
}
}
}
```
## Advanced usage
### Typed children
In cases where you want one type of component to be passed as children to your component,
you can use `yew::html::ChildrenWithProps<T>`.
```rust
use yew::{html, ChildrenWithProps, Component, Context, Html, Properties};
pub struct Item;
impl Component for Item {
type Message = ();
type Properties = ();
fn create(_ctx: &Context<Self>) -> Self {
Self
}
fn view(&self, _ctx: &Context<Self>) -> Html {
html! {
{ "item" }
}
}
}
#[derive(Properties, PartialEq)]
pub struct ListProps {
#[prop_or_default]
pub children: ChildrenWithProps<Item>,
}
pub struct List;
impl Component for List {
type Message = ();
type Properties = ListProps;
fn create(_ctx: &Context<Self>) -> Self {
Self
}
fn view(&self, ctx: &Context<Self>) -> Html {
html! {
<div class="list">
{ for ctx.props().children.iter() }
</div>
}
}
}
```
### Enum typed children
Of course, sometimes you might need to restrict the children to a few different
components. In these cases, you have to get a little more hands-on with Yew.
The [`derive_more`](https://github.com/JelteF/derive_more) crate is used here
for better ergonomics. If you don't want to use it, you can manually implement
`From` for each variant.
```rust
use yew::{
html, html::ChildrenRenderer, virtual_dom::VChild, Component,
Context, Html, Properties,
};
pub struct Primary;
impl Component for Primary {
type Message = ();
type Properties = ();
fn create(_ctx: &Context<Self>) -> Self {
Self
}
fn view(&self, _ctx: &Context<Self>) -> Html {
html! {
{ "Primary" }
}
}
}
pub struct Secondary;
impl Component for Secondary {
type Message = ();
type Properties = ();
fn create(_ctx: &Context<Self>) -> Self {
Self
}
fn view(&self, _ctx: &Context<Self>) -> Html {
html! {
{ "Secondary" }
}
}
}
#[derive(Clone, derive_more::From, PartialEq)]
pub enum Item {
Primary(VChild<Primary>),
Secondary(VChild<Secondary>),
}
// Now, we implement `Into<Html>` so that yew knows how to render `Item`.
#[allow(clippy::from_over_into)]
impl Into<Html> for Item {
fn into(self) -> Html {
match self {
Self::Primary(child) => child.into(),
Self::Secondary(child) => child.into(),
}
}
}
#[derive(Properties, PartialEq)]
pub struct ListProps {
#[prop_or_default]
pub children: ChildrenRenderer<Item>,
}
pub struct List;
impl Component for List {
type Message = ();
type Properties = ListProps;
fn create(_ctx: &Context<Self>) -> Self {
Self
}
fn view(&self, ctx: &Context<Self>) -> Html {
html! {
<div class="list">
{ for ctx.props().children.iter() }
</div>
}
}
}
```
### Optional typed child
You can also have a single optional child component of a specific type too:
```rust
use yew::{
html, html_nested, virtual_dom::VChild, Component,
Context, Html, Properties
};
pub struct PageSideBar;
impl Component for PageSideBar {
type Message = ();
type Properties = ();
fn create(_ctx: &Context<Self>) -> Self {
Self
}
fn view(&self, _ctx: &Context<Self>) -> Html {
html! {
{ "sidebar" }
}
}
}
#[derive(Properties, PartialEq)]
pub struct PageProps {
#[prop_or_default]
pub sidebar: Option<VChild<PageSideBar>>,
}
struct Page;
impl Component for Page {
type Message = ();
type Properties = PageProps;
fn create(_ctx: &Context<Self>) -> Self {
Self
}
fn view(&self, ctx: &Context<Self>) -> Html {
html! {
<div class="page">
{ ctx.props().sidebar.clone().map(Html::from).unwrap_or_default() }
// ... page content
</div>
}
}
}
// The page component can be called either with the sidebar or without:
pub fn render_page(with_sidebar: bool) -> Html {
if with_sidebar {
// Page with sidebar
html! {
<Page sidebar={html_nested! {
<PageSideBar />
}} />
}
} else {
// Page without sidebar
html! {
<Page />
}
}
}
```

View File

@ -0,0 +1,74 @@
---
title: 'How it works'
description: 'Low level details about the framework'
---
# Low-level library internals
## Under the hood of the `html!` macro
The `html!` macro turns code written in a custom HTML-like syntax into valid Rust code. Using this
macro is not necessary for developing Yew applications, but it is recommended. The code generated
by this macro makes use of the public Yew library API which can be used directly if you wish. Note
that some methods used are undocumented intentionally to avoid accidental misuse. With each
update of `yew-macro`, the generated code will be more efficient and handle any breaking changes
without many (if any) modifications to the `html!` syntax.
Because the `html!` macro allows you to write code in a declarative style, your UI layout code will
closely match the HTML that is generated to the page. This becomes increasingly useful as your
application gets more interactive and your codebase gets larger. Rather than manually writing the
all of the code to manipulate the DOM yourself, the macro will handle it for you.
Using the `html!` macro can feel pretty magic, but it has nothing to hide. If you're curious about
how it works, try expanding the `html!` macro calls in your program. There's a useful command called
`cargo expand` which allows you to see the expansion of Rust macros. `cargo expand` isn't shipped with
`cargo` by default so you'll need to install it with `cargo install cargo-expand` if you haven't
already. [Rust-Analyzer](https://rust-analyzer.github.io/) also provides a mechanism for
[obtaining macro output from within an IDE](https://rust-analyzer.github.io/manual.html#expand-macro-recursively).
Output from the `html!` macro is often pretty terse! This is a feature: machine-generated code can
sometimes clash with other code in an application. In order to prevent issues, `proc_macro`
"hygiene" is adhered to. Some examples include:
1. Instead of using `yew::<module>` the macro generates `::yew::<module>` to make sure that the
Yew package is referenced correctly. This is also why `::alloc::vec::Vec::new()` is called instead
of just `Vec::new()`.
2. Due to potential trait method name collisions, `<Type as Trait>` is used to make sure that we're
using members from the correct trait.
## What is a virtual DOM?
The DOM ("document object model") is a representation of the HTML content that is managed by the browser
for your web page. A "virtual" DOM is simply a copy of the DOM that is held in application memory. Managing
a virtual DOM results in a higher memory overhead, but allows for batching and faster reads by avoiding
or delaying the use of browser APIs.
Having a copy of the DOM in memory can be really helpful for libraries which promote the use of
declarative UIs. Rather than needing specific code for describing how the DOM should be modified
in response to a user event, the library can use a generalized approach with DOM "diffing". When a Yew
component is updated and wants to change how it is rendered, the Yew library will build a second copy
of the virtual DOM and directly compare to a virtual DOM which mirrors what is currently on screen.
The "diff" (or difference) between the two can be broken down into incremental updates and applied in
a batch with browser APIs. Once the updates are applied, the old virtual DOM copy is discarded and the
new copy is saved for future diff checks.
This "diff" algorithm can be optimized over time to improve the performance of complex applications.
Since Yew applications are run with WebAssembly, we believe that Yew has a competitive edge to adopt
more sophisticated algorithms in the future.
The Yew virtual DOM is not exactly one-to-one with the browser DOM. It also includes "lists" and
"components" for organizing DOM elements. A list can simply be an ordered list of elements but can
also be much more powerful. By annotating each list element with a "key", application developers
can help Yew make additional optimizations to ensure that when a list changes, the least amount
of work is done to calculate the diff update. Similarly, components provide custom logic to
indicate whether a re-render is required to help with performance.
## Yew scheduler and component-scoped event loop
_Contribute to the docs explain how `yew::scheduler` and `yew::html::scope` work in depth_
## Further reading
- [More information about macros from the Rust Book](https://doc.rust-lang.org/stable/book/ch19-06-macros.html)
- [More information about `cargo-expand`](https://github.com/dtolnay/cargo-expand)
- [The API documentation for `yew::virtual_dom`](https://docs.rs/yew/*/yew/virtual_dom/index.html)

View File

@ -0,0 +1,24 @@
---
title: 'Immutable Types'
description: 'Immutable data structures for Yew'
---
## What are immutable types?
These are types that you can instantiate but never mutate the values. In order
to update a value, you must instantiate a new value.
## Why using immutable types?
Properties, like in React, are propagated from ancestors to
children. This means that the properties must live when each component is
updated. This is why properties should —ideally— be cheap to clone. In order to
achieve this we usually wrap things in `Rc`.
Immutable types are a great fit for holding property's values because they can
be cheaply cloned when passed from component to component.
## Further reading
- [Immutable example](https://github.com/yewstack/yew/tree/master/examples/immutable)
- [Crate `implicit-clone`](https://docs.rs/implicit-clone/)

View File

@ -0,0 +1,163 @@
---
title: 'Optimizations & Best Practices'
sidebar_label: Optimizations
description: 'Make your app faster'
---
## Using smart pointers effectively
**Note: if you're unsure about some of the terms used in this section, the Rust Book has a useful
[chapter about smart pointers](https://doc.rust-lang.org/book/ch15-00-smart-pointers.html).**
In an effort to avoid cloning large amounts of data to create props when re-rendering, we can use
smart pointers to only clone a reference to the data instead of the data itself. If you pass
references to the relevant data in your props and child components instead of the actual data you
can avoid cloning any data until you need to modify it in the child component, where you can
use `Rc::make_mut` to clone and obtain a mutable reference to the data you want to alter.
This brings further benefits in `Component::changed` when working out whether prop changes require
the component to re-render. This is because instead of comparing the value of the data the
underlying pointer addresses (i.e. the position in a machine's memory where the data is stored) can
instead be compared; if two pointers point to the same data then the value of the data they point to
must be the same. Note that the inverse might not be true! Even if two pointer addresses differ the
underlying data might still be the same - in this case you should compare the underlying data.
To do this comparison you'll need to use `Rc::ptr_eq` instead of just using `PartialEq` (which is
automatically used when comparing data using the equality operator `==`). The Rust documentation
has [more details about `Rc::ptr_eq`](https://doc.rust-lang.org/stable/std/rc/struct.Rc.html#method.ptr_eq).
This optimization is most useful for data types that don't implement `Copy`. If you can copy your
data cheaply, then it isn't worth putting it behind a smart pointer. For structures that
can be data-heavy like `Vec`s, `HashMap`s, and `String`s using smart pointers is likely to bring
performance improvements.
This optimization works best if the values are never updated by the children, and even better, if
they are rarely updated by parents. This makes `Rc<_>s` a good choice for wrapping property values
in for pure components.
However, it must be noted that unless you need to clone the data yourself in the child component,
this optimization is not only useless, it also adds unnecessary cost of reference counting. Props
in Yew are already reference counted and no data clones occur internally.
## View functions
For code readability reasons, it often makes sense to migrate sections of `html!` to their own
functions. Not only does this make your code more readable because it reduces the amount of
indentation present, it also encourages good design patterns particularly around building
composable applications because these functions can be called in multiple places which reduces the
amount of code that has to be written.
## Pure Components
Pure components are components that don't mutate their state, only displaying content and
propagating messages up to normal, mutable components. They differ from view functions in that they
can be used from within the `html!` macro using the component syntax \(`<SomePureComponent />`\)
instead of expression syntax \(`{some_view_function()}`\), and that depending on their
implementation, they can be memoized (this means that once a function is called its value is "saved"
so that if it's called with the same arguments more than once it doesn't have to recompute its value
and can just return the saved value from the first function call) - preventing re-renders for
identical props. Yew compares the props internally and so the UI is only re-rendered if the props change.
## Reducing compile time using workspaces
Arguably, the largest drawback to using Yew is the long time it takes to compile Yew apps. The time
taken to compile a project seems to be related to the quantity of code passed to the `html!` macro.
This tends to not be much of an issue for smaller projects, but for larger applications it makes
sense to split code across multiple crates to minimize the amount of work the compiler has to do for
each change made to the application.
One possible approach is to make your main crate handle routing/page selection, and then make a
different crate for each page, where each page could be a different component, or just a big
function that produces `Html`. Code which is shared between the crates containing different parts of
the application could be stored in a separate crate which is depended on throughout the project.
In the best case scenario, you go from rebuilding all of your code on each compile to rebuilding
only the main crate, and one of your page crates. In the worst case, where you edit something in the
"common" crate, you will be right back to where you started: compiling all code that depends on that
commonly shared crate, which is probably everything else.
If your main crate is too heavyweight, or you want to rapidly iterate on a deeply nested page \(eg.
a page that renders on top of another page\), you can use an example crate to create a simplified
implementation of the main page and render the component you are working on on top of that.
## Reducing binary sizes
- optimize Rust code
- `cargo.toml` \( defining release profile \)
- optimize wasm code using `wasm-opt`
**Note: more information about reducing binary sizes can be found in the
[Rust Wasm Book](https://rustwasm.github.io/book/reference/code-size.html#optimizing-builds-for-code-size).**
### Cargo.toml
It is possible to configure release builds to be smaller using the available settings in the
`[profile.release]` section of your `Cargo.toml`.
```toml, title=Cargo.toml
[profile.release]
# less code to include into binary
panic = 'abort'
# optimization over all codebase ( better optimization, slower build )
codegen-units = 1
# optimization for size ( more aggressive )
opt-level = 'z'
# optimization for size
# opt-level = 's'
# link time optimization using using whole-program analysis
lto = true
```
### Nightly Cargo configuration
You can also gain additional benefits from experimental nightly features of rust and
cargo. To use the nightly toolchain with `trunk`, set the `RUSTUP_TOOLCHAIN="nightly"` environment
variable. Then, you can configure unstable rustc features in your `.cargo/config.toml`.
Refer to the doc of [unstable features], specifically the section about [`build-std`] and
[`build-std-features`], to understand the configuration.
```toml, title=".cargo/config.toml"
[unstable]
# Requires the rust-src component. `rustup +nightly component add rust-src`
build-std = ["std", "panic_abort"]
build-std-features = ["panic_immediate_abort"]
```
[unstable features]: https://doc.rust-lang.org/cargo/reference/unstable.html
[`build-std`]: https://doc.rust-lang.org/cargo/reference/unstable.html#build-std
[`build-std-features`]: https://doc.rust-lang.org/cargo/reference/unstable.html#build-std-features
:::caution
The nightly rust compiler can contain bugs, such as [this one](https://github.com/yewstack/yew/issues/2696),
that require occasional attention and tweaking. Use these experimental options with care.
:::
### wasm-opt
Further more it is possible to optimize size of `wasm` code.
The Rust Wasm Book has a section about reducing the size of Wasm binaries:
[Shrinking .wasm size](https://rustwasm.github.io/book/game-of-life/code-size.html)
- using `wasm-pack` which by default optimizes `wasm` code in release builds
- using `wasm-opt` directly on `wasm` files.
```text
wasm-opt wasm_bg.wasm -Os -o wasm_bg_opt.wasm
```
#### Build size of 'minimal' example in yew/examples/
Note: `wasm-pack` combines optimization for Rust and Wasm code. `wasm-bindgen` is used in this example without any Rust size optimization.
| used tool | size |
| :-------------------------- | :---- |
| wasm-bindgen | 158KB |
| wasm-bindgen + wasm-opt -Os | 116KB |
| wasm-pack | 99 KB |
## Further reading:
- [The Rust Book's chapter on smart pointers](https://doc.rust-lang.org/book/ch15-00-smart-pointers.html)
- [Information from the Rust Wasm Book about reducing binary sizes](https://rustwasm.github.io/book/reference/code-size.html#optimizing-builds-for-code-size)
- [Documentation about Rust profiles](https://doc.rust-lang.org/cargo/reference/profiles.html)
- [binaryen project](https://github.com/WebAssembly/binaryen)

View File

@ -0,0 +1,64 @@
---
title: 'Portals'
description: 'Rendering into out-of-tree DOM nodes'
---
## What is a portal?
Portals provide a first-class way to render children into a DOM node that exists outside the DOM hierarchy of the parent component.
`yew::create_portal(child, host)` returns a `Html` value that renders `child` not hierarchically under its parent component,
but as a child of the `host` element.
## Usage
Typical uses of portals can include modal dialogs and hovercards, as well as more technical applications
such as controlling the contents of an element's
[`shadowRoot`](https://developer.mozilla.org/en-US/docs/Web/API/Element/shadowRoot), appending
stylesheets to the surrounding document's `<head>` and collecting referenced elements inside a
central `<defs>` element of an `<svg>`.
Note that `yew::create_portal` is a low-level building block. Libraries should use it to implement
higher-level APIs which can then be consumed by applications. For example, here is a
simple modal dialogue that renders its `children` into an element outside `yew`'s control,
identified by the `id="modal_host"`.
```rust
use yew::prelude::*;
#[derive(Properties, PartialEq)]
pub struct ModalProps {
#[prop_or_default]
pub children: Children,
}
#[function_component]
fn Modal(props: &ModalProps) -> Html {
let modal_host = gloo::utils::document()
.get_element_by_id("modal_host")
.expect("Expected to find a #modal_host element");
create_portal(
html!{ {for props.children.iter()} },
modal_host.into(),
)
}
```
## Event handling
Events emitted on elements inside portals follow the virtual DOM when bubbling up. That is,
if a portal is rendered as the child of an element, then an event listener on that element
will catch events dispatched from inside the portal, even if the portal renders its contents
in an unrelated location in the actual DOM.
This allows developers to be oblivious of whether a component they consume, is implemented with
or without portals. Events fired on its children will bubble up regardless.
A known issue is that events from portals into **closed** shadow roots will be dispatched twice,
once targeting the element inside the shadow root and once targeting the host element itself. Keep
in mind that **open** shadow roots work fine. If this impacts you, feel free to open a bug report
about it.
## Further reading
- [Portals example](https://github.com/yewstack/yew/tree/master/examples/portals)

View File

@ -0,0 +1,196 @@
---
title: 'Server-side Rendering'
description: 'Render Yew on the server-side.'
---
# Server-side Rendering
By default, Yew components render at the client side. When a viewer
visits a website, the server sends a skeleton html file without any actual
content and a WebAssembly bundle to the browser.
Everything is rendered at the client side by the WebAssembly
bundle. This is known as client-side rendering.
This approach works fine for most websites, with some caveats:
1. Users will not be able to see anything until the entire WebAssembly
bundle is downloaded and initial render has completed.
This can result in poor user experience if the user is using a slow network.
2. Some search engines do not support dynamically rendered web content and
those who do usually rank dynamic websites lower in the search results.
To solve these problems, we can render our website on the server side.
## How it Works
Yew provides a `ServerRenderer` to render pages on the
server-side.
To render Yew components at the server-side, you can create a renderer
with `ServerRenderer::<App>::new()` and call `renderer.render().await`
to render `<App />` into a `String`.
```rust
use yew::prelude::*;
use yew::ServerRenderer;
#[function_component]
fn App() -> Html {
html! {<div>{"Hello, World!"}</div>}
}
// we use `flavor = "current_thread"` so this snippet can be tested in CI,
// where tests are run in a WASM environment. You likely want to use
// the (default) `multi_thread` favor as:
// #[tokio::main]
#[tokio::main(flavor = "current_thread")]
async fn no_main() {
let renderer = ServerRenderer::<App>::new();
let rendered = renderer.render().await;
// Prints: <div>Hello, World!</div>
println!("{}", rendered);
}
```
## Component Lifecycle
The recommended way of working with server-side rendering is
function components.
All hooks other than `use_effect` (and `use_effect_with_deps`)
will function normally until a component successfully renders into `Html`
for the first time.
:::caution Web APIs are not available!
Web APIs such as `web_sys` are not available when your component is
rendering on the server-side.
Your application will panic if you try to use them.
You should isolate logics that need Web APIs in `use_effect` or
`use_effect_with_deps` as effects are not executed during server side
rendering.
:::
:::danger Struct Components
Whilst it's possible to use Struct Components with server-side rendering,
there's no clear boundaries between client-side safe logic like the
`use_effect` hook for function components and lifecycle events are invoked
in a different order than client side.
In addition, Struct Components will continue to accept messages until all of its
children are rendered and `destroy` method is called. Developers need to
make sure no messages possibly passed to components would link to logic
that makes use of Web APIs.
When designing an application with server-side rendering support,
prefer function components unless you have a good reason not to.
:::
## Data Fetching during Server-side Rendering
Data fetching is one of the difficult point with server side rendering
and hydration.
Traditionally, when a component renders, it is instantly available
(outputs a virtual dom to be rendered). This works fine when the
component does not want to fetch any data. But what happens if the component
wants to fetch some data during rendering?
In the past, there's no mechanism for Yew to detect whether a component is still
fetching data. The data fetching client is responsible to implement
a solution to detect what's being requested during initial render and triggers
a second render after requests are fulfilled. The server repeats this process until
no more pending requests are added during a render before returning a response.
Not only this wastes CPU resources by repeatedly rendering components,
but the data client also needs to provide a way to make the data fetched on
the server-side available during hydration process to make sure that the
virtual dom returned by initial render is consistent with the
server-side rendered DOM tree which can be hard to implement.
Yew takes a different approach by trying to solve this issue with `<Suspense />`.
Suspense is a special component that when used on the client-side,
provides a way to show a fallback UI while the component is fetching
data (suspended) and resumes to normal UI when the data fetching completes.
When the application is rendered on the server-side, Yew waits until a
component is no longer suspended before serializing it into the string
buffer.
During the hydration process, elements within a `<Suspense />` component
remains dehydrated until all of its child components are no longer
suspended.
With this approach, developers can build a client-agnostic, SSR ready
application with data fetching with very little effort.
## SSR Hydration
Hydration is the process that connects a Yew application to the
server-side generated HTML file. By default, `ServerRender` prints
hydratable html string which includes additional information to facilitate hydration.
When the `Renderer::hydrate` method is called, instead of start rendering from
scratch, Yew will reconcile the Virtual DOM generated by the application
with the html string generated by the server renderer.
:::caution
To successfully hydrate an html representation created by the
`ServerRenderer`, the client must produce a Virtual DOM layout that
exactly matches the one used for SSR including components that do not
contain any elements. If you have any component that is only useful in
one implementation, you may want to use a `PhantomComponent` to fill the
position of the extra component.
:::
## Component Lifecycle during hydration
During Hydration, components schedule 2 consecutive renders after it is
created. Any effects are called after the second render completes.
It is important to make sure that the render function of the your
component is side-effect free. It should not mutate any states or trigger
additional renders. If your component currently mutates states or triggers
additional renders, move them into an `use_effect` hook.
It's possible to use Struct Components with server-side rendering in
hydration, the view function will be called
multiple times before the rendered function will be called.
The DOM is considered as not connected until rendered function is called,
you should prevent any access to rendered nodes
until `rendered()` method is called.
## Example
```rust ,ignore
use yew::prelude::*;
use yew::Renderer;
#[function_component]
fn App() -> Html {
html! {<div>{"Hello, World!"}</div>}
}
fn main() {
let renderer = Renderer::<App>::new();
// hydrates everything under body element, removes trailing
// elements (if any).
renderer.hydrate();
}
```
Example: [simple_ssr](https://github.com/yewstack/yew/tree/master/examples/simple_ssr)
Example: [ssr_router](https://github.com/yewstack/yew/tree/master/examples/ssr_router)
:::caution
Server-side rendering is currently experiemental. If you find a bug, please file
an issue on [GitHub](https://github.com/yewstack/yew/issues/new?assignees=&labels=bug&template=bug_report.md&title=).
:::

View File

@ -0,0 +1,86 @@
---
title: 'Callbacks'
---
## Callbacks
Callbacks are used to communicate with services, agents, and parent components within Yew.
Internally their type is just `Fn` wrapped in `Rc` to allow them to be cloned.
They have an `emit` function that takes their `<IN>` type as an argument and converts that to a message expected by its destination. If a callback from a parent is provided in props to a child component, the child can call `emit` on the callback in its `update` lifecycle hook to send a message back to its parent. Closures or Functions provided as props inside the `html!` macro are automatically converted to Callbacks.
A simple use of a callback might look something like this:
```rust
use yew::{html, Component, Context, Html};
enum Msg {
Clicked,
}
struct Comp;
impl Component for Comp {
type Message = Msg;
type Properties = ();
fn create(_ctx: &Context<Self>) -> Self {
Self
}
fn view(&self, ctx: &Context<Self>) -> Html {
// highlight-next-line
let onclick = ctx.link().callback(|_| Msg::Clicked);
html! {
// highlight-next-line
<button {onclick}>{ "Click" }</button>
}
}
}
```
The function passed to `callback` must always take a parameter. For example, the `onclick` handler requires a function which takes a parameter of type `MouseEvent`. The handler can then decide what kind of message should be sent to the component. This message is scheduled for the next update loop unconditionally.
If you need a callback that might not need to cause an update, use `batch_callback`.
```rust
use yew::{events::KeyboardEvent, html, Component, Context, Html};
enum Msg {
Submit,
}
struct Comp;
impl Component for Comp {
type Message = Msg;
type Properties = ();
fn create(_ctx: &Context<Self>) -> Self {
Self
}
fn view(&self, ctx: &Context<Self>) -> Html {
// highlight-start
let onkeypress = ctx.link().batch_callback(|event: KeyboardEvent| {
if event.key() == "Enter" {
Some(Msg::Submit)
} else {
None
}
});
html! {
<input type="text" {onkeypress} />
}
// highlight-end
}
}
```
## Relevant examples
- [Counter](https://github.com/yewstack/yew/tree/master/examples/counter)
- [Timer](https://github.com/yewstack/yew/tree/master/examples/timer)

View File

@ -0,0 +1,82 @@
---
title: 'Higher Order Components'
---
There are several cases where Struct components dont directly support a feature (ex. Suspense) or require a lot of boiler plate to use the features (ex. Context).
In those cases it is recommended to create function components that are higher order components.
## Higher Order Components Definition
Higher Order Components are components that dont add any new Html and only wrap some other component to provide extra functionality.
### Example
Hook into Context and pass it down to a struct component
```rust
use yew::prelude::*;
#[derive(Clone, Debug, PartialEq)]
struct Theme {
foreground: String,
background: String,
}
#[function_component]
pub fn App() -> Html {
let ctx = use_state(|| Theme {
foreground: "#000000".to_owned(),
background: "#eeeeee".to_owned(),
});
html! {
<ContextProvider<Theme> context={(*ctx).clone()}>
<ThemedButtonHOC />
</ContextProvider<Theme>>
}
}
// highlight-start
#[function_component]
pub fn ThemedButtonHOC() -> Html {
let theme = use_context::<Theme>().expect("no ctx found");
html! {<ThemedButtonStructComponent {theme} />}
}
// highlight-end
#[derive(Properties, PartialEq)]
pub struct Props {
pub theme: Theme,
}
struct ThemedButtonStructComponent;
impl Component for ThemedButtonStructComponent {
type Message = ();
type Properties = Props;
fn create(_ctx: &Context<Self>) -> Self {
Self
}
fn view(&self, ctx: &Context<Self>) -> Html {
let theme = &ctx.props().theme;
html! {
<button style={format!(
"background: {}; color: {};",
theme.background,
theme.foreground
)}
>
{ "Click me!" }
</button>
}
}
}
```

View File

@ -0,0 +1,32 @@
---
title: 'Introduction'
description: 'Components in Yew'
---
## What are Components?
Components are the building blocks of Yew. They manage their own state and can render themselves to the DOM.
Components are created by implementing the `Component` trait for a type.
## Writing Component's markup
Yew uses Virtual DOM to render elements to the DOM. The Virtual DOM tree can be constructed by using the
`html!` macro. `html!` uses syntax which is similar to HTML but is not exactly the same. The rules are also
much stricter. It also provides super-powers like conditional rendering and rendering of lists using iterators.
:::info
[Learn more about the `html!` macro, how it's used and its syntax](concepts/html/introduction.mdx)
:::
## Passing data to a component
Yew components use _props_ to communicate between parent and children. A parent component may pass any data as props to
its children. Props are similar to HTML attributes but any Rust type can be passed as props.
:::info
[Learn more about the props](advanced-topics/struct-components/properties.mdx)
:::
:::info
For other than parent/child communication, use [contexts](../../concepts/contexts.mdx)
:::

View File

@ -0,0 +1,305 @@
---
title: 'Lifecycle'
description: 'Components and their lifecycle hooks'
---
The `Component` trait has a number of methods which need to be implemented; Yew will call these at different
stages in the lifecycle of a component.
## Lifecycle
:::important contribute
`Contribute to our docs:` [Add a diagram of the component lifecycle](https://github.com/yewstack/yew/issues/1915)
:::
## Lifecycle Methods
### Create
When a component is created, it receives properties from its parent component and is stored within
the `Context<Self>` that's passed down to the `create` method. The properties can be used to
initialize the component's state and the "link" can be used to register callbacks or send messages to the component.
```rust
use yew::{Component, Context, html, Html, Properties};
#[derive(PartialEq, Properties)]
pub struct Props;
pub struct MyComponent;
impl Component for MyComponent {
type Message = ();
type Properties = Props;
// highlight-start
fn create(ctx: &Context<Self>) -> Self {
MyComponent
}
// highlight-end
fn view(&self, _ctx: &Context<Self>) -> Html {
html! {
// impl
}
}
}
```
### View
The `view` method allows you to describe how a component should be rendered to the DOM. Writing
HTML-like code using Rust functions can become quite messy, so Yew provides a macro called `html!`
for declaring HTML and SVG nodes (as well as attaching attributes and event listeners to them) and a
convenient way to render child components. The macro is somewhat similar to React's JSX (the
differences in programming language aside).
One difference is that Yew provides a shorthand syntax for properties, similar to Svelte, where instead of writing `onclick={onclick}`, you can just write `{onclick}`.
```rust
use yew::{Component, Context, html, Html, Properties};
enum Msg {
Click,
}
#[derive(PartialEq, Properties)]
struct Props {
button_text: String,
}
struct MyComponent;
impl Component for MyComponent {
type Message = Msg;
type Properties = Props;
fn create(_ctx: &Context<Self>) -> Self {
Self
}
// highlight-start
fn view(&self, ctx: &Context<Self>) -> Html {
let onclick = ctx.link().callback(|_| Msg::Click);
html! {
<button {onclick}>{ &ctx.props().button_text }</button>
}
}
// highlight-end
}
```
For usage details, check out [the `html!` guide](concepts/html/introduction.mdx).
### Rendered
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. This method is useful when you
want to perform actions that can only be completed after the component has rendered elements. There
is also a parameter called `first_render` which can be used to determine whether this function is
being called on the first render, or instead a subsequent one.
```rust
use web_sys::HtmlInputElement;
use yew::{
Component, Context, html, Html, NodeRef,
};
pub struct MyComponent {
node_ref: NodeRef,
}
impl Component for MyComponent {
type Message = ();
type Properties = ();
fn create(_ctx: &Context<Self>) -> Self {
Self {
node_ref: NodeRef::default(),
}
}
fn view(&self, ctx: &Context<Self>) -> Html {
html! {
<input ref={self.node_ref.clone()} type="text" />
}
}
// highlight-start
fn rendered(&mut self, _ctx: &Context<Self>, first_render: bool) {
if first_render {
if let Some(input) = self.node_ref.cast::<HtmlInputElement>() {
input.focus();
}
}
}
// highlight-end
}
```
:::tip note
Note that this lifecycle method does not require an implementation and will do nothing by default.
:::
### Update
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. Messages can be sent
by event listeners, child components, Agents, Services, or Futures.
Here's an example of what an implementation of `update` could look like:
```rust
use yew::{Component, Context, html, Html};
// highlight-start
pub enum Msg {
SetInputEnabled(bool)
}
// highlight-end
struct MyComponent {
input_enabled: bool,
}
impl Component for MyComponent {
// highlight-next-line
type Message = Msg;
type Properties = ();
fn create(_ctx: &Context<Self>) -> Self {
Self {
input_enabled: false,
}
}
// highlight-start
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
Msg::SetInputEnabled(enabled) => {
if self.input_enabled != enabled {
self.input_enabled = enabled;
true // Re-render
} else {
false
}
}
}
}
// highlight-end
fn view(&self, _ctx: &Context<Self>) -> Html {
html! {
// impl
}
}
}
```
### Changed
Components may be re-rendered by their parents. When this happens, they could receive new properties
and need to re-render. This design facilitates parent to child component communication by just
changing the values of a property. There is a default implementation which re-renders the component
when props are changed.
### Destroy
After Components are unmounted from the DOM, Yew calls the `destroy` lifecycle method; this is
necessary if you need to undertake operations to clean up after earlier actions of a component
before it is destroyed. This method is optional and does nothing by default.
### Infinite loops
Infinite loops are possible with Yew's lifecycle methods, but are only caused when trying to update
the same component after every render when that update also requests the component to be rendered.
A simple example can be seen below:
```rust
use yew::{Context, Component, Html};
struct Comp;
impl Component for Comp {
type Message = ();
type Properties = ();
fn create(_ctx: &Context<Self>) -> Self {
Self
}
fn update(&mut self, _ctx: &Context<Self>, _msg: Self::Message) -> bool {
// We are going to always request to re-render on any msg
true
}
fn view(&self, _ctx: &Context<Self>) -> Html {
// For this example it doesn't matter what is rendered
Html::default()
}
fn rendered(&mut self, ctx: &Context<Self>, _first_render: bool) {
// Request that the component is updated with this new msg
ctx.link().send_message(());
}
}
```
Let's run through what happens here:
1. Component is created using the `create` function.
2. The `view` method is called so Yew knows what to render to the browser DOM.
3. The `rendered` method is called, which schedules an update message using the `Context` link.
4. Yew finishes the post-render phase.
5. Yew checks for scheduled events and sees the update message queue is not empty so works through
the messages.
6. The `update` method is called which returns `true` to indicate something has changed and the
component needs to re-render.
7. Jump back to 2.
You can still schedule updates in the `rendered` method and it's often useful to do so, but
consider how your component will terminate this loop when you do.
## Associated Types
The `Component` trait has two associated types: `Message` and `Properties`.
```rust ,ignore
impl Component for MyComponent {
type Message = Msg;
type Properties = Props;
// ...
}
```
The `Message` type is used to send messages to a component after an event has taken place; for
example you might want to undertake some action when a user clicks a button or scrolls down the
page. Because components tend to have to respond to more than one event, the `Message` type will
normally be an enum, where each variant is an event to be handled.
When organizing your codebase, it is sensible to include the definition of the `Message` type in the
same module in which your component is defined. You may find it helpful to adopt a consistent naming
convention for message types. One option (though not the only one) is to name the types
`ComponentNameMsg`, e.g. if your component was called `Homepage` then you might call the type
`HomepageMsg`.
```rust
enum Msg {
Click,
FormInput(String)
}
```
`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. This type is used when creating and updating a component. It is common practice to create a struct called `Props` in your component's module and use that as the component's `Properties` type. It is common to shorten "properties" to "props". Since props are handed down from parent components, the root component of your application typically has a `Properties` type of `()`. If you wish to specify properties for your root component, use the `App::mount_with_props` method.
:::info
[Learn more about properties](./properties)
:::
## Lifecycle Context
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.

View File

@ -0,0 +1,145 @@
---
title: 'Properties'
description: 'Parent to child communication'
---
Properties enable child and parent components to communicate with each other.
Every component has an associated properties type which describes what is passed down from the parent.
In theory this can be any type that implements the `Properties` trait, but in practice there's no
reason for it to be anything but a struct where each field represents a property.
## Derive macro
Instead of implementing the `Properties` trait yourself, you should use `#[derive(Properties)]` to
automatically generate the implementation instead.
Types for which you derive `Properties` must also implement `PartialEq`.
### Field attributes
When deriving `Properties`, all fields are required by default.
The following attributes allow you to give your props initial values which will be used unless they're set to another value.
:::tip
Attributes aren't visible in Rustdoc generated documentation.
The doc strings of your properties should mention whether a prop is optional and if it has a special default value.
:::
#### `#[prop_or_default]`
Initialize the prop value with the default value of the field's type using the `Default` trait.
#### `#[prop_or(value)]`
Use `value` to initialize the prop value. `value` can be any expression that returns the field's type.
For example, to default a boolean prop to `true`, use the attribute `#[prop_or(true)]`.
#### `#[prop_or_else(function)]`
Call `function` to initialize the prop value. `function` should have the signature `FnMut() -> T` where `T` is the field type.
## `PartialEq`
`Properties` require `PartialEq` to be implemented. This is so that they can be compared by Yew to call the `changed` method
only when they change.
## Memory/speed overhead of using Properties
Internally properties are reference counted. This means that only a pointer is passed down the component tree for props.
It saves us from the cost of having to clone the entire props, which might be expensive.
:::tip
Make use of `AttrValue` which is our custom type for attribute values instead of defining them as String or another similar type.
:::
## Example
```rust
use yew::Properties;
/// Importing the AttrValue from virtual_dom
use yew::virtual_dom::AttrValue;
#[derive(Clone, PartialEq)]
pub enum LinkColor {
Blue,
Red,
Green,
Black,
Purple,
}
fn create_default_link_color() -> LinkColor {
LinkColor::Blue
}
#[derive(Properties, PartialEq)]
pub struct LinkProps {
/// The link must have a target.
href: AttrValue,
/// Also notice that we're using AttrValue instead of String
text: AttrValue,
/// Color of the link. Defaults to `Blue`.
#[prop_or_else(create_default_link_color)]
color: LinkColor,
/// The view function will not specify a size if this is None.
#[prop_or_default]
size: Option<u32>,
/// When the view function doesn't specify active, it defaults to true.
#[prop_or(true)]
active: bool,
}
```
## Props macro
The `yew::props!` macro allows you to build properties the same way the `html!` macro does it.
The macro uses the same syntax as a struct expression except that you can't use attributes or a base expression (`Foo { ..base }`).
The type path can either point to the props directly (`path::to::Props`) or the associated properties of a component (`MyComp::Properties`).
```rust
use yew::{props, Properties, virtual_dom::AttrValue};
#[derive(Clone, PartialEq)]
pub enum LinkColor {
Blue,
Red,
Green,
Black,
Purple,
}
fn create_default_link_color() -> LinkColor {
LinkColor::Blue
}
#[derive(Properties, PartialEq)]
pub struct LinkProps {
/// The link must have a target.
href: AttrValue,
/// Also notice that we're using AttrValue instead of String
text: AttrValue,
/// Color of the link. Defaults to `Blue`.
#[prop_or_else(create_default_link_color)]
color: LinkColor,
/// The view function will not specify a size if this is None.
#[prop_or_default]
size: Option<u32>,
/// When the view function doesn't specify active, it defaults to true.
#[prop_or(true)]
active: bool,
}
impl LinkProps {
/// Notice that this function receives href and text as String
/// We can use `AttrValue::from` to convert it to a `AttrValue`
pub fn new_link_with_size(href: String, text: String, size: u32) -> Self {
// highlight-start
props! {LinkProps {
href: AttrValue::from(href),
text: AttrValue::from(text),
size,
}}
// highlight-end
}
}
```

View File

@ -0,0 +1,55 @@
---
title: 'Refs'
description: 'Out-of-band DOM access'
---
The `ref` keyword can be used inside of any HTML element or component to get the DOM `Element` that
the item is attached to. This can be used to make changes to the DOM outside of the `view` lifecycle
method.
This is useful for getting ahold of canvas elements, or scrolling to different sections of a page.
For example, using a `NodeRef` in a component's `rendered` method allows you to make draw calls to
a canvas element after it has been rendered from `view`.
The syntax is:
```rust
use web_sys::Element;
use yew::{html, Component, Context, Html, NodeRef};
struct Comp {
node_ref: NodeRef,
}
impl Component for Comp {
type Message = ();
type Properties = ();
fn create(_ctx: &Context<Self>) -> Self {
Self {
// highlight-next-line
node_ref: NodeRef::default(),
}
}
fn view(&self, _ctx: &Context<Self>) -> Html {
html! {
// highlight-next-line
<div ref={self.node_ref.clone()}></div>
}
}
fn rendered(&mut self, _ctx: &Context<Self>, _first_render: bool) {
// highlight-start
let has_attributes = self.node_ref
.cast::<Element>()
.unwrap()
.has_attributes();
// highlight-end
}
}
```
## Relevant examples
- [Node Refs](https://github.com/yewstack/yew/tree/master/examples/node_refs)

View File

@ -0,0 +1,81 @@
---
title: 'Scope'
description: "Component's Scope"
---
## Component's `Scope<_>` API
The component "`Scope`" is the mechanism through which components are able to create callbacks and update themselves
using messages. We obtain a reference to this by calling `link()` on the context object passed to the component.
### `send_message`
Sends a message to the component.
Messages are handled by the `update` method which determines whether the component should re-render.
### `send_message_batch`
Sends multiple messages to the component at the same time.
This is similar to `send_message` but if any of the messages cause the `update` method to return `true`,
the component will re-render after all messages in the batch have been processed.
If the given vector is empty, this function doesn't do anything.
### `callback`
Create a callback that will send a message to the component when it is executed.
Under the hood, it will call `send_message` with the message returned by the provided closure.
```rust
use yew::{html, Component, Context, Html};
enum Msg {
Text(String),
}
struct Comp;
impl Component for Comp {
type Message = Msg;
type Properties = ();
fn create(_ctx: &Context<Self>) -> Self {
Self
}
fn view(&self, ctx: &Context<Self>) -> Html {
// Create a callback that accepts some text and sends it
// to the component as the `Msg::Text` message variant.
// highlight-next-line
let cb = ctx.link().callback(|text: String| Msg::Text(text));
// The previous line is needlessly verbose to make it clearer.
// It can be simplified it to this:
// highlight-next-line
let cb = ctx.link().callback(Msg::Text);
// Will send `Msg::Text("Hello World!")` to the component.
// highlight-next-line
cb.emit("Hello World!".to_owned());
html! {
// html here
}
}
}
```
### `batch_callback`
Create a callback that will send a batch of messages to the component when it is executed.
The difference to `callback` is that the closure passed to this method doesn't have to return a message.
Instead, the closure can return either `Vec<Msg>` or `Option<Msg>` where `Msg` is the component's message type.
`Vec<Msg>` is treated as a batch of messages and uses `send_message_batch` under the hood.
`Option<Msg>` calls `send_message` if it is `Some`. If the value is `None`, nothing happens.
This can be used in cases where, depending on the situation, an update isn't required.
This is achieved using the `SendAsMessage` trait which is only implemented for these types.
You can implement `SendAsMessage` for your own types which allows you to use them in `batch_callback`.

View File

@ -0,0 +1,64 @@
---
title: 'Agents'
description: "Yew's Actor System"
---
import useBaseUrl from '@docusaurus/useBaseUrl'
import ThemedImage from '@theme/ThemedImage'
Agents are a way to offload tasks to web workers.
In order for agents to run concurrently, Yew uses
[web-workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers).
## Lifecycle
<!--
The diagram is produced with nomnoml (nomnoml.com),
The code can be found in the <desc> tag of the svgs.
-->
<ThemedImage
alt="agent lifecycle diagram"
sources={{
light: useBaseUrl('/img/agent-lifecycle-light.svg'),
dark: useBaseUrl('/img/agent-lifecycle-dark.svg'),
}}
/>
## Types of Agents
### Reaches
- Public - There will exist at most one instance of a Public Agent at any given time. Bridges will
spawn or connect to an already spawned agent in a web worker.
When no bridges are connected to this agent, the agent will disappear.
- Private - Spawn a new agent in a web worker for every new bridge. This is good for moving shared but
independent behavior that communicates with the browser out of components. When
the the connected bridge is dropped, the agent will disappear.
- Global \(WIP\)
## Communication between Agents and Components
### Bridges
A bridge allows bi-directional communication between an agent and a component. Bridges also allow agents to communicate with one another.
A `use_bridge` hook is also provided to create bridges in a function component.
### Dispatchers
A dispatcher allows uni-directional communication between a component and an agent. A dispatcher allows a component to send messages to an agent.
## Overhead
Agents use web workers \(i.e. Private and Public\). They incur a serialization overhead on the
messages they send and receive. Agents use [bincode](https://github.com/servo/bincode) to communicate
with other threads, so the cost is substantially higher than just calling a function.
## Further reading
- The [web_worker_fib](https://github.com/yewstack/yew/tree/master/examples/web_worker_fib) example shows how
components can send message to and receive message from agents.

View File

@ -0,0 +1,101 @@
---
title: 'CSS with classes!'
description: 'A handy macro to handle classes'
comment: 'Keep this file as short and simple as possible. Its purpose is to ease in the reader into components in Yew instead of providing proper API docs'
---
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
Yew does not natively provide a css in rust solution, but helps with styling by providing
programmatic ways to interact with the html `class` attribute.
## Classes
The `classes!` macro and associated `Classes` struct simplify the use of HTML classes:
<Tabs>
<TabItem value="Literal" label="Literal">
```rust
use yew::{classes, html};
html! {
<div class={classes!("container")}></div>
};
```
</TabItem>
<TabItem value="Multiple" label="Multiple">
```rust
use yew::{classes, html};
html! {
<div class={classes!("class-1", "class-2")}></div>
};
```
</TabItem>
<TabItem value="String" label="String">
```rust
use yew::{classes, html};
html! {
<div class={classes!(String::from("class-1 class-2"))}></div>
};
```
</TabItem>
<TabItem value="Optional" label="Optional">
```rust
use yew::{classes, html};
html! {
<div class={classes!(Some("class"))} />
};
```
</TabItem>
<TabItem value="Vector" label="Vector">
```rust
use yew::{classes, html};
html! {
<div class={classes!(vec!["class-1", "class-2"])}></div>
};
```
</TabItem>
<TabItem value="Slice" label="Slice">
```rust
use yew::{classes, html};
html! {
<div class={classes!(["class-1", "class-2"].as_ref())}></div>
};
```
</TabItem>
</Tabs>
We will expand upon this concept in [more CSS](../../more/css).
## Inline Styles
Currently Yew does not provide any special help with inline styles specified via the `styles` attribute,
but you can use it like any other html attribute:
```rust
use yew::{classes, html};
html! {
<div styles="color: red;"></div>
};
```
We will expand upon this concept in [more CSS](../../more/css).

View File

@ -0,0 +1,79 @@
---
title: 'HTML with html!'
description: 'Its HTML but not quite!'
comment: 'Keep this file as short and simple as possible. Its purpose is to ease in the reader into components in Yew instead of providing proper API docs'
---
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
You can write expressions resembling HTML with the `html!` macro. Behind the scenes Yew turns
it into rust code representing the DOM to generate.
```rust
use yew::prelude::*;
let my_header: Html = html! {
<img src="img_girl.jpg" alt="Girl in a jacket" width="500" height="600" />
};
```
Similar to format expressions, there is an easy way to embed values from the surrounding
context into the html by applying curly brackets:
```rust
use yew::prelude::*;
let header_text = "Hello world".to_string();
let header_html: Html = html! {
<h1>{header_text}</h1>
};
let count: usize = 5;
let counter_html: Html = html! {
<p>{"My age is: "}{count}</p>
};
let combined_html: Html = html! {
<div>{header_html}{counter_html}</div>
};
```
One rule major rule comes with use of `html!` - you can only return 1 wrapping node.
To render a list of multiple elements, `html!` allows fragments. Fragments are tags
without a name, that produce no html element by themselves.
<Tabs>
<TabItem value="Invalid" label="Invalid">
```rust , compile_fail
use yew::html;
// error: only one root html element allowed
html! {
<div></div>
<p></p>
};
```
</TabItem>
<TabItem value="Valid" label="Valid">
```rust
use yew::html;
// fixed: using html fragments
html! {
<>
<div></div>
<p></p>
</>
};
```
</TabItem>
</Tabs>
We will introduce Yew and HTML further in depth in [more HTML](concepts/html/introduction.mdx).

View File

@ -0,0 +1,53 @@
---
title: 'JS with RS'
description: 'Javascript with Rust'
comment: 'Keep this file as short and simple as possible. Its purpose is to ease in the reader into components in Yew instead of providing proper API docs'
---
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
> Yew centrally operates on the idea of keeping everything that a reusable piece of
> UI may need in one place - rust files, while also keeping the underlying technology
> accessible where necessary.
As of today, WebAssembly is not feature-complete for DOM interactions. This means even in Yew we
sometimes rely on calling Javascript. What follows is an overview of the involved libraries.
## wasm-bindgen
[`wasm-bindgen`](https://github.com/rustwasm/wasm-bindgen) is a library and tool that enables calls to javascript from rust and back to rust from javascript.
We highly recommend you take a look at their [documentation](https://rustwasm.github.io/docs/wasm-bindgen/) and our [quick guide](./wasm-bindgen.mdx).
## web-sys
The [`web-sys` crate](https://crates.io/crates/web-sys) provides bindings for Web APIs and allows us to write Javascript code in a rustyfied and safe way.
Example:
<Tabs>
<TabItem value="JS" label="JS">
```js
let document = window.document
```
</TabItem>
<TabItem value="RS" label="RS">
```rust ,no_run
use wasm_bindgen::UnwrapThrowExt;
use web_sys::window;
let document = window()
.expect_throw("window is undefined")
.document()
.expect_throw("document is undefined");
```
</TabItem>
</Tabs>
Once again we highly recommend you take a look at their [documentation](https://rustwasm.github.io/docs/wasm-bindgen/) and our [quick guide](./web-sys.mdx).

View File

@ -0,0 +1,243 @@
---
title: 'wasm-bindgen'
sidebar_label: wasm-bindgen
---
[`wasm-bindgen`](https://github.com/rustwasm/wasm-bindgen) is a library and tool to facilitate
high-level interactions between Wasm modules and JavaScript; it is built with Rust by
[The Rust and WebAssembly Working Group](https://rustwasm.github.io/).
Yew uses `wasm-bindgen` to interact with the browser through a number of crates:
- [`js-sys`](https://crates.io/crates/js-sys)
- [`wasm-bindgen`](https://crates.io/crates/wasm-bindgen)
- [`wasm-bindgen-futures`](https://crates.io/crates/wasm-bindgen-futures)
- [`web-sys`](https://crates.io/crates/web-sys)
This section will explore some of these crates in a high level in order to make it easier to understand
and use `wasm-bindgen` APIs with Yew. For a more in-depth guide to `wasm-bindgen` and its associated
crates then check out [The `wasm-bindgen` Guide](https://rustwasm.github.io/docs/wasm-bindgen/).
For documentation on the above crates check out [`wasm-bindgen docs.rs`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/index.html).
:::tip
Use the `wasm-bindgen` doc.rs search to find browser APIs and JavaScript types that have been imported
over using `wasm-bindgen`.
:::
## [`wasm-bindgen`](https://crates.io/crates/wasm-bindgen)
This crate provides many of the building blocks for the rest of the crates above. In this section we
are only going to cover two main areas of the `wasm-bindgen` crate and that is the macro and some
types / traits you will see pop up again and again.
### `#[wasm_bindgen]` macro
The `#[wasm_bindgen]` macro provides an interface between Rust and JavaScript, providing a system
for translating between the two. Using this macro is more advanced, and you shouldn't need to reach
for it unless you are trying to use an external JavaScript library. The `js-sys` and `web-sys`
crates expose `wasm-bindgen` definitions for built-in Javascript types and browser APIs.
Let's go over a simple example of using the `#[wasm-bindgen]` macro to import some specific flavours
of the [`console.log`](https://developer.mozilla.org/en-US/docs/Web/API/Console/log) function.
```rust ,no_run
use wasm_bindgen::prelude::*;
// First up let's take a look of binding `console.log` manually, without the
// help of `web_sys`. Here we're writing the `#[wasm_bindgen]` annotations
// manually ourselves, and the correctness of our program relies on the
// correctness of these annotations!
#[wasm_bindgen]
extern "C" {
// Use `js_namespace` here to bind `console.log(..)` instead of just
// `log(..)`
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);
// The `console.log` is quite polymorphic, so we can bind it with multiple
// signatures. Note that we need to use `js_name` to ensure we always call
// `log` in JS.
#[wasm_bindgen(js_namespace = console, js_name = log)]
fn log_u32(a: u32);
// Multiple arguments too!
#[wasm_bindgen(js_namespace = console, js_name = log)]
fn log_many(a: &str, b: &str);
}
// using the imported functions!
log("Hello from Rust!");
log_u32(42);
log_many("Logging", "many values!");
```
_This example was adapted from [1.2 Using console.log of The `wasm-bindgen` Guide](https://rustwasm.github.io/docs/wasm-bindgen/examples/console-log.html)_.
### Simulating inheritance
Inheritance between JavaScript classes is a core feature of the Javascript language, and the DOM
(Document Object Model) is designed around it. When types are imported using `wasm-bindgen` you can
also add attributes that describe their inheritance.
In Rust this inheritance is represented using the [`Deref`](https://doc.rust-lang.org/std/ops/trait.Deref.html)
and [`AsRef`](https://doc.rust-lang.org/std/convert/trait.AsRef.html) traits. An example of this
might help; so say you have three types `A`, `B`, and `C` where `C` extends `B` which in turn
extends `A`.
When importing these types the `#[wasm-bindgen]` macro will implement the `Deref` and `AsRef`
traits in the following way:
- `C` can `Deref` to `B`
- `B` can `Deref` to `A`
- `C` can be `AsRef` to `B`
- Both `C` & `B` can be `AsRef` to `A`
These implementations allow you to call a method from `A` on an instance of `C` and to use `C` as if
it was `&B` or `&A`.
Its important to note that every single type imported using `#[wasm-bindgen]` has the same root type,
you can think of it as the `A` in the example above, this type is [`JsValue`](#jsvalue) which has
its own section below.
_[extends section in The `wasm-bindgen` Guide](https://rustwasm.github.io/docs/wasm-bindgen/reference/attributes/on-js-imports/extends.html)_
### [`JsValue`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/struct.JsValue.html) {#jsvalue}
This is a representation of an object owned by JavaScript, this is a root catch-all type for `wasm-bindgen`.
Any type that comes from `wasm-bindgen` is a `JsValue` and this is because JavaScript doesn't have
a strong type system so any function that accepts a variable `x` doesn't define its type so `x` can be
a valid JavaScript value; hence `JsValue`. If you are working with imported functions or types that
accept a `JsValue`, then any imported value is _technically_ valid.
`JsValue` can be accepted by a function but that function may still only accept certain types and this
can lead to panics - so when using raw `wasm-bindgen` APIs check the documentation of the JavaScript
being imported as to whether an exception (panic) will be raised if that value is not a certain type.
_[`JsValue` documentation](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/struct.JsValue.html)._
### [`JsCast`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/trait.JsCast.html) {#JsCast}
Rust has a strong type system and JavaScript...doesn't 😞. In order for Rust to maintain these
strong types but still be convenient the WebAssembly group came up with a pretty neat trait `JsCast`.
Its job is to help you move from one JavaScript "type" to another, which sounds vague, but it means
that if you have one type which you know is really another then you can use the functions of `JsCast`
to jump from one type to the other. It's a nice trait to get to know when working with `web-sys`,
`wasm_bindgen`, `js-sys` - you'll notice lots of types will implement `JsCast` from those crates.
`JsCast` provides both checked and unchecked methods of casting - so that at runtime if you are
unsure what type a certain object is you can try to cast it which returns possible failure types like
[`Option`](https://doc.rust-lang.org/std/option/enum.Option.html) and
[`Result`](https://doc.rust-lang.org/std/result/enum.Result.html).
A common example of this in [`web-sys`](./web-sys.mdx) is when you are trying to get the
target of an event, you might know what the target element is but the
[`web_sys::Event`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Event.html) API will always return an [`Option<web_sys::EventTarget>`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Event.html#method.target)
so you will need to cast it to the element type. so you can call its methods.
```rust
// need to import the trait.
use wasm_bindgen::JsCast;
use web_sys::{Event, EventTarget, HtmlInputElement, HtmlSelectElement};
fn handle_event(event: Event) {
let target: EventTarget = event
.target()
.expect("I'm sure this event has a target!");
// maybe the target is a select element?
if let Some(select_element) = target.dyn_ref::<HtmlSelectElement>() {
// do something amazing here
return;
}
// if it wasn't a select element then I KNOW it's a input element!
let input_element: HtmlInputElement = target.unchecked_into();
}
```
The [`dyn_ref`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/trait.JsCast.html#method.dyn_ref)
method is a checked cast that returns an `Option<&T>` which means the original type
can be used again if the cast failed and thus returned `None`. The
[`dyn_into`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/trait.JsCast.html#method.dyn_into)
method will consume `self`, as per convention for into methods in Rust, and the type returned is
`Result<T, Self>`. If the casting fails, the original `Self` value is returned in `Err`. You can try again
or do something else with the original type.
_[`JsCast` documentation](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/trait.JsCast.html)._
### [`Closure`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/closure/struct.Closure.html)
The `Closure` type provides a way to transfer Rust closures to JavaScript, the closures past to
JavaScript must have a `'static` lifetime for soundness reasons.
This type is a "handle" in the sense that whenever it is dropped it will invalidate the JS
closure that it refers to. Any usage of the closure in JS after the Closure has been dropped will
raise an exception.
`Closure` is often used when you are working with a `js-sys` or `web-sys` API that accepts a type
[`&js_sys::Function`](https://rustwasm.github.io/wasm-bindgen/api/js_sys/struct.Function.html).
An example of using a `Closure` in Yew can be found in the [Using `Closure` section](../html/events.mdx#using-closure-verbose)
on the [Events](../html/events.mdx) page.
_[`Closure` documentation](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/closure/struct.Closure.html)._
## [`js-sys`](https://crates.io/crates/js-sys)
The `js-sys` crate provides bindings / imports of JavaScript's standard, built-in objects, including
their methods and properties.
This does not include any web APIs as this is what [`web-sys`](./web-sys.mdx) is for!
_[`js-sys` documentation](https://rustwasm.github.io/wasm-bindgen/api/js_sys/index.html)._
## [`wasm-bindgen-futures`](https://crates.io/crates/wasm-bindgen-futures)
The `wasm-bindgen-futures` crate provides a bridge for working with JavaScript Promise types as a
Rust [`Future`](https://doc.rust-lang.org/stable/std/future/trait.Future.html), and contains
utilities to turn a rust Future into a JavaScript Promise. This can be useful when working with
asynchronous or otherwise blocking work in Rust (wasm), and provides the ability to interoperate
with JavaScript events and JavaScript I/O primitives.
There are three main interfaces in this crate currently:
1. [`JsFuture`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen_futures/struct.JsFuture.html) -
A type that is constructed with a [`Promise`](https://rustwasm.github.io/wasm-bindgen/api/js_sys/struct.Promise.html)
and can then be used as a `Future<Output=Result<JsValue, JsValue>>`. This Rust future will resolve
or reject with the value coming out of the `Promise`.
2. [`future_to_promise`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen_futures/fn.future_to_promise.html) -
Converts a Rust `Future<Output=Result<JsValue, JsValue>>` into a
JavaScript `Promise`. The futures result will translate to either a resolved or rejected
`Promise` in JavaScript.
3. [`spawn_local`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen_futures/fn.spawn_local.html) -
Spawns a `Future<Output = ()>` on the current thread. This is the best way
to run a Future in Rust without sending it to JavaScript.
_[`wasm-bindgen-futures` documentation](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen_futures/index.html)._
### [`spawn_local`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen_futures/fn.spawn_local.html)
`spawn_local` is going to be the most commonly used part of the `wasm-bindgen-futures` crate in Yew
as this helps when using libraries that have async APIs.
```rust ,no_run
use web_sys::console;
use wasm_bindgen_futures::spawn_local;
async fn my_async_fn() -> String { String::from("Hello") }
spawn_local(async {
let mut string = my_async_fn().await;
string.push_str(", world!");
// console log "Hello, world!"
console::log_1(&string.into());
});
```
Yew has also added support for futures in certain APIs, most notably you can create a
`callback_future` which accepts an `async` block - this uses `spawn_local` internally.
_[`spawn_local` documentation](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen_futures/fn.spawn_local.html)._

View File

@ -0,0 +1,245 @@
---
title: 'web-sys'
description: 'The web-sys crate provides bindings for Web APIs.'
---
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
The [`web-sys` crate](https://crates.io/crates/web-sys) provides bindings for Web APIs. This is
procedurally generated from browser WebIDL which is why some names are so long and why some types are vague.
## Features in `web-sys`
The `web-sys` crate with all of it's features enabled can add lots of bloat to a Wasm application,
in order to get around this issue most types are feature gated so that you only include the types
you require for your application. Yew includes a number of features from `web-sys` and
exposes some types in it's public API, you will often need to add `web-sys` as a dependency yourself.
## Inheritance in `web-sys`
In the [Simulating inheritance section](./wasm-bindgen.mdx#simulating-inheritance) you can read how in
general Rust provides an approach to simulate inheritance in JavaScript. This is very important in
`web-sys` as understanding what methods are available on a type means understanding it's inheritance.
This section is going to look at a specific element and list out it's inheritance using Rust by
calling [`Deref::deref`](https://doc.rust-lang.org/std/ops/trait.Deref.html#tymethod.deref) until
the value is [`JsValue`](./wasm-bindgen.mdx#jsvalue):
```rust
use std::ops::Deref;
use web_sys::{
Element,
EventTarget,
HtmlElement,
HtmlTextAreaElement,
Node,
};
fn inheritance_of_text_area(text_area: HtmlTextAreaElement) {
// HtmlTextAreaElement is <textarea> in html.
let html_element: &HtmlElement = text_area.deref();
let element: &Element = html_element.deref();
let node: &Node = element.deref();
let event_target: &EventTarget = node.deref();
// Notice we've moved from web-sys types now into built-in
// JavaScript types which are in the js-sys crate.
let object: &js_sys::Object = event_target.deref();
// Notice we've moved from js-sys type to the root JsValue from
// the wasm-bindgen crate.
let js_value: &wasm_bindgen::JsValue = object.deref();
// Using deref like this means we have to manually traverse
// the inheritance tree, however, you can call JsValue methods
// on the HtmlTextAreaElement type.
// The `is_string` method comes from JsValue.
assert!(!text_area.is_string());
// empty function just to prove we can pass HtmlTextAreaElement as a
// &EventTarget.
fn this_function_only_takes_event_targets(targets: &EventTarget) {};
// The compiler will walk down the deref chain in order to match the types here.
this_function_only_takes_event_targets(&text_area);
// The AsRef implementations allow you to treat the HtmlTextAreaElement
// as an &EventTarget.
let event_target: &EventTarget = text_area.as_ref();
}
```
_[Inheritance in `web-sys` in The `wasm-bindgen` Guide](https://rustwasm.github.io/wasm-bindgen/web-sys/inheritance.html)._
## The `Node` in `NodeRef`
Yew uses a [`NodeRef`](concepts/function-components/node-refs.mdx) in order to provide a way for keeping a reference to
a `Node` made by the [`html!`](concepts/html/introduction.mdx) macro. The `Node` part of `NodeRef` is referring to
[`web_sys::Node`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Node.html). The
`NodeRef::get` method will return a `Option<Node>` value, however, most of the time in Yew you want
to cast this value to a specific element so you can use it's specific methods. This casting
can be done using [`JsCast`](./wasm-bindgen.mdx#JsCast) on the `Node` value, if present, but Yew
provides the `NodeRef::cast` method to perform this casting for convenience and so that you don't
necessarily have to include the `wasm-bindgen` dependency for the `JsCast` trait.
The two code blocks below do essentially the same thing, the first is using `NodeRef::cast` and
the second is using [`JsCast::dyn_into`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/trait.JsCast.html#method.dyn_into)
on the `web_sys::Node` returned from `NodeRef::get`.
<Tabs>
<TabItem value="Using NodeRef::cast" label="Using NodeRef::cast">
```rust
use web_sys::HtmlInputElement;
use yew::NodeRef;
fn with_node_ref_cast(node_ref: NodeRef) {
if let Some(input) = node_ref.cast::<HtmlInputElement>() {
// do something with HtmlInputElement
}
}
```
</TabItem>
<TabItem value="Using NodeRef::get" label="Using NodeRef::get">
```rust
use wasm_bindgen::JsCast;
use web_sys::HtmlInputElement;
use yew::NodeRef;
fn with_jscast(node_ref: NodeRef) {
if let Some(input) = node_ref
.get()
.and_then(|node| node.dyn_into::<HtmlInputElement>().ok()) {
// do something with HtmlInputElement
}
}
```
</TabItem>
</Tabs>
## JavaScript example to Rust
This section is to help show that any examples that use JavaScript to interact with the Web APIs
can be adapted and written using Rust with `web-sys`.
### JavaScript example
```js
document.getElementById('mousemoveme').onmousemove = (e) => {
// e = Mouse event.
var rect = e.target.getBoundingClientRect()
var x = e.clientX - rect.left //x position within the element.
var y = e.clientY - rect.top //y position within the element.
console.log('Left? : ' + x + ' ; Top? : ' + y + '.')
}
```
### `web-sys` example
Using `web-sys` alone the above JavaSciprt example could be implemented like this:
```toml title=Cargo.toml
[dependencies]
wasm-bindgen = "0.2"
[dependencies.web-sys]
version = "0.3"
# We need to enable all the web-sys features we want to use!
features = [
"console",
"Document",
"HtmlElement",
"MouseEvent",
"DomRect",
]
```
```rust ,no_run
use wasm_bindgen::{prelude::Closure, JsCast};
use web_sys::{console, Document, HtmlElement, MouseEvent};
let mousemove = Closure::<dyn Fn(MouseEvent)>::wrap(Box::new(|e| {
let rect = e
.target()
.expect("mouse event doesn't have a target")
.dyn_into::<HtmlElement>()
.expect("event target should be of type HtmlElement")
.get_bounding_client_rect();
let x = (e.client_x() as f64) - rect.left();
let y = (e.client_y() as f64) - rect.top();
console::log_1(&format!("Left? : {} ; Top? : {}", x, y).into());
}));
Document::new()
.expect("global document not set")
.get_element_by_id("mousemoveme")
.expect("element with id `mousemoveme` not present")
.unchecked_into::<HtmlElement>()
.set_onmousemove(mousemove.as_ref().dyn_ref());
// we now need to save the `mousemove` Closure so that when
// this event fires the closure is still in memory.
```
This version is much more verbose, but you will probably notice part of that is because of failure
types reminding us that some of these function calls have invariants that must be held otherwise will
cause a panic in Rust. Another part of the verbosity is the calls to `JsCast` in order to cast into
different types so that you can call it's specific methods.
### Yew example
In Yew you will mostly be creating [`Callback`](concepts/function-components/callbacks.mdx)s to use in the
[`html!`](concepts/html/introduction.mdx) macro so the example is going to use this approach instead of completely copying
the approach above:
```toml title=Cargo.toml
[dependencies.web-sys]
version = "0.3"
# We need to enable the `DomRect` feature in order to use the
# `get_bounding_client_rect` method.
features = [
"console",
"HtmlElement",
"MouseEvent",
"DomRect",
]
```
```rust
use web_sys::{console, HtmlElement, MouseEvent};
use yew::{
html,
Callback, TargetCast,
};
let onmousemove = Callback::from(|e: MouseEvent| {
if let Some(target) = e.target_dyn_into::<HtmlElement>() {
let rect = target.get_bounding_client_rect();
let x = (e.client_x() as f64) - rect.left();
let y = (e.client_y() as f64) - rect.top();
console::log_1(&format!("Left? : {} ; Top? : {}", x, y).into());
}
});
html! {
<div id="mousemoveme" {onmousemove}></div>
};
```
## External libraries
`web-sys` is a raw binding to the Web API so it comes with some pain in Rust because it was not
designed with Rust or even a strong type system in mind, this is where community crates come in to
provide abstractions over `web-sys` to provide more idiomatic Rust APIs.
_[External libraries page](/community/external-libs)_

View File

@ -0,0 +1,174 @@
---
title: 'Contexts'
sidebar_label: Contexts
description: 'Using contexts to pass deeply nested data'
---
Usually, data is passed from a parent component to a child component via props.
But passing props can become verbose and annoying if you have to pass them through many components in the middle,
or if many components in your app need the same information. Context solve this problem by allowing a
parent component to make data available to _any_ component in the tree below it, no matter how deep,
without having to pass it down with props.
## The problem with props: "Prop Drilling"
Passing [props](./function-components/properties.mdx) is a great way to pass data directly from parent to a child.
They become cumbersome to pass down through deeply nested component tree or when multiple components share the same data.
A common solution to data sharing is lifting the data to a common ancestor and making the children take it as props.
However, this can lead to cases where the prop has to go through multiple components in order to reach the component needs it.
This situation is called "Prop Drilling".
Consider the following example which passes down the theme using props:
```rust
use yew::{html, Children, Component, Context, Html, Properties, function_component};
#[derive(Clone, PartialEq)]
pub struct Theme {
foreground: String,
background: String,
}
#[derive(PartialEq, Properties)]
pub struct NavbarProps {
theme: Theme,
}
#[function_component]
fn Navbar(props: &NavbarProps) -> Html {
html! {
<div>
<Title theme={props.theme.clone()}>
{ "App title" }
</Title>
<NavButton theme={props.theme.clone()}>
{ "Somewhere" }
</NavButton>
</div>
}
}
#[derive(PartialEq, Properties)]
pub struct ThemeProps {
theme: Theme,
children: Children,
}
#[function_component]
fn Title(_props: &ThemeProps) -> Html {
html! {
// impl
}
}
#[function_component]
fn NavButton(_props: &ThemeProps) -> Html {
html! {
// impl
}
}
/// App root
#[function_component]
fn App() -> Html {
let theme = Theme {
foreground: "yellow".to_owned(),
background: "pink".to_owned(),
};
html! {
<Navbar {theme} />
}
}
```
We "drill" the theme prop through `Navbar` so that it can reach `Title` and `NavButton`.
It would be nice if `Title` and `NavButton`, the components that need access to the theme, can just access the theme
without having to pass it to them as prop. Contexts solve this problem by allowing a parent to pass data, theme in this case,
to its children.
## Using Contexts
### Step 1: Providing the context
A context provider is required to consume the context. `ContextProvider<T>`, where `T` is the context struct is used as the provider.
`T` must implement `Clone` and `PartialEq`. `ContextProvider` is the component whose children will have the context available to them.
The children are re-rendered when the context changes. A struct is used to define what data is to be passed. The `ContextProvider` can be used as:
```rust
use std::rc::Rc;
use yew::prelude::*;
#[derive(Clone, Debug, PartialEq)]
struct Theme {
foreground: String,
background: String,
}
#[function_component]
fn NavButton() -> Html {
let theme = use_context::<Theme>();
html! {
// use theme
}
}
#[function_component]
fn App() -> Html {
let theme = use_memo(|_| Theme {
foreground: "yellow".to_owned(),
background: "pink".to_owned(),
}, ());
html! {
<ContextProvider<Rc<Theme>> context={theme}>
<NavButton />
</ContextProvider<Rc<Theme>>>
}
}
```
### Step 2: Consuming context
#### Function components
`use_context` hook is used to consume contexts in function components.
See [docs for use_context](https://yew-rs-api.web.app/next/yew/functional/fn.use_context.html) to learn more.
#### Struct components
We have 2 options to consume contexts in struct components:
- [Higher Order Components](../advanced-topics/struct-components/hoc.mdx): A higher order function component will consume the context and pass the data to the struct component which requires it.
- Consume context directly in struct component. See [example of struct component as a consumer](https://github.com/yewstack/yew/tree/master/examples/contexts/src/struct_component_subscriber.rs)
## Use cases
Generally, if some data is needed by distant components in different parts of the tree, it's likely that context will help you.
Here's some examples of such cases:
- **Theming**: You can put a context at the top of the app that holds your app theme and use it to adjust the visual appearance, as shown in the above example.
- **Current user account**: In many cases, components need to know the current logged-in user. You can use a context to provide the current user object to the components.
### Considerations to make before using contexts
Contexts are very easy to use. That makes them very easy to misuse/overuse.
Just because you can use a context to share props to components multiple levels deep, doesn't mean that you should.
For example, you may be able to extract a component and pass that component as a child to another component. For example,
you may have a `Layout` component which takes `articles` as prop and passes it down to `ArticleList` component.
You should refactor the `Layout` component to take children as props and display `<Layout> <ArticleList {articles} /> </Layout>`.
## Mutating context value a child
Because of Rust's ownership rules, a context cannot have a method that takes `&mut self` that can be called by children.
In order to mutate a context's value, we must combine it with a reducer. This is done by using the
[`use_reducer`](https://yew-rs-api.web.app/next/yew/functional/fn.use_reducer.html) hook.
The [contexts example](https://github.com/yewstack/yew/tree/master/examples/contexts) demonstrates mutable contexts
with the help of contexts
## Further reading
- The [contexts example](https://github.com/yewstack/yew/tree/master/examples/contexts)

View File

@ -0,0 +1,74 @@
---
title: 'Callbacks'
---
Callbacks are used to asynchronously communicate upwards the components tree and with other things like agents or the DOM during event handling.
Internally their type is just an `Fn` wrapped in `Rc` to allow them to be cheaply cloned.
They have an `emit` function if you want to call them manually.
```rust
use yew::{html, Component, Context, Html, Callback};
let cb: Callback<String, String> = Callback::from(move |name: String| {
format!("Bye {}", name)
});
let result = cb.emit(String::from("Bob")); // call the callback
// web_sys::console::log_1(&result.into()); // if uncommented will print "Bye Bob"
```
## Passing callbacks as props
A common pattern in yew is to create a callback and pass it down as a prop.
```rust
use yew::{function_component, html, Html, Properties, Callback};
#[derive(Properties, PartialEq)]
pub struct Props {
pub on_name_entry: Callback<String>,
}
#[function_component]
fn HelloWorld(props: &Props) -> Html {
props.on_name_entry.emit(String::from("Bob"));
html! { "Hello" }
}
// Then supply the prop
#[function_component]
fn App() -> Html {
let on_name_entry: Callback<String> = Callback::from(move |name: String| {
let greeting = format!("Hey, {}!", name);
// web_sys::console::log_1(&greeting.into()); // if uncommented will print
});
html! { <HelloWorld {on_name_entry} /> }
}
```
## DOM Events and Callbacks
Callbacks are also used to hook into DOM events.
For example here we define a callback that will be called when the user clicks the button:
```rust
use yew::{function_component, html, Html, Properties, Callback};
#[function_component]
fn App() -> Html {
let onclick = Callback::from(move |_| {
let greeting = String::from("Hi there");
// web_sys::console::log_1(&greeting.into()); // if uncommented will print
});
html! {
<button {onclick}>{ "Click" }</button>
}
}
```

View File

@ -0,0 +1,41 @@
---
title: 'Children'
---
`Children` is a special prop type that allows you to recieve nested `Html` that is provided like html child elements.
```rust
use yew::{function_component, html, Html, Properties, Children};
#[function_component]
fn App() -> Html {
html! {
// highlight-start
<HelloWorld>
<span>{"Hey what is up ;)"}</span>
<h1>{"THE SKY"}</h1>
</HelloWorld>
// highlight-end
}
}
#[derive(Properties, PartialEq)]
pub struct Props {
// highlight-next-line
pub children: Children, // the field name `children` is important!
}
#[function_component]
fn HelloWorld(props: &Props) -> Html {
html! {
<div class="very-stylized-container">
// highlight-next-line
{ for props.children.iter() } // you can forward children like this
</div>
}
}
```
## Further reading
- [Advanced ways to handle children](../../advanced-topics/children)

View File

@ -0,0 +1,11 @@
---
title: 'Communication between components'
---
## Parent to child messaging
Pass data as [props](./properties) that cause a rerender, this is the way to pass messages to children.
## Child to parent messaging
Pass down a callback via props, that the child on an event can call. [Example](callbacks#passing-callbacks-as-props)

View File

@ -0,0 +1,44 @@
---
title: 'Generic Components'
description: 'The #[function_component] attribute'
---
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
The `#[function_component]` attribute also works with generic functions for creating generic components.
```rust
use std::fmt::Display;
use yew::{function_component, html, Properties, Html};
#[derive(Properties, PartialEq)]
pub struct Props<T>
where
T: PartialEq,
{
data: T,
}
#[function_component]
pub fn MyGenericComponent<T>(props: &Props<T>) -> Html
where
T: PartialEq + Display,
{
html! {
<p>
{ &props.data }
</p>
}
}
// then can be used like this
html! {
<MyGenericComponent<i32> data=123 />
};
// or
html! {
<MyGenericComponent<String> data={"foo".to_string()} />
};
```

View File

@ -0,0 +1,113 @@
---
title: 'Custom Hooks'
---
## Defining custom Hooks
Component's stateful logic can be extracted into reusable function by creating custom Hooks.
Consider that we wish to create an event listener that listens to an event on the `window`
object.
```rust
use yew::prelude::*;
use gloo::events::EventListener;
use gloo::utils::window;
use std::mem::drop;
#[function_component(ShowStorageChanged)]
pub fn show_storage_changed() -> Html {
let state_storage_changed = use_state(|| false);
{
let state_storage_changed = state_storage_changed.clone();
use_effect(|| {
let listener = EventListener::new(&window(), "storage", move |_| state_storage_changed.set(true));
move || { drop(listener); }
});
}
html! { <div>{"Storage Event Fired: "}{*state_storage_changed}</div> }
}
```
There's one problem with this code: the logic can't be reused by another component.
If we build another component which keeps track of the an event,
instead of copying the code, we can move the logic into a custom hook.
We'll start by creating a new function called `use_event`.
The `use_` prefix denotes that a function is a hook.
This function will take an event target, an event type and a callback.
All hooks must be marked by `#[hook]` to function as as hook.
```rust
use web_sys::{Event, EventTarget};
use std::borrow::Cow;
use gloo::events::EventListener;
use yew::prelude::*;
#[hook]
pub fn use_event<E, F>(target: &EventTarget, event_type: E, callback: F)
where
E: Into<Cow<'static, str>>,
F: Fn(&Event) + 'static,
{
todo!()
}
```
This is a simple hook which can be created by composing built-in hooks. For this example, we'll use the
`use_effect_with_deps` hook, so an event listener can be recreated when the hook arguments change.
```rust
use yew::prelude::*;
use web_sys::{Event, EventTarget};
use std::borrow::Cow;
use std::rc::Rc;
use gloo::events::EventListener;
#[hook]
pub fn use_event<E, F>(target: &EventTarget, event_type: E, callback: F)
where
E: Into<Cow<'static, str>>,
F: Fn(Event) + 'static,
{
#[derive(PartialEq, Clone)]
struct EventDependents {
target: EventTarget,
event_type: Cow<'static, str>,
callback: Callback<Event>,
}
let deps = EventDependents {
target: target.clone(),
event_type: event_type.into(),
callback: Callback::from(callback),
};
use_effect_with_deps(
|deps| {
let EventDependents {
target,
event_type,
callback,
} = deps.clone();
let listener = EventListener::new(&target, event_type, move |e| {
callback.emit(e.clone());
});
move || {
drop(listener);
}
},
deps,
);
}
```
Although this approach works in almost all cases, it can't be used to write primitive hooks like the pre-defined hooks we've been using already.
View the docs on [docs.rs](https://docs.rs/yew) for documentation and `hooks` directory to see implementations of pre-defined hooks.

View File

@ -0,0 +1,51 @@
---
title: 'Hooks'
slug: /concepts/function-components/hooks
---
## Hooks
Hooks are functions that let you store state and perform side-effects.
Yew comes with a few pre-defined Hooks. You can also create your own or discover many [community made hooks](/community/awesome#hooks).
## Rules of hooks
1. A hook function name always has to start with `use_`
2. Hooks can only be used in the following locations:
- Top level of a function / hook.
- Blocks inside a function / hook, given it's not already branched.
- In the condition of a top level `if` expression inside a function / hook.
- In the scrutinee of a top level `match` expression inside a function / hook.
3. Hooks must be called in the same order for every render. Returning early is only allowed when using [Suspense](../../suspense.mdx)
These rules are enforced by either compile time or run-time errors.
### Pre-defined Hooks
Yew comes with the following predefined Hooks:
- `use_state`
- `use_state_eq`
- `use_memo`
- `use_callback`
- `use_mut_ref`
- `use_node_ref`
- `use_reducer`
- `use_reducer_eq`
- `use_effect`
- `use_effect_with_deps`
- `use_context`
- `use_force_update`
The documentation for these hooks can be found in the [Yew API docs](https://yew-rs-api.web.app/next/yew/functional/)
### Custom Hooks
There are cases where you want to define your own Hooks to encapsulate potentially stateful logic from a component into reusable functions.
See the [Defining custom hooks](concepts/function-components/hooks/custom-hooks.mdx#defining-custom-hooks) section for more information.
## Further reading
- The React documentation has a section on [React hooks](https://reactjs.org/docs/hooks-intro.html).
These are not exactly the same as Yew's hooks, but the underlying concept is similar.

View File

@ -0,0 +1,76 @@
---
title: 'Function Components'
slug: /concepts/function-components
---
Lets revisit this previous statement:
> Yew centrally operates on the idea of keeping everything that a reusable piece of
> UI may need in one place - rust files.
We will refine this statement, by introducing the concept that will define the logic and
presentation behaviour of an application: "components".
## What are Components?
Components are the building blocks of Yew.
They:
- Take arguments in form of [Props](./properties.mdx)
- Can have their own state
- Compute pieces of HTML visible to the user (DOM)
## Two flavours of Yew Components
You are currently reading about function components - the recommended way to write components
when starting with Yew and when writing simple presentation logic.
There is a more advanced, but less accessible, way to write components - [Struct components](advanced-topics/struct-components/introduction.mdx).
They allow very detailed control, though you will not need that level of detail most of the time.
## Creating function components
To create a function component add the `#[function_component]` attribute to a function.
By convention, the function is named in PascalCase, like all components, to contrast its
use to normal html elements inside the `html!` macro.
```rust
use yew::{function_component, html, Html};
#[function_component]
fn HelloWorld() -> Html {
html! { "Hello world" }
}
// Then somewhere else you can use the component inside `html!`
#[function_component]
fn App() -> Html {
html! { <HelloWorld /> }
}
```
## What happens to components
When rendering, Yew will build a virtual tree of these components.
It will call the view function of each (function) component to compute a virtual version (VDOM) of the DOM
that you as the library user see as the `Html` type.
For the previous example this would look like this:
```xhtml
<App>
<HelloWorld>
<p>"Hello world"</p>
</HelloWord>
</App>
```
When an update is necessary, Yew will again call the view function and reconcile the new virtual DOM with its
previous version and only propagate the new/changed/necessary parts to the actual DOM.
This is what we call **rendering**.
:::note
Behind the scenes `Html` is just an alias for `VNode` - virtual node.
:::

View File

@ -0,0 +1,21 @@
---
title: 'Node Refs'
description: 'Out-of-band DOM access'
---
The `ref` attribute can be used to attach the `NodeRef` to an HTML element. In callbacks,
you can then get the DOM `Element` that the ref is attached to. This can be used to make
changes to the DOM outside of the `view` lifecycle method, retrieve the value of an `<input>`
and other direct interactions with the DOM via the javascript API.
This is useful for getting ahold of canvas elements, or scrolling to different sections of a page.
:::caution
Do not manually modify the DOM tree that is rendered by Yew. Treat the `NodeRef` as a read-only
access, if you are unsure.
:::
## Further Reading
- [use_node_ref hook](https://yew-rs-api.web.app/next/yew/functional/fn.use_node_ref.html)
- [`node_refs` example](https://github.com/yewstack/yew/tree/master/examples/node_refs)

View File

@ -0,0 +1,300 @@
---
title: 'Properties'
description: 'Parent to child communication'
---
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
:::note
Properties are often shortened as "Props".
:::
Properties are essentially component arguments that Yew can keep watch on.
A type has to implement the `Properties` trait before it can be used as the properties of a component.
## Reactivity
Yew checks if props have changed when reconciling the vdom during rerendering, to know if nested components needs to be rerendered.
This way Yew can be considered a very reactive framework as changes from the parent will always be propagated downwards
and the view will never be out of sync from the data coming from props/state.
:::tip
If you have not yet completed the [tutorial](../../tutorial), try it out and test this reactivity yourself!
:::
## Derive macro
Yew provides a derive macro to easily implement the `Properties` trait on structs.
Types for which you derive `Properties` must also implement `PartialEq` so Yew can do data comparison.
```rust
use yew::Properties;
#[derive(Properties, PartialEq)]
pub struct Props {
pub is_loading: bool,
}
```
## Use in function components
The attribute `#[function_component]` allows to optionally receive Props in the function arguments. To supply them,
they are assigned via attributes in the `html!` macro.
<Tabs>
<TabItem value="with-props" label="With Props">
```rust
use yew::{function_component, html, Html, Properties};
#[derive(Properties, PartialEq)]
pub struct Props {
pub is_loading: bool,
}
#[function_component]
fn HelloWorld(props: &Props) -> Html {
html! { <>{"Am I loading? - "}{props.is_loading.clone()}</> }
}
// Then supply the prop
#[function_component]
fn App() -> Html {
html! {<HelloWorld is_loading={true} />}
}
```
</TabItem>
<TabItem value="no-props" label="No Props">
```rust
use yew::{function_component, html, Html};
#[function_component]
fn HelloWorld() -> Html {
html! { "Hello world" }
}
// No props to supply
#[function_component]
fn App() -> Html {
html! {<HelloWorld />}
}
```
</TabItem>
</Tabs>
## Derive macro field attributes
When deriving `Properties` all fields are required by default.
The following attributes allow you to give your props default values which will be used when parent has not set them.
:::tip
Attributes aren't visible in Rustdoc generated documentation.
The doc strings of your properties should mention whether a prop is optional and if it has a special default value.
:::
<Tabs>
<TabItem value="prop_or_default" label="#[prop_or_default]">
Initialize the prop value with the default value of the field's type using the `Default` trait.
```rust
use yew::{function_component, html, Html, Properties};
#[derive(Properties, PartialEq)]
pub struct Props {
// highlight-start
#[prop_or_default]
// highlight-end
pub is_loading: bool,
}
#[function_component]
fn HelloWorld(props: &Props) -> Html {
if props.is_loading.clone() {
html! { "Loading" }
} else {
html! { "Hello world" }
}
}
// Then use like this with default
#[function_component]
fn Case1() -> Html {
html! {<HelloWorld />}
}
// Or no override the default
#[function_component]
fn Case2() -> Html {
html! {<HelloWorld is_loading={true} />}
}
```
</TabItem>
<TabItem value="prop_or_value" label="#[prop_or(value)]">
Use `value` to initialize the prop value. `value` can be any expression that returns the field's type.
For example, to default a boolean prop to `true`, use the attribute `#[prop_or(true)]`. The expression
is evaluated when the properties are constructed and no explicit value has been given.
```rust
use yew::{function_component, html, Html, Properties};
#[derive(Properties, PartialEq)]
pub struct Props {
// highlight-start
#[prop_or("Bob".to_string())]
// highlight-end
pub name: String,
}
#[function_component]
fn HelloWorld(props: &Props) -> Html {
html! {<>{"Hello world"}{props.name.clone()}</>}
}
// Then use like this with default
#[function_component]
fn Case1() -> Html {
html! {<HelloWorld />}
}
// Or no override the default
#[function_component]
fn Case2() -> Html {
html! {<HelloWorld name={"Sam".to_string()} />}
}
```
</TabItem>
<TabItem value="prop_or_else_function" label="#[prop_or_else(function)]">
Call `function` to initialize the prop value. `function` should have the signature `FnMut() -> T` where `T` is the field type.
The function is called when no explicit value has been given for that attribute.
```rust
use yew::{function_component, html, Html, Properties};
fn create_default_name() -> String {
"Bob".to_string()
}
#[derive(Properties, PartialEq)]
pub struct Props {
// highlight-start
#[prop_or_else(create_default_name)]
// highlight-end
pub name: String,
}
#[function_component]
fn HelloWorld(props: &Props) -> Html {
html! {<>{"Hello world"}{props.name.clone()}</>}
}
// Then use like this with default
#[function_component]
fn Case1() -> Html {
html! {<HelloWorld />}
}
// Or no override the default
#[function_component]
fn Case2() -> Html {
html! {<HelloWorld name={"Sam".to_string()} />}
}
```
</TabItem>
</Tabs>
## Memory/speed overhead of using Properties
Internally properties are reference counted. This means that only a shared pointer is passed down the component tree for props.
It saves us from the cost of having to clone the entire props, which might be expensive.
:::tip
Make use of `AttrValue` which is our custom type for attribute values instead of defining them as String or another similar type.
:::
## Props macro
The `yew::props!` macro allows you to build properties the same way the `html!` macro does it.
The macro uses the same syntax as a struct expression except that you can't use attributes or a base expression (`Foo { ..base }`).
The type path can either point to the props directly (`path::to::Props`) or the associated properties of a component (`MyComp::Properties`).
```rust
use yew::{function_component, html, Html, Properties, props, virtual_dom::AttrValue};
#[derive(Properties, PartialEq)]
pub struct Props {
#[prop_or(AttrValue::from("Bob"))]
pub name: AttrValue,
}
#[function_component]
fn HelloWorld(props: &Props) -> Html {
html! {<>{"Hello world"}{props.name.clone()}</>}
}
#[function_component]
fn App() -> Html {
// highlight-start
let pre_made_props = props! {
Props {} // Notice we did not need to specify name prop
};
// highlight-end
html! {<HelloWorld ..pre_made_props />}
}
```
## Evaluation Order
Props are evaluated in the order they're specified, as shown by the following example:
```rust
#[derive(yew::Properties, PartialEq)]
struct Props { first: usize, second: usize, last: usize }
fn main() {
let mut g = 1..=3;
let props = yew::props!(Props { first: g.next().unwrap(), second: g.next().unwrap(), last: g.next().unwrap() });
assert_eq!(props.first, 1);
assert_eq!(props.second, 2);
assert_eq!(props.last, 3);
}
```
## Anti Patterns
While almost any Rust type can be passed as properties, there are some anti-patterns that should be avoided.
These include, but are not limited to:
1. Using `String` type instead of `AttrValue`. <br />
**Why is this bad?** `String` can be expensive to clone.
Cloning is often needed when the prop value is used with hooks and callbacks. `AttrValue` is either
a reference-counted string (`Rc<str>`) or a `&'static str`, thus very cheap to clone.<br />
**Note**: `AttrValue` internally is `IString` from [implicit-clone](https://crates.io/crates/implicit-clone)
See that crate to learn more.
2. Using interior mutability. <br />
**Why is this bad?** Interior mutability (such as with `RefCell`, `Mutex`, etc.) should
_generally_ be avoided. It can cause problems with re-renders (Yew doesn't know when state has changed)
so you may have to manually force a render. Like all things, it has its place. Use it with caution.
3. You tell us. Did you run into an edge-case you wish you knew about earlier? Feel free to create an issue
or PR a fix to this documentation.

View File

@ -0,0 +1,39 @@
---
title: 'Pure Components'
---
A function component is considered [pure] when the returned `Html` is deterministically derived
from its props, and its view function mutates no state or has other side-effects.
[pure]: https://en.wikipedia.org/wiki/Pure_function
For example below is a pure component. For a given prop `is_loading` it will always result in the same `Html` without any side effects.
```rust
use yew::{Properties, function_component, Html, html};
#[derive(Properties, PartialEq)]
pub struct Props {
pub is_loading: bool,
}
#[function_component]
fn HelloWorld(props: &Props) -> Html {
if props.is_loading {
html! { "Loading" }
} else {
html! { "Hello world" }
}
}
```
:::note
If you have an internal pure component that makes no use of hooks and other component machinery, you can often write it instead
as a normal function returning `Html` and avoid a bit of overhead for Yew, related to running the component lifecycle. Use
[expression syntax](concepts/html/literals-and-expressions.mdx#expressions) to render them in `html!`.
:::
## Impure components
You might wonder if a component can be impure if it does not use any globals, since its just a function that is called every render.
This is where the next topic comes in - [hooks](./hooks)

View File

@ -0,0 +1,26 @@
---
title: 'State'
---
## General view of how to store state
This table can be used as a guide when deciding what state storing type fits best for your use case:
| Hook | Type | Rerender when? | Scope |
| ------------------------ | -------------------------- | ---------------------------- | ------------------- |
| [use_state] | `T` | got set | component instance |
| [use_state_eq] | `T: PartialEq` | got set with diff. value | component instance |
| [use_reducer] | `T: Reducible` | got reduced | component instance |
| [use_reducer_eq] | `T: Reducible + PartialEq` | got reduced with diff. value | component instance |
| [use_memo] | `Deps -> T` | dependencies changed | component instance |
| [use_callback] | `Deps -> Callback<E>` | dependencies changed | component instance |
| [use_mut_ref] | `T` | - | component instance |
| a static global variable | `T` | - | global, used by all |
[use_state]: https://yew-rs-api.web.app/next/yew/functional/fn.use_state.html
[use_state_eq]: https://yew-rs-api.web.app/next/yew/functional/fn.use_state_eq.html
[use_reducer]: https://yew-rs-api.web.app/next/yew/functional/fn.use_reducer.html
[use_reducer_eq]: https://yew-rs-api.web.app/next/yew/functional/fn.use_reducer_eq.html
[use_memo]: https://yew-rs-api.web.app/next/yew/functional/fn.use_memo.html
[use_callback]: https://yew-rs-api.web.app/next/yew/functional/fn.use_callback.html
[use_mut_ref]: https://yew-rs-api.web.app/next/yew/functional/fn.use_mut_ref.html

View File

@ -0,0 +1,130 @@
---
title: 'Classes'
description: 'A handy macro to handle classes'
---
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
## Classes
The struct `Classes` can be used to deal with HTML classes.
When pushing a string to the set, `Classes` ensures that there is one element
for every class even if a single string might contain multiple classes.
`Classes` can also be merged by using `Extend` (i.e.
`classes1.extend(classes2)`) or `push()` (i.e. `classes1.push(classes2)`). In
fact, anything that implements `Into<Classes>` can be used to push new classes
to the set.
The macro `classes!` is a convenient macro that creates one single `Classes`.
Its input accepts a comma separated list of expressions. The only requirement
is that every expression implements `Into<Classes>`.
<Tabs>
<TabItem value="Literal" label="Literal">
```rust
use yew::{classes, html};
html! {
<div class={classes!("container")}></div>
};
```
</TabItem>
<TabItem value="Multiple" label="Multiple">
```rust
use yew::{classes, html};
html! {
<div class={classes!("class-1", "class-2")}></div>
};
```
</TabItem>
<TabItem value="String" label="String">
```rust
use yew::{classes, html};
let my_classes = String::from("class-1 class-2");
html! {
<div class={classes!(my_classes)}></div>
};
```
</TabItem>
<TabItem value="Optional" label="Optional">
```rust
use yew::{classes, html};
html! {
<div class={classes!(Some("class"))} />
};
```
</TabItem>
<TabItem value="Vector" label="Vector">
```rust
use yew::{classes, html};
html! {
<div class={classes!(vec!["class-1", "class-2"])}></div>
};
```
</TabItem>
<TabItem value="Array" label="Array">
```rust
use yew::{classes, html};
let my_classes = ["class-1", "class-2"];
html! {
<div class={classes!(my_classes.as_ref())}></div>
};
```
</TabItem>
</Tabs>
## Components that accept classes
```rust
use yew::prelude::*;
#[derive(PartialEq, Properties)]
struct Props {
#[prop_or_default]
class: Classes,
fill: bool,
children: Children,
}
#[function_component]
fn MyComponent(props: &Props) -> Html {
let Props {
class,
fill,
children,
} = props;
html! {
<div
class={classes!(
"my-container-class",
fill.then(|| Some("my-fill-class")),
class.clone(),
)}
>
{ children.clone() }
</div>
}
}
```

View File

@ -0,0 +1,172 @@
---
title: 'Components'
description: 'Create complex layouts with component hierarchies'
---
## Basic
Components can be used in the `html!` macro:
```rust
use yew::prelude::*;
#[function_component]
fn MyComponent() -> Html {
html! {
{ "This component has no properties!" }
}
}
#[derive(Clone, PartialEq, Properties)]
struct Props {
user_first_name: String,
user_last_name: String,
}
#[function_component]
fn MyComponentWithProps(props: &Props) -> Html {
let Props { user_first_name, user_last_name } = props;
html! {
<>{"user_first_name: "}{user_first_name}{" and user_last_name: "}{user_last_name}</>
}
}
let props = Props {
user_first_name: "Bob".to_owned(),
user_last_name: "Smith".to_owned(),
};
html!{
<>
// No properties
<MyComponent />
// With Properties
<MyComponentWithProps user_first_name="Sam" user_last_name="Idle" />
// With the whole set of props provided at once
<MyComponentWithProps ..props.clone() />
// With Properties from a variable and specific values overridden
<MyComponentWithProps user_last_name="Elm" ..props />
</>
};
```
## Nested
Components can be passed children if they have a `children` field in their `Properties`.
```rust title="parent.rs"
use yew::prelude::*;
#[derive(PartialEq, Properties)]
struct Props {
id: String,
children: Children,
}
#[function_component]
fn Container(props: &Props) -> Html {
html! {
<div id={props.id.clone()}>
{ props.children.clone() }
</div>
}
}
html! {
<Container id="container">
<h4>{ "Hi" }</h4>
<div>{ "Hello" }</div>
</Container>
};
```
The `html!` macro allows you to pass a base expression with the `..props` syntax instead of specifying each property individually,
similar to Rust's [Functional Update Syntax](https://doc.rust-lang.org/stable/reference/expressions/struct-expr.html#functional-update-syntax).
This base expression must occur after any individual props are passed.
When passing a base props expression with a `children` field, the children passed in the `html!` macro overwrite the ones already present in the props.
```rust
use yew::prelude::*;
#[derive(PartialEq, Properties)]
struct Props {
id: String,
children: Children,
}
#[function_component]
fn Container(props: &Props) -> Html {
html! {
<div id={props.id.clone()}>
{ props.children.clone() }
</div>
}
}
let props = yew::props!(Props {
id: "container-2",
children: Children::default(),
});
html! {
<Container ..props>
// props.children will be overwritten with this
<span>{ "I am a child, as you can see" }</span>
</Container>
};
```
## Nested Children with Props
Nested component properties can be accessed and mutated if the containing component types its children. In the following example, the `List` component can wrap `ListItem` components. For a real world example of this pattern, check out the `yew-router` source code. For a more advanced example, check out the `nested-list` example in the main yew repository.
```rust
use std::rc::Rc;
use yew::prelude::*;
#[derive(Clone, PartialEq, Properties)]
pub struct ListItemProps {
value: String,
}
#[function_component]
fn ListItem(props: &ListItemProps) -> Html {
let ListItemProps { value } = props.clone();
html! {
<span>
{value}
</span>
}
}
#[derive(PartialEq, Properties)]
pub struct Props {
pub children: ChildrenWithProps<ListItem>,
}
#[function_component]
fn List(props: &Props) -> Html {
let modified_children = props.children.iter().map(|mut item| {
let mut props = Rc::make_mut(&mut item.props);
props.value = format!("item-{}", props.value);
item
});
html! { for modified_children }
}
html! {
<List>
<ListItem value="a" />
<ListItem value="b" />
<ListItem value="c" />
</List>
};
```
## Relevant examples
- [Function Todo MVC](https://github.com/yewstack/yew/tree/master/examples/function_todomvc)
- [Function Router](https://github.com/yewstack/yew/tree/master/examples/function_router)

View File

@ -0,0 +1,73 @@
---
title: 'Conditional rendering'
description: 'Rendering nodes conditionally in html!'
---
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
## If blocks
To conditionally render some markup, we wrap it in an `if` block:
<Tabs>
<TabItem value="if" label="if">
```rust
use yew::prelude::*;
html! {
if true {
<p>{ "True case" }</p>
}
};
```
</TabItem>
<TabItem value="if - else" label="if - else">
```rust
use yew::prelude::*;
let some_condition = true;
html! {
if some_condition {
<p>{ "True case" }</p>
} else {
<p>{ "False case" }</p>
}
};
```
</TabItem>
<TabItem value="if let" label="if let">
```rust
use yew::prelude::*;
let some_text = Some("text");
html! {
if let Some(text) = some_text {
<p>{ text }</p>
}
};
```
</TabItem>
<TabItem value="if let else" label="if let else">
```rust
use yew::prelude::*;
let some_text = Some("text");
html! {
if let Some(text) = some_text {
<p>{ text }</p>
} else {
<p>{ "False case" }</p>
}
};
```
</TabItem>
</Tabs>

Some files were not shown because too many files have changed in this diff Show More