最適化とベストプラクティス
スマートポインタの効果的な使用
注: このセクションで使用されている用語の一部が不明な場合は、Rust の書籍にスマートポインタに関する有用な章があります。
再レンダリング時に大量のデータをクローンしてプロップを作成するのを避けるために、スマートポインタを使用して、データ自体ではなくデータへの参照のみをクローンできます。プロップと子コンポーネントに関連データへの参照を渡すことで、子コンポーネントで変更する必要があるまでデータのクローン作成を回避できます。子コンポーネントでは、`Rc::make_mut`を使用して、変更したいデータのクローンを作成し、変更可能な参照を取得できます。
これにより、プロップの変更がコンポーネントの再レンダリングを必要とするかどうかを判断する際の`Component::changed`でさらなる利点が生じます。これは、データの値を比較する代わりに、基となるポインタアドレス(つまり、データが格納されているマシンのメモリ内の位置)を比較できるためです。2つのポインタが同じデータを参照している場合、それらが指すデータの値は同じでなければなりません。ただし、逆は必ずしも真ではありません!2つのポインタアドレスが異なっていても、基となるデータは同じ場合があります。この場合は、基となるデータを比較する必要があります。
この比較を行うには、等値演算子`==`を使用してデータを比較する場合に自動的に使用される`PartialEq`ではなく、`Rc::ptr_eq`を使用する必要があります。Rust のドキュメントには`Rc::ptr_eq`に関する詳細があります。
この最適化は、`Copy`を実装しないデータ型で最も役立ちます。データを安価にコピーできる場合は、スマートポインタの背後に置く価値はありません。`Vec`、`HashMap`、`String`などのデータが大きくなる可能性のある構造体の場合、スマートポインタを使用するとパフォーマンスが向上する可能性が高いです。
この最適化は、値が子によって決して更新されない場合、さらに親によってめったに更新されない場合に最適です。これにより、`Rc<_>`は純粋なコンポーネントのプロパティ値をラップするのに適した選択肢になります。
ただし、子コンポーネントで自分でデータをクローンする必要がない限り、この最適化は無意味であるだけでなく、参照カウントの不要なコストも追加されます。Yew のプロップは既に参照カウントされており、内部的にはデータのクローンは発生しません。
ビュー関数
コードの可読性の理由から、`html!`のセクションを独自の関数に移行することが理にかなっています。これにより、インデントの量を減らすことでコードの可読性が向上するだけでなく、優れた設計パターン(特に、これらの関数を複数の場所で呼び出すことができるため、記述する必要があるコードの量を減らすことができる、コンポーザブルなアプリケーションの構築に関するパターン)が促進されます。
ピュアコンポーネント
ピュアコンポーネントは、状態を変化させず、コンテンツを表示してメッセージを通常の変更可能なコンポーネントに伝播するのみのコンポーネントです。ビュー関数とは、コンポーネント構文(`
ワークスペースを使用したコンパイル時間の削減
Yew を使用することの最大の欠点はおそらく、Yew アプリのコンパイルに時間がかかることです。プロジェクトのコンパイルにかかる時間は、`html!`マクロに渡されるコードの量に関連しているようです。これは小規模なプロジェクトではそれほど問題ではありませんが、大規模なアプリケーションでは、コンパイラがアプリケーションへの各変更に対して行う作業量を最小限に抑えるために、複数のクレートにコードを分割することが理にかなっています。
1つの可能なアプローチは、メインクレートでルーティング/ページの選択を処理し、各ページに異なるクレートを作成することです。各ページは異なるコンポーネントでも、`Html`を生成する大きな関数でもかまいません。アプリケーションの異なる部分を構成するクレート間で共有されるコードは、プロジェクトが依存する別のクレートに格納できます。最高のシナリオでは、各コンパイルでコード全体を再構築することから、メインクレートと1つのページクレートのみを再構築することに移行します。「共通」クレートに変更を加えた最悪のケースでは、元の状態に戻り、その共有クレートに依存するすべてのコード(おそらくそれ以外のすべて)をコンパイルすることになります。
メインクレートが大きすぎる場合、または深くネストされたページ(たとえば、別のページの上にレンダリングされるページ)を迅速に反復処理したい場合は、サンプルクレートを使用してメインページの簡略化された実装を作成し、さらに作業中のコンポーネントをレンダリングできます。
バイナリサイズの削減
- Rust コードの最適化
- `cargo.toml`(リリースプロファイルの定義)
- `wasm-opt`を使用した Wasm コードの最適化
注: バイナリサイズの削減に関する詳細については、Rust Wasm の書籍を参照してください。
Cargo.toml
`Cargo.toml`の`[profile.release]`セクションで使用可能な設定を使用して、リリースビルドのサイズを小さく構成できます。
[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
Nightly Cargo の構成
Rust と Cargo の実験的な Nightly 機能からも追加のメリットを得ることができます。`trunk`でNightly ツールチェーンを使用するには、`RUSTUP_TOOLCHAIN="nightly"`環境変数を設定します。その後、`.cargo/config.toml`で不安定な rustc 機能を構成できます。不安定な機能のドキュメント、特に`build-std`と`build-std-features`に関するセクションを参照して、構成を理解してください。
[unstable]
# Requires the rust-src component. `rustup +nightly component add rust-src`
build-std = ["std", "panic_abort"]
build-std-features = ["panic_immediate_abort"]
Nightly Rust コンパイラには、これのようなバグが含まれていることがあり、時折注意と調整が必要です。これらの実験的なオプションは注意して使用してください。
wasm-opt
さらに、`wasm`コードのサイズを最適化することもできます。
Rust Wasm の書籍には、Wasm バイナリのサイズを削減する方法について説明するセクションがあります。.wasm サイズの縮小
- `wasm-pack`の使用(デフォルトでリリースビルドで`wasm`コードを最適化します)
- `wasm`ファイルに`wasm-opt`を直接使用します。
wasm-opt wasm_bg.wasm -Os -o wasm_bg_opt.wasm
yew/examples/ にある「最小限」の例のビルドサイズ
注:`wasm-pack`は、Rust と Wasm コードの最適化を組み合わせます。この例では、Rust のサイズ最適化なしで`wasm-bindgen`が使用されています。
使用ツール | サイズ |
---|---|
wasm-bindgen | 158KB |
wasm-bindgen + wasm-opt -Os | 116KB |
wasm-pack | 99 KB |