はじめに
仕事で使う事になったので1からVue.jsについて学んだ。
ちゃんと覚えておかないとまずそうな事を備忘録として1つ1つ残しておく。
$emitメソッドの正体 コンポーネント(子→親)の間のデータ受け渡しでやってる事
カスタムイベントを定義・発火させることができるメソッド1。
このメソッドを応用して、コンポーネント間(子→親)のデータの受け渡しを行う事ができる。
<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>
<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
(親)へデータの受け渡しを行っている。
上記の考え方としては、
-
child-component.vue
の方で$emitメソッド
でカスタムイベントmy-click
を定義し発火させる - その際に
$emitメソッド
では第二引数を取る事ができるので、第二引数に値this.totalNumber + 1
を渡すようにする -
parent-component.vue
では、v-onディレクティブでカスタムイベントmy-click
が発火した際に何らかの処理を行うように設定する事を考える - 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"
は以下のような実装に書き換える事もできる。
<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では親→子のコンポーネント間データ受け渡しをする(子が親に依存する)場合、子側で親から渡ってくるデータを変更しないようにオススメしている。
propsでオブジェクト・配列を渡す時は参照渡しになるのでより注意する
オブジェクト・配列を変数に格納すると参照渡しになるように、Vue.jsでも親→子でコンポーネント間データ渡しでオブジェクト・配列をpropsで渡すと参照渡しになってしまう。
この場合、子コンポーネントでpropsで渡ってきた値を書き換えた時、親のdataの値も書き換えられるという影響が出るので注意が必要。
※基本的には上記で述べたように、子側で親から渡ってくるデータを変更しないが原則なので、予期せぬバグを発生させないためにも子側で親から渡ってきたpropsを変更しないようにする。
$emitメソッドのカスタムイベント名はケバブケースで
カスタムイベントはHTML上5に書く事になるので、基本的にはHTML側の慣例にならいケバブケースで書くのがオススメ。
Vue.jsの勉強メモ一覧記事へのリンク
Vue.jsについて勉強した際に書いた勉強メモ記事のリンクを集約した記事。