Vue.js

【Vue.js】syncでコンポーネントの間の通信

こんにちは!ともです(@_tomo_engineer)!

今回はVue.jsのコンポーネント間でデータ通信を行う方法について書きます。

基本的に親子間のデータ通信に『コンポーネント間の通信』という記事を書きました。

上記リンクで下記のような図を示しました。

今回はv-modelやsyncを利用したデータ通信について書きますが、基本は上記の図の処理となります。コンポーネント間の通信の基本を抑える事が大切です。

参考にしたものは以下です。

基礎から学ぶVue.js(書籍)

Vue.js:日本語ドキュメント

コンポーネントにv-model

v-modelはデータバインディングするディレクティブです。

v-modelをコンポーネントに付けると、どのデータをバインドするのでしょうか?

new Vue({
    el: '#app',
    components: {
        'my-component': myComponent,
    },
    data: {
        data: 'スライム',
    }
})
 # 展開前
<my-component v-model="data"></my-component>

上記の記法は次のように展開されます。

#展開後
<my-component v-bind:value="data"
                      v-on:input="data = $event">
</my-component>

つまり、

  • 属性として、dataオプションのdataを渡する
  • inputイベントが発火した場合、dataに$eventを格納する

という事は、子コンポーネントでは

  • props[‘value’]に親からのデータが格納される
  • $emit(‘input’, “子からのデータ”)で発火すると、親のデータに格納される

ですので、子のコンポーネントを次のように定義しました。

var myComponent = {
    template: "<div>{{value}}</div>",
    props: ['value']
}

これにより、親からのデータが表示されます。

子側で変更と同期をとる

次のようにコンポーネントを定義した場合、子のデータが変更されます。

この変更を親側のデータと同期を取るにはどうすれば良いのでしょうか。

# 親コンポーネント
<my-component v-model="data"></my-component>
# 子コンポーネント
var myComponent = {
    template: "<input v-model='value'>",
    props: ['value']
}

この場合

<my-component v-bind:value="data"
                      v-on:input="data = $event">
</my-component>

として展開される事を思い出します。

inputというイベントが発火したとき、子からのデータを親に格納してくれます。

よって、次のように子のデータ変化を監視し、変更があった場合イベントを発火させます。

var myComponent = {
    template: "<input v-model='syncData'>",
    props: ['value'],
    data: function(){
        return{
            data: '',
        }
    },
    mounted: function(){
        this.data = this.value
    },
    computed:{
        syncData:{
            get: function(){return this.data},
            set: function(val){this.$emit('input', val)}
        }
    }
}
  • マウントの段階で親からのデータ(props[‘value’])をdataに格納する
  • dataを算術プロパティで監視し、変更があれば$emitでイベントを発火する

属性として受け取り、$emitで発火させるという『コンポーネント間の通信』と同様の流れです。

v-modelで簡略化されているため、読み換える必要があります。

syncを利用する

v-modelでは単一のプロパティしか同期を取ることが出来ませんでした。

しかし、syncを利用すると複数のプロパティを同期することが出来ます。

syncを利用する前に、次のコンポーネントを見てください。ドキュメとから拝借しました。

<text-document
  v-bind:title="doc.title"
  v-on:update:title="doc.title = $event"
></text-document>

親のdoc.titleをtitleという属性に渡しています。

ですので、props[‘title’]で子側で受け取れます。また”update:title”というイベントを発火する事で親と同期を取ってくれる事も分かります。

これは.syncを利用する事で次にように書けます。

<text-document v-bind:title.sync="doc.title"></text-document>

かなりの省略ですね。.syncで複数のプロパティを同期を取ることが出来ます。

<text-document v-bind:title.sync="doc.title"
      v-bind:content.sync="doc.content">
</text-document>

こうすればテンプレート内でprops[‘title’, ‘content’]で受け取れますし、$emitでイベントを発火させれば同期を取ってくれる事が分かります。

まとめ

コンポーネント間の通信』に引き続き、親子間でデータを受け渡す方法について書きました。

『event up』と『props down』という基本は変わりませんでしたね。

まとめ
  • componentにv-modelを付けると、v-bind, v-onが展開される
  • syncを利用する事で複数のプロパティに対して同期を取る事が出来る。

アルバイト先ではcomponentにv-modelを付けて、どのデータと結び付けてるんだ?ってなりましたが、v-bind, v-onに展開されることが分かれば単純でした。

記述が簡潔になるので、どんどん使っていきたいです。