はじめに
Vue.js + Vuetify で親-子-孫の3世代で双方向バインディングの実装と挙動を確認します。
Vue.js でコンポーネント間のデータ受け渡しをする方法はいくつかありますが、今回は Vuex を使わない方法を紹介します。
とはいえ既に様々な例が偉大な先人達から紹介されているので、今回はデータ受け渡しからもう一歩踏み込んだ考察もしたいと思います。
尚、 Vuetify の使用は個人的な好みです。
改訂履歴
2020-06-10: typo 修正(firld → field)
環境
Vue.js: 2.6.10
Vuetify: 2.1.0
双方向バインディング(3世代)
親と子と孫に見立てたテキストエリアを使い、3世代で双方向バインディングが動くことを確認します。
孫
<template>
<v-textarea
label="Grandchild"
v-model="grandchildFieldComputed"
/>
</template>
<script>
export default {
props: {
grandchildField: null
},
computed: {
grandchildFieldComputed: {
get () {
return this.grandchildField
},
set (value) {
this.$emit('update:grandchildField', value)
}
}
}
}
</script>
解説
props: {
grandchildField: null
}
props の定義で子(孫から見た親)からのデータを受け取ります。
grandchildFieldComputed: {
get () {
return this.grandchildField
},
set (value) {
this.$emit('update:grandchildField', value)
}
}
'update:grandchildField'
でイベント発火。
子
<template>
<v-container>
<v-row>
<v-textarea
label="Child"
v-model="childFieldComputed"
/>
</v-row>
<v-row>
<GrandChild
v-bind:grandchild-field.sync="childFieldComputed"
/>
</v-row>
</v-container>
</template>
<script>
import GrandChild from './GrandChild'
export default {
components: {
GrandChild
},
props: {
childField: null
},
computed: {
childFieldComputed: {
get () {
return this.childField
},
set (value) {
this.$emit('update:childField', value)
}
}
}
}
</script>
解説
<GrandChild
v-bind:grandchild-field.sync="childFieldComputed"
/>
.sync 修飾子で孫(子から見た子)の grandchildField
(props とイベント)と紐付く。
props: {
childField: null
}
props の定義で親からのデータを受け取ります。
childFieldComputed: {
get () {
return this.childField
},
set (value) {
this.$emit('update:childField', value)
}
}
'update:childField'
でイベント発火。
親
<template>
<v-container>
<v-row>
<v-textarea
label="Parent"
v-model="fieldComputed"
/>
</v-row>
<v-row>
<Child
v-bind:child-field.sync="fieldComputed"
/>
</v-row>
</v-container>
</template>
<script>
import Child from './Child'
export default {
components: {
Child
},
data: () => ({
field: null
}),
computed: {
fieldComputed: {
get () {
return this.field
},
set (value) {
this.field =value
}
}
}
}
</script>
解説
<Child
v-bind:child-field.sync="fieldComputed"
/>
.sync 修飾子で子の childField
(props とイベント)と紐付く。
動作確認
親 (Parent) 、子 (Child) 、孫 (Grandchild) のテキストエリアでそれぞれ入力すると、3世代すべてのテキストエリアに値が連携されているのがわかります。
考察
以下、私見ですがデータ受け渡しの実装方法から考察します。
今回は使用していませんが Vuex は Vue.js で状態管理をするためのとても便利なライブラリです。
今回のようなコンポーネントを跨いだデータの受け渡しは Vuex を使っても実装できます。
ただ、状態管理をすべて Vuex で行うと別の問題も発生します。
コンポーネントは可能な限り再利用できる形で実装するべきですが、 そのためにはコンポーネントの独立性を保証する必要があります。
状態管理に Vuex を使用したコンポーネントは用途が Vuex に依存して独立性が低くなり、結果、そのコンポーネントは再利用しにくくなります。
例えば今回のケースでは、3世代間のデータ受け渡しで Vuex を使用すると3世代のコンポーネント全てが Vuex に依存しますが、 Vuex の使用を親に限定(=状態管理を親に委譲)すると、子と孫の独立性は保たれ、子と孫は再利用可能なコンポーネントとなります。
コンポーネントの再利用は、コード量が減る=バグが減ることにつながるため、実装時は常に意識しておきたいところですね。
まとめ
3世代での双方向バインディングの実装と挙動が確認できました。
また、コンポーネント間のデータ受け渡しをどう実装するかはコンポーネントの再利用性をどう実現していくかにつながることがわかりました(大げさ)。
今回、使用したコードはGitHubで公開しています。
https://github.com/ponko2bunbun/vuetify-parent_child_grandchild-sample