最近Vue.jsを使ったプロジェクトに手を出しています。
そこでコンポーネント間で値をやり取りする方法を調べていましたが、
こうするべきでしょ、というのを私なりに探っていった結果をまとめました。
こちらのコードの例で使用しているVueのバージョンは2.5.21です。
親から子に単方向でやり取りする方法
親コンポーネントから子コンポーネントに一方的にデータの値を渡して、
子からはそれを変更しても、親に変更は伝搬させない場合のやり方です。
シンプルにprops
に渡す
props
で親のもつ値を渡します。Vueを使うなら普通のやり方ですね。
See the Pen vue component test parent -&rt; child by stoba (@rhistoba) on CodePen.
子から親に単方向でやり取りする方法
子コンポーネントのもつデータの変更を、親コンポーネントに一方的に伝搬させる方法です。
$emit
を使う
子コンポーネントでthis.$emit
を実行すると、値の変更があったことをイベントとして値とともに親に知らせることができます。
親はそのイベントがトリガされた場合の処理をmethods
に書きます。
See the Pen vue component test parent <- child by stoba (@rhistoba) on CodePen.
(好ましくない例)$parent
で親のプロパティを参照する
子コンポーネントでthis.$parent
を使って親コンポーネントのデータを参照して、そこに直接値を代入する方法です。
しかし、これは親がデータとして何を持つべきかを子が縛ることになるので、使わない方がいいでしょう。
See the Pen vue component test parent <- child bad case by stoba (@rhistoba) on CodePen.
親子で双方向にやり取りする方法
親子両方からの変更に対して、双方ともにデータの変更を伝搬させる方法です。
(不可能な例)子のprops
の値を直接変更する
子のprops
にある、親から渡されたデータを直接変更すればよさそうに思えますが、不可能です。
props
の子からの変更は親に対して変更を与えません。
参考: https://jp.vuejs.org/v2/guide/components-props.html#%E5%8D%98%E6%96%B9%E5%90%91%E3%81%AE%E3%83%87%E3%83%BC%E3%82%BF%E3%83%95%E3%83%AD%E3%83%BC
See the Pen vue component test parent <-&rt; child bad case 1 by stoba (@rhistoba) on CodePen.
(好ましくない例)$parent
を使う
前述の$parent
を使って親のデータを直接参照する方法です。
実現こそ可能でコード量こそ少なくて済みますが、前述通り親のデータを子が縛ることになるので、
使わない方がいいでしょう。
See the Pen vue component test parent <-&rt; child bad case $parent by stoba (@rhistoba) on CodePen.
props
で親のデータを受け取り、$emit
で変更をトリガする
前述の単方向のやり方を組み合わせた方法です。
冗長な感じに思えますが、親子間の双方向のやり取りはこれが基本となると思います。
See the Pen vue component test parent <-&rt; child by stoba (@rhistoba) on CodePen.
v-model
を使う
v-model
ディレクティブを使うことで、上の冗長な感じをある程度改善することができます。
親側で子コンポーネントを使用する際、v-model="message"
と記述することで、
:value="message" @input="message = $event"
と同義になります。
そのため、子コンポーネントでプロパティにvalue
を指定して、$emit('input')
でイベントを送出することで、
親側で子のプロパティ変更のイベントに対する処理を記述する必要が無くなり、コンポーネントが再利用しやすくなります。
参考: https://jp.vuejs.org/v2/guide/components.html#%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E3%81%A7-v-model-%E3%82%92%E4%BD%BF%E3%81%86
ただし、子コンポーネントのプロパティで使う名前が強制的にvalue
になってしまうため、
これにエイリアスを与えてどのような値であるかを明示的にしたいですね。
そのため、computed
で単純にvalue
を返すgetterメソッドを用意するようにしましょう。
双方向のやり取りは、このやり方が最も実用的ではないでしょうか。
See the Pen vue component test parent <-&rt; child v-model bad case by stoba (@rhistoba) on CodePen.
まとめ
以上、Vueで親子コンポーネントでデータの値をやり取りする方法でした。
重要なのは、変更の処理($emit
)と参照(computed
など)を切り分けて明確に行うことだと考えます。
これはRedux
やVuex
といったfluxアーキテクチャでも同様な考えが含まれますが、
Vue単体でもそれを考慮したプログラミングが必要だと思います。