子組件
警告
檢查和操作 Children
往往會導致應用程式中令人驚訝且難以解釋的行為。這可能導致邊緣情況,並且通常不會產生預期的結果。如果您嘗試操作 Children
,則應考慮其他方法。
Yew 支援將 Html
用作子元件屬性的類型。如果您不需要 Children
或 ChildrenRenderer
,則應使用 Html
作為子元件。它沒有 Children
的缺點,且效能開銷較低。
一般用法
*大多數情況下,*當允許元件具有子元件時,您不關心元件具有的子元件的類型。在這種情況下,下面的範例就足夠了。
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 {
Self
}
fn view(&self, ctx: &Context<Self>) -> Html {
html! {
<div class="list">
{ctx.props().children.clone()}
</div>
}
}
}
進階用法
類型化子元件
在您希望將一種類型的元件作為子元件傳遞給您的元件的情況下,您可以使用 yew::html::ChildrenWithProps<T>
。
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>
}
}
}
帶有屬性的巢狀子元件
如果包含元件對其子元件進行了類型化,則可以存取和變更巢狀元件的屬性。
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>
};
枚舉類型的子元件
當然,有時您可能需要將子元件限制為幾種不同的元件。在這些情況下,您必須更深入地了解 Yew。
這裡使用 derive_more
來提供更好的人體工學。如果您不想使用它,您可以為每個變體手動實現 From
。
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>),
}
// 現在,我們實現 `Into<Html>`,以便 yew 知道如何渲染 `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>
}
}
}
可選類型的子元件
您也可以具有特定類型的單一可選子元件:
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() }
// ... 页面内容
</div>
}
}
}
// 页面组件可以选择是否附带侧边栏:
pub fn render_page(with_sidebar: bool) -> Html {
if with_sidebar {
// 附带侧边栏的页面
html! {
<Page sidebar={html_nested! {
<PageSideBar />
}} />
}
} else {
// 不附带 侧边栏的页面
html! {
<Page />
}
}
}
進一步閱讀
- 有關此模式的真實範例,請查閱 yew-router 的原始程式碼。有關更高級的範例,請查看 yew 儲存庫中的相關範例清單