属性
属性通常缩写为“Props”。
属性本质上是 Yew 可以监视的组件参数。
在将类型用作组件的属性之前,必须实现Properties
特性。
响应性
在重新渲染期间协调虚拟 DOM 时,Yew 会检查道具是否已更改,以了解是否需要重新渲染嵌套组件。通过这种方式,Yew 可以被认为是一个非常响应的框架,因为父级中的更改将始终向下传播,并且视图永远不会与来自道具/状态的数据不同步。
如果您尚未完成教程,请尝试一下并自己测试这种响应性!
派生宏
Yew 提供了一个派生宏,可以轻松地在结构上实现Properties
特性。
为其派生Properties
的类型还必须实现PartialEq
,以便 Yew 可以进行数据比较。
use yew::Properties;
#[derive(Properties, PartialEq)]
pub struct Props {
pub is_loading: bool,
}
在函数组件中使用
属性#[function_component]
允许在函数参数中接收 Props。要提供它们,它们通过html!
宏中的属性进行分配。
- 带 Props
- 无 Props
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} />}
}
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 />}
}
派生宏字段属性
派生Properties
时,默认情况下所有字段都是必需的。以下属性允许您为道具提供默认值,当父级未设置这些值时将使用这些值。
属性在 Rustdoc 生成的文档中不可见。属性的文档字符串应提及道具是否可选以及是否具有特殊的默认值。
- #[prop_or_default]
- #[prop_or(value)]
- #[prop_or_else(function)]
使用 Default
特性,使用字段类型的默认值初始化 prop 值。
use yew::{function_component, html, Html, Properties};
#[derive(Properties, PartialEq)]
pub struct Props {
#[prop_or_default]
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} />}
}
使用 value
初始化 prop 值。value
可以是返回字段类型的任何表达式。例如,要将布尔 prop 的默认值设为 true
,请使用属性 #[prop_or(true)]
。在构造属性且未给出显式值时,将计算表达式。
use yew::{function_component, html, Html, Properties};
#[derive(Properties, PartialEq)]
pub struct Props {
#[prop_or("Bob".to_string())]
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()} />}
}
调用 function
初始化 prop 值。function
应具有签名 FnMut() -> T
,其中 T
是字段类型。在未为该属性给出显式值时,将调用该函数。
use yew::{function_component, html, Html, Properties};
fn create_default_name() -> String {
"Bob".to_string()
}
#[derive(Properties, PartialEq)]
pub struct Props {
#[prop_or_else(create_default_name)]
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()} />}
}
使用属性的内存/速度开销
内部属性是引用计数的。这意味着仅将共享指针向下传递到组件树以获取 prop。这使我们节省了克隆整个 prop 的成本,这可能是昂贵的。
利用 AttrValue
,这是我们自定义的属性值类型,而不是将它们定义为 String 或其他类似类型。
Prop 宏
yew::props!
宏允许你以与 html!
宏相同的方式构建属性。
该宏使用与结构表达式相同的语法,但你不能使用属性或基本表达式 (Foo { ..base }
)。类型路径可以指向 prop 本身 (path::to::Props
) 或组件的相关属性 (MyComp::Properties
)。
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 {
let pre_made_props = props! {
Props {} // Notice we did not need to specify name prop
};
html! {<HelloWorld ..pre_made_props />}
}
计算顺序
Prop 按指定顺序计算,如下例所示
#[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);
}
反模式
虽然几乎任何 Rust 类型都可以作为属性传递,但有一些反模式应该避免。这些包括但不限于
- 使用
String
类型,而不是AttrValue
。
为什么这样做不好?String
克隆成本较高。当 prop 值与钩子和回调一起使用时,通常需要克隆。AttrValue
是引用计数字符串 (Rc<str>
) 或&'static str
,因此克隆成本很低。
注意:AttrValue
内部是 implicit-clone 中的IString
。请参阅该 crate 以了解更多信息。 - 使用内部可变性。
为什么这样做不好? 通常情况下,应避免使用内部可变性(例如RefCell
、Mutex
等)。它可能会导致重新渲染问题(Yew 不知道状态何时已更改),因此你可能必须手动强制渲染。与所有事物一样,它有其用途。请谨慎使用。 - 你告诉我们。你是否遇到了希望早点了解到的极端情况?请随时创建问题或 PR 来修复此文档。
yew-autoprops
yew-autoprops 是一个实验性包,它允许你根据函数的参数动态创建 Props 结构。如果从未重复使用过属性结构,它可能会很有用。