Vue3コンポーネントの実装のしやすさについて Vision.9
Vue3のコンポーネントの実装について、前回はVue3コンポーネントの詳細から、
props渡しの詳細について、まとめました。
前回:props渡しの詳細
今回は、一方向のデータフローを以下にまとめます。
Vue3のコンポーネントの詳細5
公式ページ:一方向のデータフローを読み進めながら、以下のように解釈。
一方向バインディング
全てのpropsは、子のプロパティと親のプロパティとの間に
一方向バインディングが形成されます。
親プロパティの更新時、子プロパティに情報が伝達されますが
子から親への伝達はありません。
一方向バインディングによって、親の状態が誤って子コンポーネントによって
変更される事態を防ぎ、アプリのデータフローが把握しにくくなるといった
事態を減らすことができます。
更にコンポーネント単位で見た場合、親コンポーネントの更新の都度
子コンポーネント内の全てのpropsは最新の値に、更新されます。
そのため、子コンポーネント内でpropsの値を更新するのは厳禁です。
もし子コンポーネント内でpropsの値を更新しようとすると
Vueがコンソールでエラーを発します。
実際に実装してみて、エラーが出ることを確認できました。
propsを設定・変更するシチュエーション
通常、propsを設定・変更したい状況には以下の2つがあります。
- 静的な初期値
propsは静的な初期値を渡すために用いて、子コンポーネントを初期化する。
初期化処理以降、子コンポーネントではローカルのデータプロパティとして
親コンポーネントの更新から切り離して、固定値として利用したい。この場合、以下のようにローカルの
データプロパティを定義して、その初期値にpropsを使用するのが最も適切です。const props = defineProps(['initialCounter']) // props.initialCounter は counter の初期値を指定するためだけに // 使われ、今後発生するpropsの更新からは切り離されます。 const counter = ref(props.initialCounter)
- 動的な変数値
親コンポーネントがpropsの値を変更する都度、子コンポーネントのpropsも変更したい。
この場合、以下のような算出プロパティを定義して、その中でpropsの値を利用するのが最も適切です。const props = defineProps(['size']) // propsが変更されると自動的に更新される算出プロパティ const normalizedSize = computed(() => props.size.trim().toLowerCase())
オブジェクト/配列のprops変更は許容されてしまう
親コンポーネントから、オブジェクトや配列をpropsとして渡した場合
子コンポーネント側でpropsの値を変更することはできません。
しかし、オブジェクトや配列のネストされたプロパティを変更することは可能です。
これは、JavaScriptの言語仕様では、今回のように呼び出し元と呼び出し先で
オブジェクトを共有することが理由です。
Vue側で、オブジェクトや配列の内部値の変更を防ぐ、基底機能を作るのは
コスト的に非現実的なため実装していません。
このような変更の主な欠点は、親コンポーネントにとって不明瞭な方法で
子コンポーネントが親の状態に影響を与えることを許してしまい、
後からデータの流れを見極めるのが難しくなる可能性があることです。
親と子を密に結合させる設計でない限り、ベストプラクティスとしては
そのような変更を避けるべきです。ほとんどの場合、子コンポーネントはイベントを発行
して、変更を親コンポーネントに実行してもらう必要があります。
上記、Vueの事情を汲み取って、実業務の上では、実装ルールとして制約を
設けるべき事項と考えました。