LoginSignup
3
4

More than 1 year has passed since last update.

【Vue.js】$emitメソッドの正体 コンポーネント(子→親)の間のデータ受け渡しでやってる事

Last updated at Posted at 2021-05-12

はじめに

仕事で使う事になったので1からVue.jsについて学んだ。
ちゃんと覚えておかないとまずそうな事を備忘録として1つ1つ残しておく。

$emitメソッドの正体 コンポーネント(子→親)の間のデータ受け渡しでやってる事

カスタムイベントを定義・発火させることができるメソッド1
このメソッドを応用して、コンポーネント間(子→親)のデータの受け渡しを行う事ができる。
ezgif.com-gif-maker (5).gif

parent-component.vue
<template>
  <div>
    <LikeHeader></LikeHeader>
    <h2>{{ number }}</h2>
    <LikeNumber :total-number="number" @my-click="number = $event"></LikeNumber>
  </div>
</template>

<script>
import LikeHeader from "./components/LikeHeader.vue";

export default {
  data() {
    return {
      number: 10,
    };
  },
  components: {
    LikeHeader,
  }
};
</script>

<style scoped>
div {
  border: 1px solid blue;
}
</style>
child-component.vue
<template>
  <div>
    <p>いいね({{ halfNumber }})</p>
    <button @click="increment">+1</button>
  </div>
</template>

<script>
export default {
  props: ["totalNumber"],
  computed: {
    halfNumber() {
      return this.totalNumber / 2;
    },
  },
  methods: {
    increment() {
      this.$emit("my-click", this.totalNumber + 1);
    },
  },
};
</script>

<style scoped>
div {
  border: 1px solid red;
}
</style>

ここでは、child-component.vue(子)からparent-component.vue(親)へデータの受け渡しを行っている。
上記の考え方としては、

  1. child-component.vueの方で$emitメソッドでカスタムイベントmy-clickを定義し発火させる
  2. その際に$emitメソッドでは第二引数を取る事ができるので、第二引数に値this.totalNumber + 1を渡すようにする
  3. parent-component.vueでは、v-onディレクティブでカスタムイベントmy-clickが発火した際に何らかの処理を行うように設定する事を考える
  4. v-onディレクティブの@hoge=""の""はJavaScriptを書く事ができるのでそこでparent-component.vueのdataプロパティの値を$eventの値に変更するようにする

というもの。$emitメソッドでカスタムイベントを発火させた場合、$eventには$emitメソッドの第二引数3の値が渡る仕様になっているので、上記のような考え方で実装する事で、$emitメソッドを応用して「子→親のコンポーネント間データ渡し」が実現できる。

※補足
$emitメソッドでは第二引数は任意の引数で省略可能4
つまり、$emitメソッドは純粋にカスタムイベントを定義・発火させるためのメソッドであり、オプションとして第二引数にデータを設定するとイベント発火時に$eventにデータを渡す事ができるというだけ
子→親のデータの受け渡しは$emitメソッドのカスタムイベント発火機能に便乗して実現されている。

@my-click="number = $event"の書き換え

@my-click="number = $event"は以下のような実装に書き換える事もできる。

sample.vue
<template>
  <!-- ・・・ -->
    <LikeNumber :total-number="number" @my-click="increaseNumber"></LikeNumber>
  <!-- ・・・ -->
</template>

<script>
  // ・・・
  },
  methods: {
    increaseNumber(value) {
      this.number = value;
    },
  },
  // ・・・
</script>

※v-onディレクティブのJavaScriptの部分がmethodsプロパティで定義された関数でかつその引数が$eventしかない場合は省略可能なので、上記の実装では@my-click="increaseNumber"と書いている。

propsのように簡単でないのはなぜ?

propsを用いると、親→子のコンポーネント間データ渡しが簡単にできるが、その逆(子→親)はなぜ$emitメソッドを使うなど面倒な事をしなければ実現できないのか?という事を思うだろう。
これはVue.jsが、単一方向のデータ渡ししかできないようにする事で、データフローを親→子へのみに限定し、データフローの複雑化を防止してシンプルになるようにしているため。
つまり、Vue.jsのコンポーネントでは、

  • 子は親(のdata)に依存する
  • 親は子(のdata、props)に依存しない

という設計がなされている。

※ちなみに、子コンポーネントで親から渡ってきたデータを変更すると

Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "totalNumber"(親コンポーネントが再レンダリングされるたびに値が上書きされるため、プロップを直接変更することは避けてください。 代わりに、小道具の値に基づいてデータまたは計算されたプロパティを使用してください。 変更されるプロップ: "totalNumber")

というエラーが出る。
Vue.jsでは親→子のコンポーネント間データ受け渡しをする(子が親に依存する)場合、子側で親から渡ってくるデータを変更しないようにオススメしている。
ezgif.com-gif-maker (6).gif

propsでオブジェクト・配列を渡す時は参照渡しになるのでより注意する

オブジェクト・配列を変数に格納すると参照渡しになるように、Vue.jsでも親→子でコンポーネント間データ渡しでオブジェクト・配列をpropsで渡すと参照渡しになってしまう。
この場合、子コンポーネントでpropsで渡ってきた値を書き換えた時、親のdataの値も書き換えられるという影響が出るので注意が必要。

※基本的には上記で述べたように、子側で親から渡ってくるデータを変更しないが原則なので、予期せぬバグを発生させないためにも子側で親から渡ってきたpropsを変更しないようにする。

$emitメソッドのカスタムイベント名はケバブケースで

カスタムイベントはHTML上5に書く事になるので、基本的にはHTML側の慣例にならいケバブケースで書くのがオススメ。

Vue.jsの勉強メモ一覧記事へのリンク

Vue.jsについて勉強した際に書いた勉強メモ記事のリンクを集約した記事。

  1. Vueインスタンスに標準で定義されているもの

  2. ソースコード全体を見たい場合はここ
    動画のソースコードは以下2

  3. interfaceを見ると$emit(event: string, ...args: any[]): this;となっている
    第二引数は...args: any[]で、これはarrayだが1つの要素しかない場合はその値が$eventに渡される

  4. 上記の例で言えば、単にthis.$emit("my-click");と書く事もできるという事

  5. 正確にはsingle file componentのtemplate

3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4