リスト
イテレータ
イテレータから HTML を構築する方法は 4 つあります:
- `for` ループ
- `while` ループ
- `for` ブロック
- `collect` メソッド
主なアプローチは for ループを使用することです。これは Rust に既に存在する for ループと同じですが、1 つの重要な違いがあります:通常の for ループは何も返せませんが、html! 内の for ループはノードのリストに変換されます。
use yew::prelude::*;
html! {
for i in 0 .. 10 {
<span>{i}</span>
}
};
for ループ本体では、html 子要素の前に Rust の文を置くことができます。終端子を持つ文であれば何でも動作します:let バインディング、; で終わる式文、アイテム定義(fn、struct、use など)、; 付きのマクロ呼び出しです。
use yew::prelude::*;
html! {
for item in items {
let label = format!("{}: {}", item.id, item.name);
let class = if item.active { "active" } else { "inactive" };
<div class={class}>{label}</div>
}
};
break と continue は通常の Rust のループと同様に動作し、出力されるノードリストに影響します。
continue は現在の反復でノードを生成せずにスキップし、break は反復を早期に終了します:
use yew::prelude::*;
html! {
for i in 0..10 {
if i % 2 == 0 {
continue
}
if i > 7 {
break
}
<span>{i}</span>
}
};
break と continue は、波括弧付きでも波括弧なしでも、match アームの本体としてそのまま使えます:
use yew::prelude::*;
html! {
for i in 0..10 {
match i {
0 => continue,
8.. => break,
_ => <span>{i}</span>,
}
}
};
ループラベルもサポートされています。break 'label や continue 'label は、マクロを囲む Rust コードで定義されたラベル付きループを対象にでき、マクロの内側から外側のループを抜けることができます:
use yew::prelude::*;
let mut rendered = Vec::new();
'outer: for section in sections {
rendered.push(html! {
for item in section.items {
if should_stop(&item) {
break 'outer;
}
<span>{item.name}</span>
}
});
}
<Type>::method(...) のような裸の限定パス式は <Type> という要素開始タグと衝突し、受け付けられません。回避方法は 2 つあります:
- 末尾に
;を付けてプリアンブルの式文にします:<Type>::method(...);。戻り値は破棄されます。 {...}で囲んで戻り値をノードとして使います:{ <Type>::method(...) }。
while および while let ループは for と同じように動作し、本体からノードのリストを生成します。for 本体で受け付けられるすべての文の形式(let バインディング、式文、アイテム、マクロ)がここでも受け付けられ、if ブロックと break / continue も使えます。
use yew::prelude::*;
let mut items = vec!["a", "b", "c", "skip", "d"].into_iter();
html! {
while let Some(item) = items.next() {
if item == "skip" {
continue
}
if item.is_empty() {
break
}
<span>{item}</span>
}
};
条件付きの通常の while も使用できます:
use yew::prelude::*;
let mut counter: i32 = 0;
html! {
while counter < 5 {
let current = counter;
counter += 1;
<span>{current}</span>
}
};
もう一つの方法は for キーワードを使用することです。これはネイティブの Rust 構文ではなく、HTML マクロによってイテレータを表示するために必要なコードを出力します。
この方法は、イテレータが既に計算されていて、マクロに渡すだけでよい場合に最初の方法より適しています。
use yew::prelude::*;
let items = (1..=10).collect::<Vec<_>>();
html! {
<ul class="item-list">
{ for items.iter() }
</ul>
};
最後の方法は、イテレータの最終変換で collect::<Html>() を呼び出すことで、Yew が表示できるリストを返します。
use yew::prelude::*;
let items = (1..=10).collect::<Vec<_>>();
html! {
<ul class="item-list">
{ items.iter().collect::<Html>() }
</ul>
};
キー付きリスト
キー付きリストは、すべての子要素にキーがある最適化されたリストです。
key は Yew が提供する特別な属性で、HTML 要素やコンポーネントに一意の識別子を与え、Yew 内部での最適化に使用されます。
キーは各リスト内で一意である必要があり、HTML の id のようにグローバルに一意である必要はありません。リストの順序に依存してはいけません。
リストにキーを追加することを常にお勧めします。
一意の String、str、または整数を特別な key 属性に渡すことでキーを追加できます。
use yew::prelude::*;
let names = vec!["Sam","Bob","Ray"]
html! {
<div id="introductions">
{
names.into_iter().map(|name| {
html!{<div key={name}>{ format!("Hello, I'am {}!",name) }</div>}
}).collect::<Html>()
}
</div>
};
パフォーマンスの最適化
キー付きリストのパフォーマンス向上をテストするための例があります。以下は簡単なテスト手順です:
- オンラインデモにアクセスします。
- 500個の要素を追加します。
- キーを無効にします。
- リストを反転します。
- "最後のレンダリングにかかった時間 Xms" を確認します(この記事の執筆時点では約60ms)。
- キーを有効にします。
- 再度リストを反転します。
- "最後のレンダリングにかかった時間 Xms" を確認します(この記事の執筆時点では約30ms)。
この記事の執筆時点では、500個のコンポーネントに対して速度が2倍に向上しました。
原理の説明
通常、リストを反復処理する際には、各リスト項目にキーを追加するだけで済みます。データの順序が変わる可能性があるためです。 リストを再レンダリングする際に、キーは調整プロセスを高速化するために使用されます。
キーがない場合、例えば ["bob", "sam", "rob"] を反復処理すると、最終的に以下のようなHTMLが生成されます:
<div id="bob">My name is Bob</div>
<div id="sam">My name is Sam</div>
<div id="rob">My name is rob</div>
次のレンダリングでリストが ["bob", "rob"] に変更された場合、Yew は id="rob" の要素を削除し、id="sam" を id="rob" に更新できます。
各要素にキーを追加すると、初期の HTML は変わりませんが、変更後のリスト ["bob", "rob"] をレンダリングすると、Yew は2番目の HTML 要素のみを削除し、他の要素はそのまま残ります。キーを使用して要素を関連付けることができるためです。
コンポーネントから別のコンポーネントに切り替える際に、両方に最高レンダリング要素として div がある場合にバグ/"機能" に遭遇した場合。 Yew はこれらの状況で最適化として既にレンダリングされた HTML div を再利用します。 その div を再利用せずに再作成する必要がある場合は、異なるキーを追加することで再利用されなくなります。