Vue.js における Component 間のデータの受け渡しまとめ


Overview

Vue.js を使っていて、この Component 間の関係だとどうやって data 渡すんだっけな...?
となることが多いので方法はいろいろあると思いますが、自分がよくやるやり方をこの記事にまとめておきます。
(他にもこんなやり方あるよ!という知見ありましたら教えていただけますと幸いです )

親 → 子

親 Component から 子 Component へのデータの受け渡しは、
親 Component で data を v-bind して、子 Component で props で data を引き取ります。

Sample Code

  • Parent.vue ( v-bind で data を渡す)
<template>
  <div id="parent">
    <child v-bind:messageFromParent="this.message" />
  </div>
</template>

<script>
import Child from './Child'

export default {
  name: 'Parent',
  data () {
    return {
      message: 'Hello from Parent!'
    }
  },
  components: {
    child: Child
  },
}
</script>
  • Child.vue ( props で引き取る)
<template>
  <div id="child">
    {{ messageFromParent }}
  </div>
</template>

<script>
export default {
  name: 'Child',
  props: ['messageFromParent']
}
</script>

子 → 親

逆に、子 Component から 親 Component への data の受け渡しは
親 Component で v-on でイベントハンドラを定義しておき、子 Component では $emit で data を持たせて 親 Component で提起しているイベントを発火させます。

※ emit : 放つ、放射する

Sample Code

  • Parent.vue ( v-on でイベントハンドラを定義する)
<template>
  <div id="parent">
    <child v-on:sendMessage="receiveChildMessage" />
    {{ this.childMessage }}
  </div>
</template>

<script>
import Child from './Child'

export default {
  name: 'Parent',
  data () {
    return {
      childMessage: ''
    }
  },
  components: {
    child: Child
  },
  methods: {
    receiveChildMessage (message) {
      this.childMessage = message
    }
  }
}
</script>
  • Child.vue ( $emit で data を持たせつつ、親のイベントを発火する)
<template>
  <div id="child"/>
</template>

<script>
export default {
  name: 'Child',
  data () {
    return {
      message: 'Hello from Child!'
    }
  },
  created () {
    this.$emit('sendMessage', this.message)
  }
}
</script>

子A → 子B

これは実装の方針が人によって分かれそうなのですが、自分は 2つ以上の Component にまたがって data の受け渡しを行う場合 (子A → 親 → 子B 、 親 → 子 → 孫 など) Vuex を使うことが多いです。

※ Vuex についての詳細はこちらをご参照ください。
https://vuex.vuejs.org/ja/

Sample Code

  • store.ts
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

interface State {
  message: string
}

const initState: State = {
    message: ''
};

export default new Vuex.Store({
  state: initState,
  mutations: {
    setMessage(state, { message }: { message: string }) {
      state.message = message;
    }
  }
})
  • Parent.vue ( store を import する)
<template>
  <div id="parent">
    <child-a />
    <child-b />
  </div>
</template>

<script>
import store from './store'
import ChildA from './ChildA'
import ChildB from './ChildB'

export default {
  name: 'Parent',
  store,
  components: {
    child-a: ChildA,
    child-b: ChildB
  },
}
</script>
  • ChildA.vue ( Vuex に data を store する)
<template>
  <div id="child-a"/>
</template>

<script>
export default {
  name: 'ChildA',
  created () {
    this.$store.commit('setMessage', { message: 'Hello from ChildA!' });
  }
}
</script>
  • ChildB.vue ( Vuex から data を参照する)
<template>
  <div id="child-b"/>
  {{ this.message }}
</template>

<script>
export default {
  name: 'ChildB',
  data () {
    return {
      message: ''
    }
  },
  created () {
    this.message = this.$store.state.message
  }
}
</script>

まとめ

  • 親 Component → 子 Component

(親) v-bind:'value'(子) props['value']

  • 子 Component → 親 Component

(子) this.$emit('childHandler', value)(親) v-on:childHandler="parentHandler"

  • 子 Component A → 子 Component B

(子A) Vuex に data を store(子B) Vuex から data を参照