diff --git a/website/docs/concepts/agents.mdx b/website/docs/concepts/agents.mdx index dc35203fa43..2b59ee47bcb 100644 --- a/website/docs/concepts/agents.mdx +++ b/website/docs/concepts/agents.mdx @@ -55,7 +55,7 @@ A dispatcher allows uni-directional communication between a component and an age ## 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 +messages they send and receive. Agents use [bincode](https://github.com/bincode-org/bincode) to communicate with other threads, so the cost is substantially higher than just calling a function. ## Further reading diff --git a/website/docs/concepts/function-components/properties.mdx b/website/docs/concepts/function-components/properties.mdx index 2927004e87f..02c3f469b56 100644 --- a/website/docs/concepts/function-components/properties.mdx +++ b/website/docs/concepts/function-components/properties.mdx @@ -78,11 +78,6 @@ fn App() -> Html { ```rust use yew::{function_component, html, Html}; - - - - - #[function_component] fn HelloWorld() -> Html { html! { "Hello world" } diff --git a/website/docs/concepts/html/events.mdx b/website/docs/concepts/html/events.mdx index 23c0532d75d..5ec6c64ab6b 100644 --- a/website/docs/concepts/html/events.mdx +++ b/website/docs/concepts/html/events.mdx @@ -107,7 +107,7 @@ pub enum Msg { ### Using `JsCast` The [`wasm-bindgen`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/index.html) crate has -a useful trait; [`JsCast`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/trait.JsCast.html) +a useful trait: [`JsCast`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/trait.JsCast.html), which allows us to hop and skip our way to the type we want, as long as it implements `JsCast`. We can do this cautiously, which involves some runtime checks and failure types like `Option` and `Result`, or we can do it dangerously. diff --git a/website/docs/concepts/html/lists.mdx b/website/docs/concepts/html/lists.mdx index 6dca5da6c48..0795f9aff8b 100644 --- a/website/docs/concepts/html/lists.mdx +++ b/website/docs/concepts/html/lists.mdx @@ -83,7 +83,7 @@ html! { We have [Keyed list](https://github.com/yewstack/yew/tree/master/examples/keyed_list) example that lets you test the performance improvements, but here is a rough rundown: -1. Go to [Keyed list](https://github.com/yewstack/yew/tree/master/examples/keyed_list) hosted demo +1. Go to [Keyed list hosted demo](https://examples.yew.rs/keyed_list) 2. Add 500 elements. 3. Disable keys. 4. Reverse the list. diff --git a/website/docs/concepts/router.mdx b/website/docs/concepts/router.mdx index 75613394c12..17c404f4157 100644 --- a/website/docs/concepts/router.mdx +++ b/website/docs/concepts/router.mdx @@ -190,7 +190,7 @@ routes. Except you supply a `to` attribute instead of a `href`. An example usage Struct variants work as expected too: ```rust ,ignore - to={Route::Post { id: "new-yew-release".to_string() }}>{ "Yew v0.19 out now!" }> + to={Route::Post { id: "new-yew-release".to_string() }}>{ "Yew!" }> ``` #### Navigator API diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current.json b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current.json index 5f3e467d264..3c794267f95 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current.json +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current.json @@ -12,11 +12,11 @@ "description": "The label for category Concepts in sidebar docs" }, "sidebar.docs.category.Concepts.link.generated-index.title": { - "message": "Yew concepts", + "message": "Yew 核心概念", "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!", + "message": "了解 Yew 的重要概念!", "description": "The generated-index page description for category Concepts in sidebar docs" }, "sidebar.docs.category.HTML": { @@ -24,7 +24,7 @@ "description": "The label for category HTML in sidebar docs" }, "sidebar.docs.category.Components": { - "message": "Function Components", + "message": "函数式组件", "description": "The label for category Components in sidebar docs" }, "sidebar.docs.category.Advanced topics": { @@ -36,7 +36,7 @@ "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!", + "message": "了解 Yew 的更多内部细节!", "description": "The generated-index page description for category Advanced topics in sidebar docs" }, "sidebar.docs.category.More": { @@ -44,11 +44,11 @@ "description": "The label for category More in sidebar docs" }, "sidebar.docs.category.More.link.generated-index.title": { - "message": "Miscellaneous", + "message": "杂项", "description": "The generated-index page title for category More in sidebar docs" }, "sidebar.docs.category.Migration guides": { - "message": "Migration guides", + "message": "迁移指南", "description": "The label for category Migration guides in sidebar docs" }, "sidebar.docs.category.yew": { @@ -64,23 +64,23 @@ "description": "The label for category yew-router in sidebar docs" }, "sidebar.docs.category.Using Basic Web Technologies In Yew": { - "message": "Intro With Basic Web Technologies", + "message": "Yew 中的基本 Web 技术", "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 Take on Basic Web Technologies", + "message": "Yew 对基本 Web 技术的看法", "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 mostly operates on the idea of keeping everything that a reusable piece of UI may need, in one place - rust files. But also seeks to stay close to the original look of the technology. Explore further to fully grasp what we mean by these statements:", + "message": "Yew 主要基于将可重用的 UI 部件所需的所有内容放在一个地方 - rust 文件的想法。但也力求保持与技术的原始外观接近。进一步探索,以充分理解我们对这些陈述的含义:", "description": "The generated-index page description for category Using Basic Web Technologies In Yew in sidebar docs" }, "sidebar.docs.category.Hooks": { - "message": "Hooks", + "message": "钩子", "description": "The label for category Hooks in sidebar docs" }, "sidebar.docs.category.Struct Components": { - "message": "Struct Components", + "message": "结构化组件", "description": "The label for category Struct Components in sidebar docs" } -} +} \ No newline at end of file diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/children.mdx b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/children.mdx new file mode 100644 index 00000000000..bed0a5f8ce2 --- /dev/null +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/children.mdx @@ -0,0 +1,308 @@ +--- +title: '子组件' +--- + +:::caution + +检查和操作 `Children` 往往会导致应用程序中令人惊讶且难以解释的行为。这可能导致边缘情况,并且通常不会产生预期的结果。如果您尝试操作 `Children`,则应考虑其他方法。 + +Yew 支持将 `Html` 用作子组件属性的类型。如果您不需要 `Children` 或 `ChildrenRenderer`,则应使用 `Html` 作为子组件。它没有 `Children` 的缺点,并且性能开销较低。 + +::: + +## 通用用法 + +_大多数情况下,_当允许组件具有子组件时,您不关心组件具有的子组件的类型。在这种情况下,下面的示例就足够了。 + +```rust +use yew::{html, Component, Context, Html, Properties}; + +#[derive(Properties, PartialEq)] +pub struct ListProps { + #[prop_or_default] + pub children: Html, +} + +pub struct List; + +impl Component for List { + type Message = (); + type Properties = ListProps; + + fn create(_ctx: &Context) -> Self { + Self + } + + fn view(&self, ctx: &Context) -> Html { + html! { +
+ {ctx.props().children.clone()} +
+ } + } +} +``` + +## 高级用法 + +### 类型化子组件 + +在您希望将一种类型的组件作为子组件传递给您的组件的情况下,您可以使用 `yew::html::ChildrenWithProps`。 + +```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 + } + + fn view(&self, _ctx: &Context) -> Html { + html! { + { "item" } + } + } +} + +#[derive(Properties, PartialEq)] +pub struct ListProps { + #[prop_or_default] + pub children: ChildrenWithProps, +} + +pub struct List; + +impl Component for List { + type Message = (); + type Properties = ListProps; + + fn create(_ctx: &Context) -> Self { + Self + } + + fn view(&self, ctx: &Context) -> Html { + html! { +
+ { for ctx.props().children.iter() } +
+ } + } +} +``` + +## 带有属性的嵌套子组件 + +如果包含组件对其子组件进行了类型化,则可以访问和更改嵌套组件的属性。 + +```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! { + + {value} + + } +} + +#[derive(PartialEq, Properties)] +pub struct Props { + pub children: ChildrenWithProps, +} + +#[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! { + + + + + +}; +``` + +### 枚举类型的子组件 + +当然,有时您可能需要将子组件限制为几种不同的组件。在这些情况下,您必须更深入地了解 Yew。 + +这里使用 [`derive_more`](https://github.com/JelteF/derive_more) 来提供更好的人机工程学。如果您不想使用它,您可以为每个变体手动实现 `From`。 + +```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 + } + + fn view(&self, _ctx: &Context) -> Html { + html! { + { "Primary" } + } + } +} + +pub struct Secondary; + +impl Component for Secondary { + type Message = (); + type Properties = (); + + fn create(_ctx: &Context) -> Self { + Self + } + + fn view(&self, _ctx: &Context) -> Html { + html! { + { "Secondary" } + } + } +} + +#[derive(Clone, derive_more::From, PartialEq)] +pub enum Item { + Primary(VChild), + Secondary(VChild), +} + +// Now, we implement `Into` so that yew knows how to render `Item`. +#[allow(clippy::from_over_into)] +impl Into 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, +} + +pub struct List; + +impl Component for List { + type Message = (); + type Properties = ListProps; + + fn create(_ctx: &Context) -> Self { + Self + } + + fn view(&self, ctx: &Context) -> Html { + html! { +
+ { for ctx.props().children.iter() } +
+ } + } +} +``` + +### 可选类型的子组件 + +您还可以具有特定类型的单个可选子组件: + +```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 + } + + fn view(&self, _ctx: &Context) -> Html { + html! { + { "sidebar" } + } + } +} + +#[derive(Properties, PartialEq)] +pub struct PageProps { + #[prop_or_default] + pub sidebar: Option>, +} + +struct Page; + +impl Component for Page { + type Message = (); + type Properties = PageProps; + + fn create(_ctx: &Context) -> Self { + Self + } + + fn view(&self, ctx: &Context) -> Html { + html! { +
+ { ctx.props().sidebar.clone().map(Html::from).unwrap_or_default() } + // ... page content +
+ } + } +} + +// 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! { + + }} /> + } + } else { + // Page without sidebar + html! { + + } + } +} +``` + +## 进一步阅读 + +- 有关此模式的真实示例,请查阅 yew-router 的源代码。有关更高级的示例,请查看 yew 存储库中的[相关示例清单](https://github.com/yewstack/yew/tree/master/examples/nested_list) \ No newline at end of file diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/how-it-works.mdx b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/how-it-works.mdx index c7645fe767c..ca2fb022071 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/how-it-works.mdx +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/how-it-works.mdx @@ -1,7 +1,39 @@ --- -description: 有关框架的底层细节 +title: '工作原理' +description: '关于框架的底层细节' --- # 底层库的内部细节 -组件生命周期状态机,虚拟 dom diff 算法。 +## `html!` 宏的内部 + +`html!` macro 会将使用类似 HTML 的自定义语法编写的代码转换为有效的 Rust 代码。使用这个宏对于开发 Yew 应用程序并不是必需的,但是是推荐的。这个宏生成的代码使用了 Yew 的公共库 API,如果你愿意的话,可以直接使用。请注意,有些方法是有意未记录的,以避免意外的误用。随着 `yew-macro` 的每次更新,生成的代码将会更加高效,并且可以处理任何破坏性的更改,而不需要对 `html!` 语法进行很多(如果有的话)修改。 + +由于 `html!` 宏允许您以声明式的风格编写代码,因此您的 UI 布局代码将与为页面生成的 HTML 非常相似。随着您的应用程序变得更加交互式,您的代码库变得更大,这种方式变得越来越有用。与手动编写所有操作 DOM 的代码相比,宏会为您处理好这一切。 + +使用 `html!` 宏可能会让人感到非常神奇,但它并没有什么可隐藏的。如果您对它的工作原理感到好奇,可以尝试展开您程序中的 `html!` 宏调用。有一个有用的命令叫做 `cargo expand`,它允许您查看 Rust 宏的展开。`cargo expand` 并不是默认随 `cargo` 一起提供的,所以如果您还没有安装它,您需要使用 `cargo install cargo-expand` 来安装它。[Rust-Analyzer](https://rust-analyzer.github.io/) 也提供了一种[从 IDE 中获取宏输出的机制](https://rust-analyzer.github.io/manual.html#expand-macro-recursively)。 + +`html!` 宏的输出通常非常简洁!这是一个特性:机器生成的代码有时可能会与应用程序中的其他代码冲突。为了防止问题,`proc_macro` 遵循了“卫生”规则。一些例子包括: + +1. 为了确保正确引用 Yew 包,宏生成的代码中使用 `::yew::`,而不是直接使用 `yew::`。这也是为什么调用 `::alloc::vec::Vec::new()` 而不是直接调用 `Vec::new()`。 +2. 由于可能存在 trait 方法名称冲突,使用 `` 来确保我们使用的是正确的 trait 成员。 + +## 什么是虚拟 DOM? + +DOM("文档对象模型")是由浏览器管理的 HTML 内容的表示。"虚拟" DOM 只是 DOM 的一个内存中的副本。管理虚拟 DOM 会导致更高的内存开销,但可以通过避免或延迟使用浏览器 API 来实现批处理和更快的读取。 + +在内存中拥有 DOM 的副本对于促进使用声明式 UI 的库是有帮助的。与需要特定代码来描述如何根据用户事件修改 DOM 不同,库可以使用一种通用的方法来进行 DOM "diffing"。当 Yew 组件更新并希望更改其呈现方式时,Yew 库将构建虚拟 DOM 的第二个副本,并直接将其与镜像当前屏幕上的内容的虚拟 DOM 进行比较。两者之间的 "diff"(差异)可以分解为增量更新,并与浏览器 API 一起应用。一旦更新应用,旧的虚拟 DOM 副本将被丢弃,新的副本将被保存以供将来的差异检查。 + +这种 "diff" 算法可以随着时间的推移进行优化,以提高复杂应用程序的性能。由于 Yew 应用程序是通过 WebAssembly 运行的,我们相信 Yew 在未来采用更复杂的算法方面具有竞争优势。 + +Yew 的虚拟 DOM 与浏览器 DOM 不完全一一对应。它还包括用于组织 DOM 元素的 "列表" 和 "组件"。列表可以简单地是元素的有序列表,但也可以更强大。通过为每个列表元素添加 "key" 注解,应用程序开发人员可以帮助 Yew 进行额外的优化,以确保在列表更改时,计算差异更新所需的工作量最小。同样,组件提供了自定义逻辑,指示是否需要重新渲染,以帮助提高性能。 + +## Yew 调度器和组件范围的事件循环 + +_贡献文档 - 深入解释 `yew::scheduler` 和 `yew::html::scope` 的工作原理_ + +## 进一步阅读 + +- [Rust 手册中关于宏的更多信息](https://doc.rust-lang.org/stable/book/ch19-06-macros.html) +- [`cargo-expand` 的更多信息](https://github.com/dtolnay/cargo-expand) +- [`yew::virtual_dom` 的 API 文档](https://docs.rs/yew/*/yew/virtual_dom/index.html) \ No newline at end of file diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/immutable.mdx b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/immutable.mdx new file mode 100644 index 00000000000..85f6e70ce96 --- /dev/null +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/immutable.mdx @@ -0,0 +1,19 @@ +--- +title: '不可变类型' +description: 'Yew 的不可变数据结构' +--- + +## 什么是不可变类型? + +这些类型是您可以实例化但永远不会更改值的类型。为了更新值,您必须实例化一个新值。 + +## 为什么使用不可变类型? + +与 React 一样,属性是从祖先传播到子代的。这意味着属性在每个组件更新时必须存在。这就是为什么属性应该——理想情况下——很容易克隆。为了实现这一点,我们通常将事物包装在 `Rc` 中。 + +不可变类型非常适合保存属性的值,因为它们可以在从组件传递到组件时以很低的成本克隆。 + +## 进一步阅读 + +- [不可变示例](https://github.com/yewstack/yew/tree/master/examples/immutable) +- [Crate `implicit-clone`](https://docs.rs/implicit-clone/) diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/optimizations.mdx b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/optimizations.mdx index 90c6bb16b9b..bc56a611b0f 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/optimizations.mdx +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/optimizations.mdx @@ -1,89 +1,116 @@ --- -description: 加速你的应用程序 +title: '优化 & 最佳实践' +sidebar_label: Optimizations +description: '让您的应用程序获得最佳性能' --- -# 性能优化与最佳实践 +## 使用智能指针 -## neq_assign +**注意:如果您对本节中使用的某些术语感到困惑,Rust 手册中有一个有用的[关于智能指针的章节](https://doc.rust-lang.org/book/ch15-00-smart-pointers.html)。** -当组件从它的父组件接收 props 时,`change` 方法将被调用。除了允许你更新组件的状态,还允许你返回一个布尔类型的值 `ShouldRender` 来指示组件是否应该响应 props 的更改而重新渲染自身。 +为了避免在重新渲染时克隆大量数据以创建 props,我们可以使用智能指针,只克隆对数据的引用而不是数据本身。如果您在 props 和子组件中传递与相关数据的引用而不是实际数据,您可以避免在需要修改数据的子组件中克隆任何数据,您可以使用 `Rc::make_mut` 来克隆并获得要更改的数据的可变引用。 -重新渲染的开销很大,你应该尽量避免。一个通用的法则是,你只应该在 props 实际更改时重新渲染。以下代码块展示了此法则,如果 props 和先前的 props 不同,则返回 `true`: +这在 `Component::changed` 中带来了更多好处,可以确定 prop 更改是否需要组件重新渲染。这是因为可以比较指针地址(即数据存储在机器内存中的位置)而不是数据的值;如果两个指针指向相同的数据,则它们指向的数据的值必须相同。请注意,反之可能不成立!即使两个指针地址不同,底层数据仍可能相同 - 在这种情况下,您应该比较底层数据。 -```rust -fn change(&mut self, props: Self::Properties) -> ShouldRender { - if self.props != &props { - *self.props = props; - true - } else { - false - } -} -``` +要进行此比较,您需要使用 `Rc::ptr_eq` 而不仅仅使用 `PartialEq`(在使用相等运算符 `==` 比较数据时自动使用)。Rust 文档有关于 `Rc::ptr_eq` 的[更多细节](https://doc.rust-lang.org/stable/std/rc/struct.Rc.html#method.ptr_eq)。 -但是我们可以更进一步!对于任何实现了 `PartialEq` 的项,可以使用一个 trait 和一个 blanket implementation 将这六行样板代码减少到一行。 - -```rust title="neq_assign.rs" -pub trait NeqAssign { - fn neq_assign(&mut self, new: Self) -> ShouldRender; -} -impl 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) -} -``` +这种优化对于不实现 `Copy` 的数据类型最有用。如果您可以廉价地复制数据,则没有必要将其放在智能指针后面。对于可能是数据密集型的结构,如 `Vec`、`HashMap` 和 `String`,使用智能指针可能会带来性能改进。 -该 trait 称为 `NeqAssign` 是因为如果目标值和新值不相等,它将赋为新值。 +如果值从不被子组件更新,则此优化效果最佳,如果父组件很少更新,则效果更佳。这使得 `Rc<_>` 是在纯组件中包装属性值的一个不错的选择。 -这比简单的实现还要短: +但是,必须注意,除非您需要在子组件中自己克隆数据,否则这种优化不仅是无用的,而且还增加了不必要的引用计数成本。Yew 中的 props 已经是引用计数的,内部不会发生数据克隆。 -```rust -// 不要这样做,除非你无法避免。 -fn change(&mut self, props: Self::Properties) -> ShouldRender { - self.props = props; - true -} -``` +## 渲染函数 + +出于代码可读性的原因,将 `html!` 的部分重复代码迁移到专门分割出来的函数中通常是有意义的。这不仅使您的代码更易读,减少了代码缩进,而且还鼓励良好的设计模式——特别是围绕构建可组合应用程序,这些函数可以在多个地方调用,从而减少代码量。 + +## 纯组件 -你不仅限在 `change` 函数中使用它。通常,在 `update` 函数中执行此操作也是有意义的,尽管性能提升在那里不太明显。 +纯组件是不会改变其状态的组件,只显示内容并将消息传播到普通的可变组件。它们与视图函数的不同之处在于,它们可以在 `html!` 宏中使用组件语法(``)而不是表达式语法(`{some_view_function()}`),并且根据其实现,它们可以被记忆化(这意味着一旦调用函数,其值就会被“保存”,因此如果多次使用相同的参数调用它,则不必重新计算其值,只需从第一个函数调用返回保存的值)- 防止相同的 props 重新渲染。Yew 在内部比较 props,因此仅在 props 更改时重新渲染 UI。 -## RC +## 使用工作区减少编译时间 -为了避免在重新渲染时为了创建 props 而克隆大块数据,我们可以使用智能指针来只克隆指针。如果在 props 和子组件中使用 `Rc<_>` 而不是普通未装箱的值,则可以延迟克隆直到需要修改子组件中的数据为止,在该组件中可以使用 `Rc::make_mut` 来对要更改数据进行克隆和获取可变引用。通过在要修改前不进行克隆,子组件可以在几乎没有性能成本的情况下拒绝与它们在 `Component::change` 中拥有状态的 props 相同的 props,这与数据本身需要先复制到父级 props 结构体中,然后在子级中进行比较和拒绝的情况相反。 +Yew 的最大缺点是编译所需的时间很长。编译项目所需的时间似乎与传递给 `html!` 宏的代码数量有关。对于较小的项目,这似乎不是什么问题,但对于较大的应用程序,将代码拆分到多个 crate 中以最小化编译器为应用程序所做的工作量是有意义的。 -对于不是 `Copy` 类型的数据,这种优化是最有用的。如果你能轻松地拷贝数据,那么将其放入智能指针中可能是不值得的。对于可以包含大量数据的结构,例如 `Vec`,`HashMap` 和 `String`,这种优化应该是值得的。 +一种可能的方法是使您的主 crate 处理路由/页面选择,然后为每个页面创建一个不同的 crate,其中每个页面可以是不同的组件或只是生成 `Html` 的大函数。存储在包含应用程序不同部分的 crate 之间的代码可以存储在项目依赖的单独 crate 中。在最理想的情况下,您从在每次编译时重新构建所有代码到仅重新构建主 crate 和一个页面 crate。在最坏的情况下,如果您在“common” crate 中编辑了某些内容,您将回到起点:编译依赖于该常用共享 crate 的所有代码,这可能是其他所有内容。 -如果子组件从不更新组件的值,则这种优化效果最好,如果父组件很少更新组件的值,则效果更好。这使得 `Rc<_>s` 是包装纯组件属性值的不错选择。 +如果您的主 crate 太重,或者您想快速迭代一个深度嵌套的页面(例如。在另一个页面上渲染的页面),您可以使用示例 crate 创建主页面的简化实现,并额外渲染您正在处理的组件。 -## 视图函数 +## 减小二进制文件大小 -出于代码可读性的原因,将 `html!` 各个部分的代码迁移到他们自己的函数中通常是有意义的,这样就可以避免在深层嵌套的 HTML 中出现代码块向右偏移。 +- optimize Rust code +- `cargo.toml` \( defining release profile \) +- optimize wasm code using `wasm-opt` -## 纯组件 / 函数式组件 +- 优化 Rust 代码 +- `cargo.toml`(定义发布配置文件) +- 使用 `wasm-opt` 优化 wasm 代码 + +**注意:有关减小二进制文件大小的更多信息,请参阅[Rust Wasm 手册](https://rustwasm.github.io/book/reference/code-size.html#optimizing-builds-for-code-size)。** + +### Cargo.toml + +可以使用 `Cargo.toml` 中 `[profile.release]` 部分中的可用设置来配置发布构建为更小。 + +```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 +``` -纯组件是不会修改它们状态的组件,它们仅展示内容和向普通可变组件传递消息。它们与视图函数不同之处在于他们可以使用组件语法(``)而不是表达式语法(`{some_view_function()}`)来在 `html!` 宏中使用,并且根据它们的实现,它们可以被记忆化 - 使用前面提到的 `neq_assign` 逻辑来防止因为相同的 props 而重新渲染。 +### 开发版 Cargo 配置 -Yew 没有原生支持纯组件或者函数式组件,但是可以通过外部库获取它们。 +您还可以从 Rust 和 cargo 的实验性开发版功能中获得额外的好处。要使用 `trunk` 的开发版工具链,请设置 `RUSTUP_TOOLCHAIN="nightly"` 环境变量。然后,您可以在 `.cargo/config.toml` 中配置不稳定的 rustc 功能。请参考[不稳定功能]的文档,特别是关于[`build-std`]和[`build-std-features`]的部分,以了解配置。 -函数式组件尚不存在,但是从理论上来讲,可以通过使用 proc 宏和标注函数生成纯组件。 +```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 +开发版 Rust 编译器可能包含错误,例如[这个例子](https://github.com/yewstack/yew/issues/2696),需要偶尔关注和调整。请谨慎使用这些实验性选项。 +::: + +### wasm-opt + +此外,可以优化 `wasm` 代码的大小。 + +Rust Wasm 手册中有关于减小 Wasm 二进制文件大小的部分:[缩小 .wasm 大小](https://rustwasm.github.io/book/game-of-life/code-size.html) + +- 使用 `wasm-pack`,默认情况下会优化发布构建中的 `wasm` 代码 +- 直接在 `wasm` 文件上使用 `wasm-opt` + +```text +wasm-opt wasm_bg.wasm -Os -o wasm_bg_opt.wasm +``` -## Keyed DOM nodes when they arrive +#### 在 yew/examples/ 中 'minimal' 示例的构建大小 -## 使用 Cargo Workspaces 进行编译速度优化 +注意:`wasm-pack` 结合了 Rust 和 Wasm 代码的优化。在此示例中,`wasm-bindgen` 未经任何 Rust 大小优化。 -可以说,使用 Yew 的最大缺点是编译时间长。编译时间似乎与 `html!` 宏块中的代码量相关。对于较小的项目,这通常不是什么大问题,但是对于跨多个页面的 web 应用程序,将代码拆分为多个 crates 以最大程度地减少编译器要做的工作通常是有意义的。 +| used tool | size | +| :-------------------------- | :---- | +| wasm-bindgen | 158KB | +| wasm-bindgen + wasm-opt -Os | 116KB | +| wasm-pack | 99 KB | -你应该尝试让主 crate 处理路由和页面选择,将所有公用的代码移动到另一个 crate,然后为每一个页面创建一个不同的 crate,其中每个页面可能是一个不同的组件,或者只是一个产生 `Html` 的大函数。在最好的情况下,你将从重新构建所有代码到只重新构建主 crate 和一个页面的 crate。在最糟糕的情况下,当你在“公共” crate 中编辑内容时,你将回到起点:编译所有依赖此公用 crate 的代码,这可能就是除此之外的所有代码。 +## 进一步阅读: -如果你的主 crate 过于庞大,或者你想在深层嵌套的页面(例如,在另一个页面顶部渲染的页面)中快速迭代,则可以使用一个示例 crate 创建一个更简单的主页面实现并在之上渲染你正在开发的组件。 +- [Rust 手册中关于智能指针的章节](https://doc.rust-lang.org/book/ch15-00-smart-pointers.html) +- [Rust Wasm 手册中关于减小二进制文件大小的信息](https://rustwasm.github.io/book/reference/code-size.html#optimizing-builds-for-code-size) +- [Rust 配置文件的文档](https://doc.rust-lang.org/cargo/reference/profiles.html) +- [binaryen 项目](https://github.com/WebAssembly/binaryen) \ No newline at end of file diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/portals.mdx b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/portals.mdx new file mode 100644 index 00000000000..1de4753af09 --- /dev/null +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/portals.mdx @@ -0,0 +1,48 @@ +--- +title: '传送门 (Portals)' +description: '将内容渲染到 DOM 树外的节点' +--- + +## 什么是 Portal? + +传送门 (Portal) 提供了一种将子元素渲染到父组件的 DOM 层次结构之外的 DOM 节点的方法。`yew::create_portal(child, host)` 返回一个 `Html` 值,它将 `child` 渲染为 `host` 元素的子元素,而不是在其父组件的层次结构下。 + +## 用法 + +传送门的典型用途包括模态对话框和悬停卡片,以及更多技术应用,例如控制元素的 [`shadowRoot`](https://developer.mozilla.org/en-US/docs/Web/API/Element/shadowRoot) 的内容,将样式表附加到周围文档的 `` 中,以及在 `` 的中央 `` 元素中收集引用的元素。 + +请注意,`yew::create_portal` 是一个低级构建块。库应该使用它来实现更高级的 API,然后应用程序可以使用这些 API。例如,这里是一个简单的模态对话框,它将其 `children` 渲染到 `yew` 之外的一个元素中,该元素由 `id="modal_host"` 标识。 + +```rust +use yew::prelude::*; + +#[derive(Properties, PartialEq)] +pub struct ModalProps { + #[prop_or_default] + pub children: Html, +} + +#[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( + props.children.clone(), + modal_host.into(), + ) +} +``` + +## 事件处理 + +传送门内部元素上发出的事件遵循虚拟 DOM 冒泡。也就是说,如果传送门被渲染为元素的子元素,那么该元素上的事件监听器将捕获从传送门内部分发出的事件,即使传送门将其内容渲染在实际 DOM 中的不相关位置。 + +这使开发人员无需关心他们使用的组件是使用传送门实现的还是没有使用传送门实现的。无论如何,其子元素上触发的事件都会冒泡。 + +已知问题是,从传送门到 **关闭** 的 shadow root 的事件将被分发两次,一次针对 shadow root 内部的元素,一次针对宿主元素本身。请记住,**打开** 的 shadow root 可以正常工作。如果这影响到您,请随时提交一个错误报告。 + +## 进一步阅读 + +- [传送门示例](https://github.com/yewstack/yew/tree/master/examples/portals) diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/server-side-rendering.mdx b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/server-side-rendering.mdx new file mode 100644 index 00000000000..9aa7200ab05 --- /dev/null +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/server-side-rendering.mdx @@ -0,0 +1,187 @@ +--- +title: '服务端渲染' +description: '在服务端渲染 Yew 组件。' +--- + +# 服务端渲染 (Server-Side Rendering) + +默认情况下,Yew 组件在客户端渲染。当用户访问一个网站时,服务器会发送一个骨架 HTML 文件,不包含任何实际内容,以及一个 WebAssembly 包给浏览器。所有内容都由 WebAssembly 包在客户端渲染。这被称为客户端渲染。 + +这种方法对于大多数网站来说都是有效的,但有一些注意事项: + +1. 用户在整个 WebAssembly 包下载并完成初始渲染之前将看不到任何内容。这可能会导致在缓慢的网络上用户体验不佳。 +2. 一些搜索引擎不支持动态渲染的网页内容,而那些支持的搜索引擎通常会将动态网站排名较低。 + +为了解决这些问题,我们可以在服务端渲染我们的网站。 + +## 工作原理 + +Yew 提供了一个 `ServerRenderer` 来在服务端渲染页面。 + +要在服务端渲染 Yew 组件,您可以使用 `ServerRenderer::::new()` 创建一个渲染器,并调用 `renderer.render().await` 将 `` 渲染为一个 `String`。 + +```rust +use yew::prelude::*; +use yew::ServerRenderer; + +#[function_component] +fn App() -> Html { + html! {
{"Hello, World!"}
} +} + +// 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::::new(); + + let rendered = renderer.render().await; + + // Prints:
Hello, World!
+ println!("{}", rendered); +} +``` + +## 组件生命周期 + +与客户端渲染不同,组件的生命周期在服务端渲染时会有所不同。 + +在组件成功第一次渲染为 `Html` 之前,除了 `use_effect` (和 `use_effect_with`) 之外的所有钩子都会正常工作。 + +:::caution 浏览器接口不可用! + +浏览器相关的接口,如 `web_sys`,在组件在服务端渲染时是不可用的。如果您尝试使用它们,您的应用程序将会崩溃。您应该将需要这部分逻辑隔离在 `use_effect` 或 `use_effect_with` 中,因为在服务端渲染时它们无法也不应当执行。 + +::: + +:::danger 结构化组件 + +尽管可以在服务端渲染时使用结构化组件,但是在客户端安全逻辑(如函数组件的 `use_effect` 钩子)和生命周期事件之间没有明确的边界,并且生命周期事件的调用顺序与客户端不同。 + +此外,结构化组件将继续接受消息,直到所有子组件都被渲染并调用了 `destroy` 方法。开发人员需要确保不会将可能传递给组件的消息链接到调用浏览器接口的逻辑。 + +在设计支持服务端渲染的应用程序时,请尽量使用函数组件,除非您有充分的理由不这样做。 + +::: + +## 服务端渲染期间的数据获取 + +数据获取是服务端渲染和注水(hydration)期间的难点之一。 + +传统做法中,当一个组件渲染时,它会立即可用(输出一个虚拟 DOM 以进行渲染)。当组件不需要获取任何数据时,这种方式是有效的。但是如果组件在渲染时想要获取一些数据会发生什么呢? + +过去,Yew 没有机制来检测组件是否仍在获取数据。数据获取客户端负责实现一个解决方案,以检测在初始渲染期间请求了什么,并在请求完成后触发第二次渲染。服务器会重复这个过程,直到在返回响应之前没有在渲染期间添加更多的挂起请求。 + +这不仅浪费了 CPU 资源,因为重复渲染组件,而且数据客户端还需要提供一种方法,在注水过程中使在服务端获取的数据可用,以确保初始渲染返回的虚拟 DOM 与服务端渲染的 DOM 树一致,这可能很难实现。 + +Yew 采用了一种不同的方法,通过 `` 来解决这个问题。 + +`` 是一个特殊的组件,当在客户端使用时,它提供了一种在组件获取数据(挂起)时显示一个回退 UI 的方法,并在数据获取完成后恢复到正常 UI。 + +当应用程序在服务端渲染时,Yew 会等待组件不再挂起,然后将其序列化到字符串缓冲区中。 + +在注水过程中,`` 组件中的元素保持未注水状态,直到所有子组件不再挂起。 + +通过这种方法,开发人员可以轻松构建一个准备好进行服务端渲染的、与客户端无关的应用程序,并进行数据获取。 + +## SSR Hydration + +## 服务端渲染注水(SSR Hydration) + +注水是将 Yew 应用程序连接到服务端生成的 HTML 文件的过程。默认情况下,`ServerRender` 打印可注水的 HTML 字符串,其中包含额外的信息以便于注水。当调用 `Renderer::hydrate` 方法时,Yew 不会从头开始渲染,而是将应用程序生成的虚拟 DOM 与服务器渲染器生成的 HTML 字符串进行协调。 + +:::caution + +为了成功对由 `ServerRenderer` 创建的 HTML 标记注水,客户端必须生成一个虚拟 DOM 布局,它与用于 SSR 的布局完全匹配,包括不包含任何元素的组件。如果您有任何只在一个实现中有用的组件,您可能希望使用 `PhantomComponent` 来填充额外组件的位置。 +::: + +:::warning + +只有在浏览器初始渲染 SSR 输出(静态 HTML)后,真实 DOM 与预期 DOM 匹配时,注水才能成功。如果您的 HTML 不符合规范,注水可能会失败。浏览器可能会更改不正确的 HTML 的 DOM 结构,导致实际 DOM 与预期 DOM 不同。例如,[如果您有一个没有 `` 的 ``,浏览器可能会向 DOM 添加一个 ``](https://github.com/yewstack/yew/issues/2684) +::: + +## 注水期间的组件生命周期 + +在注水期间,组件在创建后安排了 2 次连续的渲染。任何效果都是在第二次渲染完成后调用的。确保您的组件的渲染函数没有副作用是很重要的。它不应该改变任何状态或触发额外的渲染。如果您的组件当前改变状态或触发额外的渲染,请将它们移动到 `use_effect` 钩子中。 + +在注水过程中,可以使用结构化组件进行服务端渲染,视图函数将在渲染函数之前被调用多次。直到调用渲染函数之前,DOM 被认为是未连接的,您应该防止在调用 `rendered()` 方法之前访问渲染节点。 + +## 示例 + +```rust ,ignore +use yew::prelude::*; +use yew::Renderer; + +#[function_component] +fn App() -> Html { + html! {
{"Hello, World!"}
} +} + +fn main() { + let renderer = Renderer::::new(); + + // hydrates everything under body element, removes trailing + // elements (if any). + renderer.hydrate(); +} +``` + +示例: [simple_ssr](https://github.com/yewstack/yew/tree/master/examples/simple_ssr) +示例: [ssr_router](https://github.com/yewstack/yew/tree/master/examples/ssr_router) + +## 单线程模式 + +Yew 支持以单线程进行服务端渲染,通过 `yew::LocalServerRenderer`。这种模式适用于像 WASI 这样的单线程环境。 + +```rust +// 在构建时使用 `wasm32-wasip1` 或 `wasm32-wasip2` 目标(在 rustc 1.78 之后)。 +// 如果您使用的是较旧版本的 rustc(1.84 之前),您仍然可以使用 `wasm32-wasi` 目标进行构建。 +// 有关更多信息,请参见 https://blog.rust-lang.org/2024/04/09/updates-to-rusts-wasi-targets.html。 + +use yew::prelude::*; +use yew::LocalServerRenderer; + +#[function_component] +fn App() -> Html { + use yew_router::prelude::*; + + html! { + <> +

{"Yew WASI SSR demo"}

+ + } +} + +pub async fn render() -> String { + let renderer = LocalServerRenderer::::new(); + let html_raw = renderer.render().await; + + let mut body = String::new(); + body.push_str(""); + body.push_str("
"); + body.push_str(&html_raw); + body.push_str("
"); + body.push_str(""); + + body +} + +#[tokio::main(flavor = "current_thread")] +async fn main() { + println!("{}", render().await); +} +``` + +示例: [wasi_ssr_module](https://github.com/yewstack/yew/tree/master/examples/wasi_ssr_module) + +:::note +如果您使用 `wasm32-unknown-unknown` 目标构建 SSR 应用程序,您可以使用 `not_browser_env` 功能标志来禁用 Yew 内部对特定于浏览器的 API 的访问。这在像 Cloudflare Worker 这样的无服务器平台上非常有用。 +::: + +:::caution + +服务端渲染目前是实验性的。如果您发现了一个 bug,[请在 GitHub 反馈](https://github.com/yewstack/yew/issues/new?assignees=&labels=bug&template=bug_report.md&title=)。 + +::: diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/struct-components/callbacks.mdx b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/struct-components/callbacks.mdx index 15b9f0961bb..00b54858ddd 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/struct-components/callbacks.mdx +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/struct-components/callbacks.mdx @@ -1,27 +1,85 @@ --- -description: ComponentLink 和 Callbacks. +title: '回调函数 (Callbacks)' --- -# 回调(Callbacks) +## 回调函数 (Callbacks) -组件“link”是一种机制,通过该机制,组件可以注册回调并自行更新。 +回调函数是用于在 Yew 中与服务、代理和父组件进行通信的。在内部,它们的类型只是 `Fn` 包装在 `Rc` 中,以允许它们被克隆。 -## ComponentLink API +它们有一个 `emit` 函数,该函数以其 `` 类型作为参数,并将其转换为其目标期望的消息。如果父组件中的回调函数作为 props 提供给子组件,子组件可以在其 `update` 生命周期钩子中调用回调函数的 `emit` 函数,以将消息发送回其父组件。在 `html!` 宏中作为 props 提供的闭包或函数会自动转换为回调函数。 -### callback +一个简单的回调函数的使用可能如下所示: -注册一个回调,该回调将在执行时将消息发送到组件的更新机制。在内部,它将使用提供的闭包返回的消息调用 `send_self`。提供 `Fn(IN) -> Vec`,返回 `Callback`。 +```rust +use yew::{html, Component, Context, Html}; -### send_message +enum Msg { + Clicked, +} -当前循环结束后立即向组件发送消息,导致另一个更新循环启动。 +struct Comp; -### send_message_batch +impl Component for Comp { -注册一个回调,该回调在执行时立即发送一批消息。如果其中任何一个消息将导致组件重新渲染,那么组件会在该批次所有消息被处理后重新渲染。提供 `Fn(IN) -> COMP::Message`,返回 `Callback`。 + type Message = Msg; + type Properties = (); -## Callbacks + fn create(_ctx: &Context) -> Self { + Self + } -Callbacks 用于与 Yew 中的 services,agents 和父组件进行通信。它们仅仅是个 `Fn`,并由 `Rc` 包裹以允许被克隆。 + fn view(&self, ctx: &Context) -> Html { + // highlight-next-line + let onclick = ctx.link().callback(|_| Msg::Clicked); + html! { + // highlight-next-line + + } + } +} +``` -它们有一个 `emit` 函数,该函数将它的 `` 类型作为参数并将其转换为目标所期望的消息。如果一个回调从父组件中通过 props 提供给子组件,则子组件可以在其 `update` 生命周期钩子中对该回调调用 `emit`,以将消息发送回父组件。在 `html!` 宏内被提供作为 props 的闭包或函数会自动转换为 Callbacks。 +这个函数传递给 `callback` 必须始终带有一个参数。例如,`onclick` 处理程序需要一个接受 `MouseEvent` 类型参数的函数。然后处理程序可以决定应该发送什么类型的消息给组件。这个消息无条件地被安排在下一个更新循环中。 + +如果你需要一个回调函数,它可能不需要引起更新,请使用 `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 + } + + fn view(&self, ctx: &Context) -> Html { + // highlight-start + let onkeypress = ctx.link().batch_callback(|event: KeyboardEvent| { + if event.key() == "Enter" { + Some(Msg::Submit) + } else { + None + } + }); + + html! { + + } + // highlight-end + } +} +``` + +## 相关示例 + +- [Counter](https://github.com/yewstack/yew/tree/master/examples/counter) +- [Timer](https://github.com/yewstack/yew/tree/master/examples/timer) diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/struct-components/hoc.mdx b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/struct-components/hoc.mdx new file mode 100644 index 00000000000..4eeeb247522 --- /dev/null +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/struct-components/hoc.mdx @@ -0,0 +1,82 @@ +--- +title: '高阶组件' +--- + +在一些情况下,结构组件不直接支持某些功能(例如 Suspense),或者使用某些功能需要大量的样板代码(例如 Context)。 + +在这些情况下,建议创建高阶组件的函数组件。 + +## 高阶组件定义 + +高阶组件是不添加任何新 HTML 的组件,只是包装其他组件以提供额外功能。 + +### 示例 + +对 Context (上下文) 挂钩并将其传递给结构组件 + +```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! { + context={(*ctx).clone()}> + + > + } +} + +// highlight-start +#[function_component] +pub fn ThemedButtonHOC() -> Html { + let theme = use_context::().expect("no ctx found"); + + html! {} +} +// 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 + } + + fn view(&self, ctx: &Context) -> Html { + let theme = &ctx.props().theme; + html! { + + } + } +} + + + + +``` diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/struct-components/introduction.mdx b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/struct-components/introduction.mdx new file mode 100644 index 00000000000..ae616da93db --- /dev/null +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/struct-components/introduction.mdx @@ -0,0 +1,28 @@ +--- +title: '简介' +description: 'Yew 中的组件' +--- + +## 什么是组件? + +组件是 Yew 的构建块。它们管理内部状态并可以将元素渲染到 DOM 中。通过为类型实现 `Component` trait 来创建组件。 + +## 编写组件标记 + +Yew 使用虚拟 DOM 将元素渲染到 DOM 中。虚拟 DOM 树可以通过使用 `html!` 宏来构建。`html!` 使用的语法类似于 HTML,但并不相同。规则也更严格。它还提供了诸如条件渲染和使用迭代器渲染列表等超能力。 + +:::info +[了解更多关于 `html!` 宏,如何使用它以及它的语法](concepts/html/introduction.mdx) +::: + +## 将数据传递给组件 + +Yew 组件使用 _props_ 在父组件和子组件之间通信。父组件可以将任何数据作为 props 传递给其子组件。Props 类似于 HTML 属性,但可以将任何 Rust 类型作为 props 传递。 + +:::info +[了解更多关于 props 的内容](advanced-topics/struct-components/properties.mdx) +::: + +:::info +对于除了父/子通信之外的其他通信,请使用 [contexts](../../concepts/contexts.mdx) +::: diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/struct-components/lifecycle.mdx b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/struct-components/lifecycle.mdx index 563663ad1e6..fe358fec96d 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/struct-components/lifecycle.mdx +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/struct-components/lifecycle.mdx @@ -1,156 +1,249 @@ --- -description: 组件及其生命周期钩子 +title: '生命周期' +description: '组件及其生命周期钩子' --- -# 组件(Components) - -## 组件是什么? - -组件是 Yew 的基石。它们管理自己的状态,并可以渲染为 DOM。组件是通过实现描述组件生命周期的 `Component` trait 来创建的。 +`Component` trait 有许多方法需要实现;Yew 会在组件的生命周期的不同阶段调用这些方法。 ## 生命周期 -:::note -`为我们的文档做出贡献:`[添加组件的生命周期图示](https://github.com/yewstack/docs/issues/22) +:::important 改进文档 +`为文档做贡献:` [添加定制生命周期的组件示例](https://github.com/yewstack/yew/issues/1915) ::: ## 生命周期方法 ### Create -当一个组件被创建时,它会从其父组件以及一个 `ComponentLink` 接收属性(properties)。属性(properties)可用于初始化组件的状态,“link”可用于注册回调或向组件发送消息。 - -通常将 props 和 link 存储在组件的结构体中,如下所示: +当组件被创建时,它会从其父组件接收属性,并存储在传递给 `create` 方法的 `Context` 中。这些属性可以用来初始化组件的状态,而 "link" 可以用来注册回调或向组件发送消息。 ```rust -pub struct MyComponent { - props: Props, - link: ComponentLink, -} +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; - // ... - fn create(props: Self::Properties, link: ComponentLink) -> Self { - MyComponent { props, link } + // highlight-start + fn create(ctx: &Context) -> Self { + MyComponent } + // highlight-end - // ... + fn view(&self, _ctx: &Context) -> Html { + html! { + // impl + } + } } ``` ### View -组件在 `view()` 方法中声明它的布局。Yew 提供了 `html!` 宏来声明 HTML 和 SVG 节点和它们的监听器及其子组件。这个宏的行为很像 React 中的 JSX,但是使用的是 Rust 表达式而不是 JavaScript。 +`view` 方法允许您描述组件应该如何呈现到 DOM 中。使用 Rust 函数编写类似 HTML 的代码可能会变得非常混乱,因此 Yew 提供了一个名为 `html!` 的宏,用于声明 HTML 和 SVG 节点(以及将属性和事件监听器附加到它们)以及一种方便的方法来渲染子组件。这个宏在某种程度上类似于 React 的 JSX(除了编程语言的差异)。一个不同之处是 Yew 提供了一种类似 Svelte 的属性的简写语法,其中您可以只写 `{onclick}`,而不用写 `onclick={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 + } - fn view(&self) -> Html { - let onclick = self.link.callback(|_| Msg::Click); + // highlight-start + fn view(&self, ctx: &Context) -> Html { + let onclick = ctx.link().callback(|_| Msg::Click); html! { - + } } + // highlight-end } ``` -有关用法的详细信息,请查看 [`html!` 宏指南](concepts/html/introduction.mdx)] +就使用上的说明,请查看 [html! 指南](concepts/html/introduction.mdx)。 -### Mounted +### Rendered -`mounted()` 组件生命周期方法调用是在 `view()` 被处理并且 Yew 已经把组件挂载到 DOM 上之后,浏览器刷新页面之前。组件通常希望实现此方法以执行只能在组件渲染元素之后才能执行的操作。如果你想在做出一些更改后重新渲染组件,返回 `true` 就可以了。 +`rendered` 组件生命周期方法在 `view` 被调用并且 Yew 已经将结果渲染到 DOM 中后调用,但在浏览器刷新页面之前。当您想要执行只能在组件渲染元素后完成的操作时,此方法非常有用。还有一个名为 `first_render` 的参数,可以用来确定此函数是在第一次渲染时调用,还是在后续渲染时调用。 ```rust -use stdweb::web::html_element::InputElement; -use stdweb::web::IHtmlElement; -use yew::prelude::*; +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 { + node_ref: NodeRef::default(), + } + } - fn view(&self) -> Html { + fn view(&self, ctx: &Context) -> Html { html! { } } - fn mounted(&mut self) -> ShouldRender { - if let Some(input) = self.node_ref.cast::() { - input.focus(); + // highlight-start + fn rendered(&mut self, _ctx: &Context, first_render: bool) { + if first_render { + if let Some(input) = self.node_ref.cast::() { + input.focus(); + } } - false } + // highlight-end } ``` -:::note -请注意,此生命周期方法不要求必须实现,默认情况下不会执行任何操作。 +:::tip note +请注意,此生命周期方法不需要实现,并且默认情况下不会执行任何操作。 ::: ### Update -组件是动态的,可以注册以接收异步信息。`update()` 生命周期方法对于每个消息都会被调用。这使得组件可以根据消息的内容来更新自身,并决定是否需要重新渲染自己。消息可以由 HTML 元素监听器触发,或者由子组件,Agents,Services 或 Futures 发送。 +与组件的通信主要通过消息进行,这些消息由 `update` 生命周期方法处理。这允许组件根据消息更新自身,并确定是否需要重新渲染自身。消息可以由事件监听器、子组件、Agents、Services 或 Futures 发送。 + +下面是 `update` 的一个实现示例: -`update()` 可能看起来像下面这个例子: ```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 { + input_enabled: false, + } + } + + // highlight-start + fn update(&mut self, _ctx: &Context, 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 update(&mut self, msg: Self::Message) -> ShouldRender { - match msg { - Msg::SetInputEnabled(enabled) => { - if self.input_enabled != enabled { - self.input_enabled = enabled; - true // 重新渲染 - } else { - false - } - } - } + fn view(&self, _ctx: &Context) -> Html { + html! { + // impl + } } + } ``` -### Change +### Changed + +组件可能会被其父组件重新渲染。当这种情况发生时,它们可能会接收新的属性并需要重新渲染。这种设计通过仅更改属性的值来促进父子组件之间的通信。当属性更改时,有一个默认实现会重新渲染组件。 + +### Destroy + +组件从 DOM 中卸载后,Yew 会调用 `destroy` 生命周期方法;如果您需要在组件被销毁之前执行清理操作,这是必要的。此方法是可选的,默认情况下不执行任何操作。 + +### 无限循环 -组件可能被其父节点重新渲染。发生这种情况时,它们可以接收新的属性(properties)并选择重新渲染。这种设计通过更改属性(properties)来促进父子组件之间的通信。你不是必须实现 `change()`,但是如果想在组件被创建后通过 props 来更新组件,则可能要这么做。 +无限循环在 Yew 的生命周期方法中是可能的,但只有在尝试在每次渲染后更新相同的组件时,当该更新还要求重新渲染组件时才会发生。 -一个原始的实现可能看起来像: +下面是一个简单的示例: ```rust -impl Component for MyComponent { - // ... +use yew::{Context, Component, Html}; - fn change(&mut self, props: Self::Properties) -> ShouldRender { - self.props = props; - true // 当提供了新的 props 将始终重新渲染。 +struct Comp; + +impl Component for Comp { + type Message = (); + type Properties = (); + + fn create(_ctx: &Context) -> Self { + Self + } + + fn update(&mut self, _ctx: &Context, _msg: Self::Message) -> bool { + // We are going to always request to re-render on any msg + true + } + + fn view(&self, _ctx: &Context) -> Html { + // For this example it doesn't matter what is rendered + Html::default() + } + + fn rendered(&mut self, ctx: &Context, _first_render: bool) { + // Request that the component is updated with this new msg + ctx.link().send_message(()); } } ``` -### Destroy +让我们看看这里发生了什么: + +1. 使用 `create` 函数创建组件。 +2. 调用 `view` 方法,以便 Yew 知道要渲染到浏览器 DOM 中的内容。 +3. 调用 `rendered` 方法,使用 `Context` 链接安排更新消息。 +4. Yew 完成后渲染阶段。 +5. Yew 检查已安排的事件,并看到更新消息队列不为空,因此处理消息。 +6. 调用 `update` 方法,返回 `true` 表示发生了变化,组件需要重新渲染。 +7. 跳回到第 2 步。 -组件从 DOM 上被卸载后,Yew 调用 `destroy()` 生命周期方法来支持任何必要的清理操作。这个方法是可选的,默认情况下不执行任何操作。 +您仍然可以在 `rendered` 方法中安排更新,这通常是有用的,但是在这样做时,请考虑您的组件将如何终止此循环。 ## 关联类型 `Component` trait 有两个关联类型:`Message` 和 `Properties`。 -```rust +```rust ,ignore impl Component for MyComponent { type Message = Msg; type Properties = Props; @@ -159,12 +252,23 @@ impl Component for MyComponent { } ``` -`Message` 表示组件可以处理以触发某些副作用的各种消息。例如,你可能有一条 `Click` 消息,该消息触发 API 请求或者切换 UI 组件的外观。通常的做法是在组件模块中创建一个叫做 `Msg` 的枚举并将其用作组件中的消息类型。通常将“message”缩写为“msg”。 +`Message` 类型用于在事件发生后向组件发送消息;例如,您可能希望在用户单击按钮或向下滚动页面时执行某些操作。因为组件通常需要响应多个事件,所以 `Message` 类型通常是一个枚举,其中每个变体都是要处理的事件。 + +在组织代码库时,将 `Message` 类型的定义包含在定义组件的同一模块中是明智的。您可能会发现采用一致的命名约定来命名消息类型很有帮助。一个选项(尽管不是唯一的选项)是将类型命名为 `ComponentNameMsg`,例如,如果您的组件名为 `Homepage`,则可以将类型命名为 `HomepageMsg`。 ```rust enum Msg { Click, + FormInput(String) } ``` -`Properties` 表示从父级传递到组件的信息。此类型必须实现 `Properties` trait(通常通过派生),并且可以指定某些属性(properties)是必需的还是可选的。创建和更新组件时使用此类型。通常的做法是在组件模块中创建一个叫做 `Props` 的结构体并将其用作组件的 `Properties` 类型。通常将“properties”缩写为“props”。由于 props 是从父组件传递下来的,因此应用程序的根组件通常有一个类型为 `()` 的 `Properties`。如果你希望为根组件指定属性(properties),请使用 `App::mount_with_props` 方法。 +`Properties` 表示从其父组件传递给组件的信息。此类型必须实现 `Properties` trait(通常通过派生它)并可以指定某些属性是必需的还是可选的。在创建和更新组件时使用此类型。在组件的模块中创建一个名为 `Props` 的结构体,并将其用作组件的 `Properties` 类型是一种常见做法。通常将 "properties" 缩写为 "props"。由于 props 是从父组件传递下来的,因此应用程序的根组件通常具有 `Properties` 类型为 `()`。如果要为根组件指定属性,请使用 `App::mount_with_props` 方法。 + +:::info +[了解更多关于属性的信息](./properties) +::: + +## 生命周期上下文 + +所有组件生命周期方法都接受一个上下文对象。此对象提供了对组件作用域的引用,允许向组件发送消息并传递给组件的 props。 diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/struct-components/properties.mdx b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/struct-components/properties.mdx index f07a81fa927..019fb24eea1 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/struct-components/properties.mdx +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/struct-components/properties.mdx @@ -1,43 +1,55 @@ --- -description: 父组件到子组件的通信 +title: '属性 (Props)' +description: '父子组件通信' --- -# 属性(Properties) - -如“组件(Components)”页面所述,Properties 用于父级到子组件的通信。 +属性 (Properties) 使子组件和父组件之间能够进行通信。每个组件都有一个关联的属性类型,用于描述从父组件传递下来的内容。理论上,这可以是任何实现了 `Properties` 特性的类型,但实际上,它应该是一个结构体,其中每个字段代表一个属性。 ## 派生宏 -不要尝试自己去实现 `Properties`,而是通过使用 `#[derive(Properties)]` 来派生它。 +无需自己实现 `Properties` 特性,我们可以用 `#[derive(Properties)]` 来自动生成实现。派生 `Properties` 的类型也必须实现 `PartialEq`。 -### 必需属性 +### 字段属性 -默认情况下,实现了 `Properties` 的结构体中的字段是必需的。当缺少了该字段并且在 `html!` 宏中创建了组件时,将返回编译错误。对于具有可选属性的字段,使用 `#[prop_or_default]` 来使用该类型的默认值。要指定一个值,请使用 `#[prop_or_else(value)]`,其中 value 是该属性的默认值。例如,要将一个布尔值的默认值设置为 `true`,请使用属性 `#[prop_or_else(true)]`。可选属性通常使用 `Option`,其默认值为 `None`。 +在派生 `Properties` 时,默认情况下所有字段都是必需的。以下属性允许您为属性提供初始值,除非它们被设置为另一个值。 -### PartialEq +:::tip +属性不会在 Rustdoc 生成的文档中显示。您的属性的文档字符串应该说明一个属性是否是可选的,以及它是否有一个特殊的默认值。 +::: -如果可以的话,在你的 props 上派生 `PartialEq` 通常是很有意义的。这使用了一个**性能优化与最佳实践**部分解释了的技巧,可以更轻松地避免重新渲染。 +#### `#[prop_or_default]` -## Properties 的内存/速度开销 +使用字段类型的默认值使用 `Default` 特性来初始化属性值。 -记住组件的 `view` 函数签名: +#### `#[prop_or(value)]` -```rust -fn view(&self) -> Html -``` +使用 `value` 来初始化属性值。`value` 可以是返回字段类型的任何表达式。例如,要将布尔属性默认为 `true`,请使用属性 `#[prop_or(true)]`。 + +#### `#[prop_or_else(function)]` -你对组件的状态取了一个引用,并用来创建 `Html`。但是 properties 是有所有权的值(owned values)。这意味着为了创造它们并且将它们传递给子组件,我们需要获取 `view` 函数里提供的引用的所有权。这是在将引用传递给组件时隐式克隆引用完成的,以获得构成其 props 的有所有权的值。 +调用 `function` 来初始化属性值。`function` 应该具有签名 `FnMut() -> T`,其中 `T` 是字段类型。 -这意味着每个组件都有从其父级传递来的状态的独特副本,而且,每当你重新渲染一个组件时,该重新渲染组件的所有子组件的 props 都将被克隆。 +## `PartialEq` -这意味着如果你将 _大量_ 数据作为 props(大小为 10 KB 的字符串)向下传递,则可能需要考虑将子组件转换为在父级运行返回 `Html` 的函数,因为这样就不会被强制克隆你的数据。 +`Properties` 需要实现 `PartialEq`。这样,Yew 才能比较它们,以便在它们发生变化时调用 `changed` 方法。 -另外,如果你不需要修改作为 props 传递的大数据,而只需要显示它,则可以将其包装在 `Rc` 中,以便仅克隆一个引用计数的指针,而不是数据本身。 +## 使用 Properties 的性能开销 + +内部属性是基于引用计数的指针存储的。这意味着只有一个指针被传递到组件树中的属性,以避免克隆整个属性所带来的昂贵性能开销。 + +:::tip +使用 `AttrValue`,这是我们提供的自定义属性值类型,这样就可以不用 String 或其他类似的需要克隆的类型。 +::: ## 示例 ```rust -pub struct LinkColor { +use yew::Properties; +/// Importing the AttrValue from virtual_dom +use yew::virtual_dom::AttrValue; + +#[derive(Clone, PartialEq)] +pub enum LinkColor { Blue, Red, Green, @@ -45,28 +57,78 @@ pub struct LinkColor { Purple, } -impl Default for LinkColor { - fn default() -> Self { - // 除非另有说明,否则链接的颜色将为蓝色 - LinkColor::Blue - } +fn create_default_link_color() -> LinkColor { + LinkColor::Blue } #[derive(Properties, PartialEq)] pub struct LinkProps { - /// 链接必须有一个目标地址 - href: String, - /// 如果链接文本很大,这将使得复制字符串开销更小 - /// 除非有性能问题,否则通常不建议这么做 - text: Rc, - /// 链接的颜色 + /// The link must have a target. + href: AttrValue, + /// Also notice that we are 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, + /// When the view function does not specify active, it defaults to true. + #[prop_or(true)] + active: bool, +} +``` + +## Props 宏 + +`yew::props!` 宏允许您以与 `html!` 宏相同的方式构建属性。 + +该宏使用与结构体表达式相同的语法,只是您不能使用属性或基本表达式 (`Foo { ..base }`)。类型路径可以直接指向属性 (`path::to::Props`),也可以指向组件的关联属性 (`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, - /// 如果为 None,则 view 函数将不指定大小 + /// The view function will not specify a size if this is None. #[prop_or_default] - size: Option - /// 当 view 函数没有指定 active,其默认为 true - #[prop_or_else(true)] + size: Option, + /// 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 + } +} ``` diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/struct-components/refs.mdx b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/struct-components/refs.mdx index 75e9e1d9fd9..33af3eced2d 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/struct-components/refs.mdx +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/struct-components/refs.mdx @@ -1,23 +1,51 @@ --- -title: Refs -description: 超出界限的 DOM 访问 +title: '引用 (Refs)' +description: '实现越界 DOM 访问' --- -`ref` 关键词可被用在任何 HTML 元素或组件内部以获得该项所附加到的 DOM 元素。这可被用于在 `view` 生命周期方法之外来对 DOM 进行更改。 +`ref` 关键字可以在任何 HTML 元素或组件中使用,以获取附加到该元素的 DOM `Element`。这可以用于在 `view` 生命周期方法之外对 DOM 进行更改。 -这对于获取 canvas 元素或者滚动到页面的不同部分是有用的。 +这对于获取 canvas 元素或滚动到页面的不同部分很有用。例如,在组件的 `rendered` 方法中使用 `NodeRef` 允许您在从 `view` 渲染后对 canvas 元素进行绘制调用。 语法如下: ```rust -// 在 create 中 -self.node_ref = NodeRef::default(); +use web_sys::Element; +use yew::{html, Component, Context, Html, NodeRef}; -// 在 view 中 -html! { -
+struct Comp { + node_ref: NodeRef, } -// 在 update 中 -let has_attributes = self.node_ref.cast::().unwrap().has_attributes(); +impl Component for Comp { + type Message = (); + type Properties = (); + + fn create(_ctx: &Context) -> Self { + Self { + // highlight-next-line + node_ref: NodeRef::default(), + } + } + + fn view(&self, _ctx: &Context) -> Html { + html! { + // highlight-next-line +
+ } + } + + fn rendered(&mut self, _ctx: &Context, _first_render: bool) { + // highlight-start + let has_attributes = self.node_ref + .cast::() + .unwrap() + .has_attributes(); + // highlight-end + } +} ``` + +## 相关示例 + +- [节点引用](https://github.com/yewstack/yew/tree/master/examples/node_refs) diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/struct-components/scope.mdx b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/struct-components/scope.mdx new file mode 100644 index 00000000000..3431b927398 --- /dev/null +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-topics/struct-components/scope.mdx @@ -0,0 +1,72 @@ +--- +title: '作用域' +description: "组件的作用域" +--- + +## 组件的 `Scope<_>` 接口 + +`Scope` 是通过消息创建回调并更新自身的机制。我们通过在传递给组件的上下文对象上调用 `link()` 来获得对它的引用。 + +### `send_message` + +这个函数可以向组件发送消息。消息由 `update` 方法处理,该方法确定组件是否应重新渲染。 + +### `send_message_batch` + +这个函数可以同时向组件发送多个消息。这类似于 `send_message`,但是如果任何消息导致 `update` 方法返回 `true`,则组件将在处理完批处理中的所有消息后重新渲染。 + +如果给定的参数向量为空,则此函数不执行任何操作。 + +### `callback` + +创建一个回调,当执行时将向组件发送消息。在内部,它将使用提供的闭包返回的消息调用 `send_message`。 + +```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 + } + + fn view(&self, ctx: &Context) -> 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` + +创建一个回调,当执行时将向组件发送一批消息。与 `callback` 的区别在于,传递给此方法的闭包不必返回消息。相反,闭包可以返回 `Vec` 或 `Option`,其中 `Msg` 是组件的消息类型。 + +`Vec` 被视为一批消息,并在内部使用 `send_message_batch`。 + +`Option` 在值为 `Some` 时调用 `send_message`。如果值为 `None`,则不执行任何操作。这可以用于根据情况,不需要更新的情况。 + +这是通过使用仅为这些类型实现的 `SendAsMessage` trait 实现的。您可以为自己的类型实现 `SendAsMessage`,这样可以在 `batch_callback` 中使用它们。 diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/concepts/agents.mdx b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/concepts/agents.mdx index 725a13b5fc8..204aa056969 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/concepts/agents.mdx +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/concepts/agents.mdx @@ -1,40 +1,56 @@ --- -title: Agents -description: Yew 的 Actor 系统 +title: '代理 (Agents)' +description: "Yew's 的代理系统" --- -Agents 和 Angular 的 [Services](https://angular.io/guide/architecture-services) 相似(但没有依赖注入),给 Yew 提供了 [Actor 模型](https://en.wikipedia.org/wiki/Actor_model)。Agents 可以用于在组件之间路由消息,而与它们在组件层次结构中的位置无关,或者可以用于协调全局状态,或者可以用于从主 UI 线程上卸载计算密集型任务,或者在不同的标签页间通信(在未来)。 +import useBaseUrl from '@docusaurus/useBaseUrl' +import ThemedImage from '@theme/ThemedImage' -Agents 使用 [web-workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers) 同时运行来实现并发。 +代理 (Agents) 是一种将任务卸载到 Web Workers 的方式。 + +为了使代理能够并发运行,Yew 使用了 [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) + + + + +## 代理的类型 + +### 范围 + +- 公开 - 在任何给定时间,公共代理的实例最多只有一个。桥梁将在 Web Worker 中生成或连接到已经生成的代理。当没有桥梁连接到此代理时,代理将消失。 -## Agents 的类型 +- 私有 - 为每个新的桥梁在 Web Worker 中生成一个新的代理。这对于将与浏览器通信的共享但独立的行为从组件中移出是很好的。当连接的桥梁被丢弃时,代理将消失。 -#### Reaches +- 全局 \(WIP\) -- 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 +通信桥 (bridge) 是一个组件和代理之间的通信通道。它允许组件向代理发送消息,并接收来自代理的消息。 -Bridges 将连接到一个 Agent 并且允许双向通信。 +`use_bridge` 钩子也提供了在函数组件中创建桥梁的功能。 -### Dispatchers +### 派发器 (Dispatchers) -Dispatchers 和 Bridges 类似,但是他们只能发送消息给 Agents。 +派发器 (Dispatchers) 允许组件和代理之间进行单向通信,组件以此方式向代理发送消息。 ## 开销 -Agents 通过使用二进制码 bincode 序列化其消息来进行通信。因此,存在比仅调用函数相比更高的性能消耗。除非计算成本或者在任意组件间协调的需求超过消息传递的成本,否则你应该尽可能地在函数中包含你的应用逻辑。 +代理使用 Web Workers(即私有和公开)。它们在发送和接收消息时会产生序列化开销。代理使用 [bincode](https://github.com/bincode-org/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. +- [web_worker_fib](https://github.com/yewstack/yew/tree/master/examples/web_worker_fib) 示例展示了组件如何向代理发送消息并接收来自代理的消息。 diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/concepts/basic-web-technologies/css.mdx b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/concepts/basic-web-technologies/css.mdx new file mode 100644 index 00000000000..f0fa186dd61 --- /dev/null +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/concepts/basic-web-technologies/css.mdx @@ -0,0 +1,99 @@ +--- +title: '使用 classes! 宏处理 CSS 类' +description: '用一个方便的宏来处理 CSS 类' +comment: '尽量保持文件简短和简单。它的目的是让读者更容易地了解 Yew 中的组件,而不是提供正确的 API 文档' +--- + +import Tabs from '@theme/Tabs' +import TabItem from '@theme/TabItem' + +Yew 并没有提供原生的 CSS-in-Rust 解决方案,但通过提供编程方式与 HTML `class` 属性交互的方式来辅助样式。 + +## `classes!` 宏 + +`classes!` 宏和相关的 `Classes` 结构简化了 HTML 类的使用: + + + + +```rust +use yew::{classes, html}; + +html! { +
+}; +``` + +
+ + +```rust +use yew::{classes, html}; + +html! { +
+}; +``` + +
+ + +```rust +use yew::{classes, html}; + +html! { +
+}; +``` + +
+ + +```rust +use yew::{classes, html}; + +html! { +
+}; +``` + + + + +```rust +use yew::{classes, html}; + +html! { +
+}; +``` + +
+ + +```rust +use yew::{classes, html}; + +html! { +
+}; +``` + +
+ + +更多 CSS 相关的内容请参见[这个文档](../../more/css)。 + +## 内联样式 + +目前 Yew 并没有提供特殊的辅助工具来处理通过 `style` 属性指定的内联样式,但你可以像处理其他 HTML 属性一样处理它: + +```rust +use yew::{classes, html}; + +html! { +
+}; +``` + +更多 CSS 相关的内容请参见[这个文档](../../more/css)。 diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/concepts/basic-web-technologies/html.mdx b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/concepts/basic-web-technologies/html.mdx new file mode 100644 index 00000000000..b8514b78d8c --- /dev/null +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/concepts/basic-web-technologies/html.mdx @@ -0,0 +1,75 @@ +--- +title: '使用 html! 宏处理 HTML' +description: '这是 HTML,但不完全是!' +comment: '尽量保持文件简短和简单。它的目的是让读者更容易地了解 Yew 中的组件,而不是提供正确的 API 文档' +--- + +import Tabs from '@theme/Tabs' +import TabItem from '@theme/TabItem' + +你可以使用 `html!` 宏编写类似 HTML 的表达式。Yew 会在后台将其转换为表达 DOM 的 Rust 代码。 + +```rust +use yew::prelude::*; + +let my_header: Html = html! { + Girl in a jacket +}; +``` + +类似于格式化表达式,您可以通过使用花括号将周围上下文的值嵌入 HTML 中: + +```rust +use yew::prelude::*; + +let header_text = "Hello world".to_string(); +let header_html: Html = html! { +

{header_text}

+}; + +let count: usize = 5; +let counter_html: Html = html! { +

{"My age is: "}{count}

+}; + +let combined_html: Html = html! { +
{header_html}{counter_html}
+}; +``` + +使用 `html!` 有一个重要的规则 - 您只能返回一个包装节点。为了渲染多个元素的列表,`html!` 允许使用空标签(Fragments)。空标签是没有名称的标签,它们本身不会产生 HTML 元素。 + + + + +```rust , compile_fail +use yew::html; + +// error: only one root HTML element allowed +html! { + +
+

+ +}; +``` + +
+ + +```rust +use yew::html; + +// fixed: using HTML fragments +html! { + <> +
+

+ +}; +``` + +
+
+ +更多关于 Yew 和 HTML 的内容请参见[更多 HTML](concepts/html/introduction.mdx)。 diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/concepts/basic-web-technologies/js.mdx b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/concepts/basic-web-technologies/js.mdx new file mode 100644 index 00000000000..2a8dfa33afd --- /dev/null +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/concepts/basic-web-technologies/js.mdx @@ -0,0 +1,50 @@ +--- +title: 'Javascript 与 Rust' +description: '在 Rust 中使用 JavaScript' +comment: '尽量保持文件简短和简单。它的目的是让读者更容易地了解 Yew 中的组件,而不是提供正确的 API 文档' +--- + +import Tabs from '@theme/Tabs' +import TabItem from '@theme/TabItem' + +> Yew 在一个地方集中了一个可重用的 UI 部分可能需要的所有内容 - rust 文件,同时也在必要时保持底层技术的可访问性。 + +截至今天,WebAssembly 对于 DOM 交互还不完全支持。这意味着即使在 Yew 中,我们有时也依赖于调用 JavaScript。接下来是涉及的库的概述。 + +## wasm-bindgen + +[`wasm-bindgen`](https://github.com/rustwasm/wasm-bindgen) 是一个在 JavaScript 和 Rust 函数之间建立调用桥梁的库和工具。 + +我们强烈建议您查看他们的[文档](https://rustwasm.github.io/docs/wasm-bindgen/)和我们的[快速指南](./wasm-bindgen.mdx)。 + +## web-sys + +[`web-sys` crate](https://crates.io/crates/web-sys) 为 Web API 提供了绑定,并允许我们以一种经过 Rust 处理和安全的方式编写 JavaScript 代码。 + +示例: + + + + +```js +let document = window.document +``` + + + + + +```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"); +``` + + + + +再次强调,我们强烈建议您查看他们的[文档](https://rustwasm.github.io/docs/wasm-bindgen/)和我们的[快速指南](./web-sys.mdx)。 diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/concepts/basic-web-technologies/wasm-bindgen.mdx b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/concepts/basic-web-technologies/wasm-bindgen.mdx new file mode 100644 index 00000000000..9c6e131d8af --- /dev/null +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/concepts/basic-web-technologies/wasm-bindgen.mdx @@ -0,0 +1,318 @@ +--- +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/). + +[`wasm-bindgen`](https://github.com/rustwasm/wasm-bindgen) 是一个在 JavaScript 和 Rust 函数之间建立调用桥梁的库和工具。它是由 [Rust 和 WebAssembly 工作组](https://rustwasm.github.io/) 使用 Rust 构建的。 + +Yew uses `wasm-bindgen` to interact with the browser through a number of crates: + +Yew 使用 `wasm-bindgen` 通过一些 crate 与浏览器进行交互: + +- [`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 at a high level, 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/). + +本节将从更抽象的层次上探讨这些 crate,以便更容易地理解和使用 Yew 中的 `wasm-bindgen` API。要了解有关 `wasm-bindgen` 及其相关 crate 的更深入指南,请查看 [`wasm-bindgen` 指引](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). + +有关上述 crate 的文档,请查看 [`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` doc.rs 搜索来查找已使用 `wasm-bindgen` 导入的浏览器 API 和 JavaScript 类型。 +::: + +## [`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. + +这个 crate 为上面的其他 crate 提供了许多构建块。在本节中,我们只会涵盖 `wasm-bindgen` crate 的两个主要领域,即宏和一些您会一遍又一遍看到的类型/特性。 + +### `#[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 should not 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. + +`#[wasm_bindgen]` 宏提供了 Rust 和 JavaScript 之间的接口,提供了一个在两者之间进行转换的系统。使用这个宏更为高级,除非您要使用外部 JavaScript 库,否则不应该使用它。`js-sys` 和 `web-sys` crate 为内置 JavaScript 类型和浏览器 API 提供了 `wasm-bindgen` 定义。 + +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. + +让我们通过一个简单的示例来使用 `#[wasm-bindgen]` 宏来导入一些特定版本的 [`console.log`](https://developer.mozilla.org/en-US/docs/Web/API/Console/log) 函数。 + +```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)_. + +_这个示例是基于 [1.2 使用 console.log 的 `wasm-bindgen` 指引](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. + +在 JavaScript 类之间的继承是 JavaScript 语言的核心特性,DOM(文档对象模型)是围绕它设计的。当使用 `wasm-bindgen` 导入类型时,您还可以添加描述它们继承关系的属性。 + +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`. + +在 Rust 中,这种继承关系使用 [`Deref`](https://doc.rust-lang.org/std/ops/trait.Deref.html) 和 [`AsRef`](https://doc.rust-lang.org/std/convert/trait.AsRef.html) 特性来表示。这里举个例子可能会有所帮助;假设您有三种类型 `A`、`B` 和 `C`,其中 `C` 扩展了 `B`,而 `B` 又扩展了 `A`。 + +When importing these types the `#[wasm-bindgen]` macro will implement the `Deref` and `AsRef` +traits in the following way: + +在导入这些类型时,`#[wasm-bindgen]` 宏将按照以下方式实现 `Deref` 和 `AsRef` 特性: + +- `C` can `Deref` to `B` +- `B` can `Deref` to `A` +- `C` can be `AsRef` to `B` +- Both `C` & `B` can be `AsRef` to `A` + +- `C` 可以 `Deref` 到 `B` +- `B` 可以 `Deref` 到 `A` +- `C` 可以被 `AsRef` 到 `B` +- `C` 和 `B` 都可以被 `AsRef` 到 `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`. + +这些实现允许您在 `C` 的实例上调用 `A` 的方法,并将 `C` 用作 `&B` 或 `&A`。 + +It is 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`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/struct.JsValue.html) which has +its section below. + +需要注意的是,使用 `#[wasm-bindgen]` 导入的每种类型都有相同的根类型,您可以将其视为上面示例中的 `A`,这种类型是 [`JsValue`](#jsvalue),下面有它的部分。 + +_[extends section in The `wasm-bindgen` Guide](https://rustwasm.github.io/docs/wasm-bindgen/reference/attributes/on-js-imports/extends.html)_ + +_[`wasm-bindgen` 指引中的 extends 部分](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) + +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 does not have +a strong type system so any function that accepts a variable `x` does not 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. + +这是 JavaScript 拥有的对象的表示,这是 `wasm-bindgen` 的根捕获类型。任何来自 `wasm-bindgen` 的类型都是 `JsValue`,这是因为 JavaScript 没有强类型系统,因此接受变量 `x` 的任何函数都不定义其类型,因此 `x` 可以是有效的 JavaScript 值;因此 `JsValue`。如果您正在使用接受 `JsValue` 的导入函数或类型,那么任何导入的值在技术上都是有效的。 + +`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` 可以被函数接受,但该函数可能仍然只接受某些类型,这可能会导致 panic - 因此在使用原始 `wasm-bindgen` API 时,请检查导入的 JavaScript 的文档,以确定是否会在该值不是某种类型时引发异常(panic)。 + +_[`JsValue` documentation](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/struct.JsValue.html)._ + +_[`JsValue` 文档](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) + +Rust has a strong type system and JavaScript...doesn't 😞. 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 another, then you can use the functions of `JsCast` +to jump from one type to the other. It is a nice trait to get to know when working with `web-sys`, +`wasm_bindgen`, `js-sys` - you will notice lots of types will implement `JsCast` from those crates. + +Rust 有一个强类型系统,而 JavaScript……没有😞。为了让 Rust 保持这些强类型但仍然方便,WebAssembly 工作组提出了一个非常巧妙的特性 `JsCast`。它的工作是帮助您从一个 JavaScript "类型" 转换到另一个 "类型",这听起来很模糊,但它意味着如果您有一个类型,您知道它是另一个类型,那么您可以使用 `JsCast` 的函数从一个类型跳到另一个类型。当使用 `web-sys`、`wasm_bindgen`、`js-sys` 时,了解这个很好的特性 - 您会注意到许多类型将从这些 crate 中实现 `JsCast`。 + +`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). + +`JsCast` 提供了转换的检查和不检查方法 - 因此在运行时,如果您不确定某个对象是什么类型,您可以尝试将其转换,这将返回可能的失败类型,如 [`Option`](https://doc.rust-lang.org/std/option/enum.Option.html) 和 [`Result`](https://doc.rust-lang.org/std/result/enum.Result.html)。 + +一个常见的例子是在 [`web-sys`](./web-sys.mdx) 中,当您尝试获取事件的目标时。您可能知道目标元素是什么,但 [`web_sys::Event`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Event.html) API 总是会返回一个 [`Option`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Event.html#method.target)。 +您需要将其转换为元素类型,以便调用其方法。 + +```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::() { + // 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`. If the casting fails, the original `Self` value is returned in `Err`. You can try again +or do something else with the original type. + +[`dyn_ref`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/trait.JsCast.html#method.dyn_ref) 方法是一个检查的转换,返回一个 `Option<&T>`,这意味着如果转换失败,则可以再次使用原始类型,因此返回 `None`。[`dyn_into`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/trait.JsCast.html#method.dyn_into) 方法将消耗 `self`,这是 Rust 中 `into` 方法的约定,返回的类型是 `Result`。如果转换失败,则原始的 `Self` 值将在 `Err` 中返回。您可以再试一次或对原始类型进行其他操作。 + +_[`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 passed to +JavaScript must have a `'static` lifetime for soundness reasons. + +`Closure` 类型提供了一种将 Rust 闭包传递到 JavaScript 的方法,出于健全性原因,传递给 JavaScript 的闭包必须具有 `'static` 生命周期。 + +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. + +这种类型是一个“句柄”,意味着每当它被丢弃时,它将使其引用的 JS 闭包无效。在 `Closure` 被丢弃后,对 JS 中闭包的任何使用都将引发异常。 + +`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. + +当您使用接受类型 [`&js_sys::Function`](https://rustwasm.github.io/wasm-bindgen/api/js_sys/struct.Function.html) 的 `js-sys` 或 `web-sys` API 时,通常会使用 `Closure`。在 [Events](../html/events.mdx) 页面的 [Using `Closure` 部分](../html/events.mdx#using-closure-verbose) 中可以找到在 Yew 中使用 `Closure` 的示例。 + +_[`Closure` 文档](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. + +`js-sys` crate 提供了 JavaScript 标准内置对象的绑定/导入,包括它们的方法和属性。 + +This does not include any web APIs as this is what [`web-sys`](./web-sys.mdx) is for! + +这不包括任何 Web API,因为这是 [`web-sys`](./web-sys.mdx) 的作用! + +_[`js-sys` 文档](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. + +`wasm-bindgen-futures` crate 提供了一个桥梁,用于将 JavaScript Promise 类型作为 Rust [`Future`](https://doc.rust-lang.org/stable/std/future/trait.Future.html) 进行处理,并包含将 Rust Future 转换为 JavaScript Promise 的实用程序。当在 Rust(wasm)中处理异步或其他阻塞工作时,这可能很有用,并提供了与 JavaScript 事件和 JavaScript I/O 原语进行交互的能力。 + +There are three main interfaces in this crate currently: + +目前这个 crate 中有三个主要接口: + +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>`. This `Future` will resolve to `Ok` if + the `Promise` is resolved and `Err` if the `Promise` is rejected, containing the resolved or rejected + value from the `Promise` respectively. + +1. [`JsFuture`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen_futures/struct.JsFuture.html) - + 一个使用 [`Promise`](https://rustwasm.github.io/wasm-bindgen/api/js_sys/struct.Promise.html) 构造的类型,然后可以用作 `Future>`。如果 `Promise` 被解析,这个 `Future` 将解析为 `Ok`,如果 `Promise` 被拒绝,则解析为 `Err`,分别包含 `Promise` 的解析或拒绝值。 + +2. [`future_to_promise`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen_futures/fn.future_to_promise.html) - + Converts a Rust `Future>` into a + JavaScript `Promise`. The future’s result will translate to either a resolved or rejected + `Promise` in JavaScript. + +2. [`future_to_promise`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen_futures/fn.future_to_promise.html) - + 将 Rust `Future>` 转换为 JavaScript `Promise`。未来的结果将转换为 JavaScript 中的已解析或已拒绝 `Promise`。 + +3. [`spawn_local`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen_futures/fn.spawn_local.html) - + Spawns a `Future` on the current thread. This is the best way + to run a Future in Rust without sending it to JavaScript. + +3. [`spawn_local`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen_futures/fn.spawn_local.html) - + 在当前线程上生成一个 `Future`。这是在 Rust 中运行 Future 的最佳方法,而不是将其发送到 JavaScript。 + +_[`wasm-bindgen-futures` 文档](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. + +`spawn_local` 将是 Yew 中 `wasm-bindgen-futures` crate 中最常用的部分,因为这有助于使用具有异步 API 的库。 + +```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. + +Yew 还在某些 API 中添加了对 futures 的支持,最值得注意的是您可以创建一个接受 `async` 块的 `callback_future` - 这在内部使用了 `spawn_local`。 + +_[`spawn_local` 文档](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen_futures/fn.spawn_local.html)._ diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/concepts/basic-web-technologies/web-sys.mdx b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/concepts/basic-web-technologies/web-sys.mdx new file mode 100644 index 00000000000..bf48874bb5f --- /dev/null +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/concepts/basic-web-technologies/web-sys.mdx @@ -0,0 +1,220 @@ +--- +title: 'web-sys' +description: 'web-sys crate 为 Web API 提供绑定。' +--- + +import Tabs from '@theme/Tabs' +import TabItem from '@theme/TabItem' + +[`web-sys` crate](https://crates.io/crates/web-sys) 为 Web API 提供绑定。这是从浏览器 WebIDL 生成的,这就是为什么有些名称如此之长,有些类型如此模糊的原因。 + +## `web-sys` 中的特性 (features) + +`web-sys` crate 中启用了所有特性可能会给 Wasm 应用程序增加很多冗余。为了解决这个问题,大多数类型都是通过启用 features 进行控制的,这样你只需要包含你的应用程序所需的类型。Yew 启用了 `web-sys` 的几个特性,并在其公共 API 中公开了一些类型。你通常需要自行将 `web-sys` 添加为依赖项。 + +## `web-sys` 中的继承 + +在[模拟继承](./wasm-bindgen.mdx#simulating-inheritance)部分,你可以了解到 Rust 通常提供了一种模拟 JavaScript 中继承的方法。这在 `web-sys` 中非常重要,因为了解一个类型上有哪些方法意味着了解它的继承。 + +这一部分将查看一个特定的元素,并使用 Rust 调用 [`Deref::deref`](https://doc.rust-lang.org/std/ops/trait.Deref.html#tymethod.deref) 列出其继承,直到该值为 [`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