屬性
屬性通常簡稱為「Props」。
屬性基本上是可以由 Yew 持續監控的組件引數。
一種類型必須先實作 Properties
性狀,才可以使用為組件的屬性。
重新整理時,Yew 會在比對虛擬 DOM 時檢查 prop 是否已變更,以得知是否需要重新整理巢狀元件。基於此情況,Yew 可以被認為是一種反應非常快速的架構,因為父元件的變更會永遠向下傳播,而且這個檢視永遠不會與 prop/狀態所提供的資料不同步。
如果您尚未完成教學課程,請嘗試自行測試這個反應能力!
派生巨集
Yew 提供一個派生巨集,可輕鬆在結構中實作 `Properties` 特徵。
類型派生 `Properties` 也必須實作 `PartialEq`,這樣 Yew 才能進行資料比較。
use yew::Properties;
#[derive(Properties, PartialEq)]
pub struct Props {
pub is_loading: bool,
}
用於函式元件
屬性 `#[function_component]` 允許在函式引數中選用接收 Props。若要提供 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` 時,預設所有欄位都是必要的。下列屬性允許您提供 Props 預設值,當父項未設定時會使用此預設值。
屬性不會顯示在 Rustdoc 生成的文件中。您的屬性文件字串應說明 prop 是否為選用,以及是否具有特殊預設值。
- #[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()} />}
}
使用 Properties 的記憶體/速度開銷
內部 Properties 是參考計數的,這代表 props 只會沿著元件樹傳遞一個共享指標,讓不會因複製整個 props(成本可能會很高)而產生開銷。
使用 AttrValue
,作為屬性值的自訂類型,而不是將它們定義為字串或其他類似類型。
Props 巨集
yew::props!
巨集讓你可以用與 html!
巨集相同的方式建構 props。
巨集使用與結構表達式相同的語法,但你無法使用屬性或基本表達式(Foo { ..base }
)。類型路徑可以指向 props 本身(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 />}
}
評估順序
Props 按指定順序評估,如下例所示
#[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 類型都可以傳遞為 props,但有一些反模式應該避免,包括但不限於以下
- 使用
String
類型而非AttrValue
。
為什麼不好?String
複製的成本很高,當 prop 值與 hooks 和回呼搭配使用時,通常需要複製。AttrValue
是參考計數字串(Rc<str>
)或&'static str
,因此複製成本非常低。
注意:AttrValue
在內部是 implicit-clone 中的IString
,請參閱該 crate 以了解更多資訊。 - 使用內部可變性。
為什麼不好? 一般而言,應該避免內部可變性(例如RefCell
、Mutex
等)。它會造成重新渲染問題(Yew 不知道狀態已變更),所以你可能必須手動強制進行一次渲染。與所有事物一樣,它有其存在的意義,請謹慎使用。 - 告訴我們你的看法,你是否曾遇到希望自己早一點知道的極端狀況?歡迎建立議題或 PR 來修正此文件。
yew-autoprops
yew-autoprops 是個實驗性套件,它允許你在函式的參數中動態建立 Props struct。可能會很有用,如果 properties struct 絕不會被重複使用。