Vueの親子コンポーネント間のデータ受け渡し方法を比較・網羅してみた

# 前置き
## この記事の目的・背景
- 親子コンポーネント間でデータ受け渡しする実装をする際、Vueのどの機能を使うか結構迷う、、、。
- ただ、意外と、親子コンポーネント間のデータ受け渡しの方法を一覧にして比較しながら紹介している記事がない
- chapGPT3.5(2024年4月現在、無料で使える最新版)にて、「vueの親子間コンポーネントでのデータ受け渡しの方法を網羅的に教えて」と聞いても、僕ごときから見ても情報の不足が目立つ


## この記事の内容
- 複数あるデータ受け渡し方法をどう使い分けるかのフローチャート(結論)
- 複数あるデータ受け渡し方法の概要、用途について
    - 各実装方法の詳しい仕様については割愛する。公式リファレンスのリンクを各章に貼っているのでそちらをご覧ください

## 対象読者
- Vueにおいて、親子コンポーネント間のデータ受け渡しの際に、どの機能を使おうか迷いがちな人
- 親子コンポーネント間のデータ受け渡しのバリエーションを増やしたい人、復習したい人


## 前提・備考
- Vue ver3.4.21
- 筆者はVueを仕事で使い始めて1年くらいの若造です。情報に間違い、不足があるかもしれませんので、その際はドシドシご指摘ください
    - ご指摘あればその都度更新予定です!

# 結論
![vue-data-transfer-methods.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3355005/1d28b3ae-cb61-5760-8848-264c3da0eeed.png)


# 親から子へデータを渡す方法一覧
## props
### 概要
- 関数呼び出しのようなイメージ。子コンポーネント側で受け入れるデータを引数のように定義し、それを呼び出す親が、それに合ったデータを渡す
- 親コンポーネントでのデータ変更が、そのデータを渡された子コンポーネントにも同期される
- 子コンポーネント側でそのデータを変更することはできない(子においてはreadonly)
### 用途
- 親から子への一方向のデータ渡しをしたい場合(子→親が不要な時)


### 公式リファレンス
[https://ja.vuejs.org/guide/components/props.html](https://ja.vuejs.org/guide/components/props.html)

## Provide/Inject
### 概要
- 親コンポーネントから、子孫コンポーネントに対して、`props`を使わずに直接データを渡せる
- 親でのデータ変更は、子孫コンポーネントすべてに反映される
### propsとの比較
- `props`が子コンポーネントにのみデータを渡すのに対し、これは、子孫コンポーネント全体に直接データを渡すことができる
### 用途
- 親→ひ孫にデータを渡す時など、propsをつかうと、何回もpropsの受け渡しロジックを記述しなくてはならず面倒くさい場合
- 公式では"propsのバケツリレー"と呼ばれています
    - https://ja.vuejs.org/guide/components/provide-inject.html#prop-drilling


### 公式リファレンス
[https://ja.vuejs.org/guide/components/provide-inject.html](https://ja.vuejs.org/guide/components/provide-inject.html)


## slot
### 概要・用途
- 親から、子のdom要素の一部を指定できる
### props, Provide/Injectとの比較
- script上の変数などのデータではなく、子の**dom要素**を親から指定できるというもの

### 公式リファレンス
[https://ja.vuejs.org/guide/components/slots.html](https://ja.vuejs.org/guide/components/slots.html)


# 子から親へデータを渡す方法一覧
## emit(+ v-on)
### 概要
- 子側で任意のイベントを発火させ、親側で定義したイベントリスナーに任意の値を渡す
### exposeとの比較
- 子がデータを渡すタイミングを制御する点で、exposeと異なる。詳しくはexpose参照
### v-modelとの関係
- `v-model`がこの機能を含有している。詳細はv-modelにて
### 用途
- 子コンポーネント側で制御しているタイミングで、子→親にデータを渡したい場合
    - たとえば、子が値を更新したときに、その新しい値を親に知らせる場合など
- (データ受け渡しを伴わずとも、単純に子側が定めるタイミングで親のイベントリスナーを呼びたいときも用いる)
### 公式リファレンス
[https://ja.vuejs.org/guide/components/events.html](https://ja.vuejs.org/guide/components/events.html)


## expose
### 概要
- 子コンポーネントのデータを親に公開する
    - Composion APIの場合は、`defineExpose`マクロを用いる
    - Options APIの場合は、デフォルトで、子コンポーネントのプロパティが親に公開されている
- 子が定義した関数なども公開できる
### emitとの比較
- emitは子からのメッセージ送信を起点に子から親にデータを渡すが、exposeは、マウント時に子から親にデータがすぐ渡される
= （マウント後なら）親が自由なタイミングで子から公開されているデータを利用できる
### 用途
- 親側が子のデータを、子からのイベント発火を経由せずに使用したい場合
- 使用例として、`Vuetify`(UIコンポーネントライブラリ)の各コンポーネント（API）において、`exposed`という項目がある
    - `v-otp-input`(ワンタイムパスワードなどを入力するUIコンポーネント)だと、このUIコンポーネントから呼び出し側（親）に対して`focus`が公開されており、これを親から使うことで、親からこのUIコンポーネントのフォーカスを制御できる
    - [VOtpInput#exposed](https://vuetifyjs.com/en/api/v-otp-input/#exposed)


### 公式リファレンス
[Composition API defineExpose](https://ja.vuejs.org/api/sfc-script-setup.html#defineexpose)
[Options API expose]( https://ja.vuejs.org/guide/essentials/template-refs.html#accessing-the-refs)

### 備考
- Composition APIにおける`defineExpose`のサンプルコードは、ググるといくつかqiita記事があるのでそちら参照(公式の例だと、子が公開したデータを親からどう使うのかがわかりづらい)

## scoped slot (slot props)
### 概要
- `slot`の一種
- slot同様、親から子のdom要素を指定できるが、その際に、子がそのdom要素（親側で定義）にデータを渡せる
    - この仕様により、実質、子→親にデータを渡す、ということが可能になっている
    - 子が定義した関数なども公開できる
### exposeとの比較
- exposeはデータが親コンポーネント全体に公開されるが、scoped slotに渡されたデータはdom要素内でのみ公開される。そのため、dom要素からしかアクセスできない
    - dom要素から、引数などの形でscript内で定義したデータに渡せば、scriptタグのなかでも使えるが、そういうひと手間が必要
### 用途
- 主に以下２つ
    - slotと同じく、親から子のdom要素を一部指定したい時。そのうえで、子のデータをそのdom要素で使いたい時(公式リファレンスだとこちらの用途の例が載っている)
    - expose同様、子のデータを親で使いたい時
        - ただし、親のdom要素からのみアクセス可能なため、exposeより用途が限られる
        - そのため、この用途なら、exposeでいい気がする？
- ２つ目の用途は、`vee-validate`(フォーム入力ライブラリ)の`Form`コンポーネントなどで使われる
    - [Form #slots](https://vee-validate.logaretm.com/v4/api/form/#slots)
    - Formコンポーネントを使う側（親、呼び出し側）のdom要素において、Form(子、ライブラリ側)で定義してあるデータ・メソッドを使うことができる
    - 上記リンク先の`resetForm`にあるサンプルコードがわかりやすい
### 公式リファレンス
[https://ja.vuejs.org/guide/components/slots.html#scoped-slots](https://ja.vuejs.org/guide/components/slots.html#scoped-slots)


# 親子間でデータを双方向に共有する方法一覧
## v-model
### 概要
- 親子間でデータを共有。親でのデータ変更も子側でのデータ変更も検知し、両方にデータ更新が同期される
- 親子コンポーネントにおける`v-model`は、`props`と`emit(+ v-on)`の組み合わせ
    - 親→子において、propsとしてデータを渡し、親の変更に応じて子のデータを同期する
    - 子→親においては、子が`update:modelValue`イベントをemitを用いて発火し、子で更新された新しい値を親に送ることで、親側のイベントリスナーがそのデータを用いて親のデータを更新し、子のデータと同期する
- ver3.4以降、Composition APIにおいては、`defineModel`というマクロができたため、親子コンポーネントにおける`v-model`が以上のような仕組みであることを意識しなくてもよくなった
### 状態管理ライブラリとの比較
- 状態管理ライブラリと異なり、データをバインドした親子コンポーネント間でのみデータが共有・同期される。詳しくは`状態管理ライブラリ`を参照
### 用途
- 親子間で、データを共有し、かつ同期したい場合
- 子でも親でも、共有データを変更する場合
### 公式リファレンス
[https://ja.vuejs.org/guide/components/v-model.html](https://ja.vuejs.org/guide/components/v-model.html)
## 状態管理ライブラリ
### 概要
- storeと呼ばれるコンテナ領域にデータ・状態を保存し、複数コンポーネント、ページ間でそのデータを共有する
- 代表例は、Pinia, Vuex (2024年4月現在のVue公式の推奨はPinia)
    - [Pinia](https://pinia.vuejs.org/)
    - [Vuex](https://vuex.vuejs.org/ja/guide/)
### v-modelとの比較
- v-modelは指定した親子コンポーネント間のみのデータ共有だが、状態管理ライブラリは、ありとあらゆるページ、コンポーネント間でデータを共有できる
    - それゆえ、安易に使うと、多数の経路からデータ変更がおきる、複雑なデータフローになりうる
### 用途
- 必ずしも親子関係にない、複数のページ・コンポーネント間でデータを共有したい場合