##1.はじめに
Vueのコンポーネントを学習する中で、propsとemitの理解が難しかったので、ここでまとめたいと思います。
記事の中に検証ソースをあげますが、webpack(ver:4.41.6)で動かしておりファイル構成は以下の通りです。
├─public
│ ├── bundle.js
│ └── index.html
├─src/
├── components
│ ├── emitChild.vue
│ ├── parent.vue
│ └── propsChild.vue
└── index.js
本記事ではpublic/index.htmlへ親コンポーネントを使用します。よってindex.htmlは以下の内容で固定です。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>VueComponent</title>
</head>
<body>
<div id="app">
<parent></parent>
</div>
<script src="/public/bundle.js"></script>
</body>
</html>
webpackの使い方は以下のQiita記事を参照しているため、こちらで確認してください。
webpack4入門
##2.propsについて
propsは親⇨子へ値を受け渡す際に使用するプロパティです。
親コンポート内に、v-bindで定義したプロパティの値を子へ受け渡します。
parentとpropsChildのファイルを編集します。
<template>
<div id="parent-area">
<!-- 子コンポーネントへ受け渡すcontext,numberを定義 -->
<propsChild :context="text" :number="num"></propsChild>
</div>
</template>
<script>
import propsChild from "./propsChild.vue";
export default {
components:{
emitChild:emitChild,
propsChild:propsChild,
},
data(){
return {
// 受け渡す値はdataプロパティで設定しないとエラーするので注意
text:'このテキストを子コンポーネントへ受け渡します',
num:1000
}
}
}
</script>
親コンポーネントで子コンポーネントを定義する際に、v-bindでpropsへ渡すプロパティを定義します。
私自身つまづいたところですが、propsプロパティ(context)へ渡すのは値は、vueで定義済みのプロパティ(text:dataで定義したプロパティ等)の必要があります。
直接値を埋め込むと定義されていないとエラーしますので注意しましょう。
<template>
<div id="props-area">
<h3>Props Area</h3>
<p>Content:{{context}}</p>
<p>Number:{{number}}</p>
</div>
</template>
<script>
export default {
// 親コンポーネントで定義したプロパティをpropsで定義して受け取る
props:{
context:String,
number:Number
},
}
</script>
propsの定義は上記のようにしていますが、他にも記述方法があり、型のバリデーションやオブジェクトの定義などが可能です。
上の例では、String型とNumberのバリデーションをしています。
公式ドキュメントに詳細があるので一度眺めるのがいいと思います。
##3.emitについて
emitは子⇨親へ値を受け渡す方法です。(ここは理解するのにちょっと苦戦しました。。。)
子⇨親のデータ受け渡しはイベントを利用して行います。
以下のような動的ページをemitを使い表示します。
EimitAreaで入力した値を親コンポーネントのResultAreaへ表示します。
parentとemitChildのファイルを編集します。
<template>
<div id="parent-area">
<!-- 「emit-event」は子コンポーネントと繋ぐ定義、「receive」は親コンポーネントで子コンポーネントからの値を取得するためのメソッド -->
<emitChild @emit-event="receive"></emitChild>
<div id="result">
<!-- 子コンポーネントからの値を表示するエリア -->
<p>Result Area</p>
<p>Result Text:{{result1}}</p>
<p>Result Number:{{result2}}</p>
</div>
</div>
</template>
<script>
import emitChild from './emitChild.vue';
export default {
components:{
emitChild:emitChild,
},
data(){
return{
result1:'',
result2:''
}
},
methods:{
receive(data1, data2){
//data1,data2は子コンポーネントから受け取った値
this.result1 = data1;
this.result2 = data2;
}
}
}
</script>
親と子のコンポーネントを繋ぐイベント(emit-event)を親の中の子コンポーネントで定義します。
また、methodsプロパティでは、子コンポーネントからの値を受け取ります。
data1,data2の引数は子コンポーネントから受け取った値になります。
<template>
<div id="emit-area">
<p>Emit Area</p>
<label>Text:<input type="text" v-model="context"></label>
<label>Number:<input type="number" v-model="number"></label>
<button @click="onClickEmit">送信</button>
</div>
</template>
<script>
export default {
data(){
return{
context:'',
number:0,
}
},
methods:{
onClickEmit(){
//親コンポーネントで定義した「emit-event」を第一引数へ定義し、以降の引数は親コンポーネントへ渡す値を定義
this.$emit('emit-event', this.context, this.number);
}
}
}
</script>
ここでは子コンポーネントのmethodsで親コンポーネントへ値を渡します。なので、必ずしもclickイベントで定義しなくてはいけないわけではありません。
$emitの第一引数には、親コンポーネントで定義したイベントを入れて、以降の引数には親コンポーネントへ渡す値を入れてあげます。
なので、親コンポーネントのmehods>receiveイベントの引数(data1、data2)はここのthis.content,this.number)となります。
##4.コンポーネント間の値の受け渡しの例
以下のようなコンポーネント間で値の受け渡しをやってみます。
emitChild ⇨ parent ⇨ propsChild
※2,3章で使ったemitchildとpropsChildのソース内容は上書きして以降は使っていきます。
<template>
<div id="parent-area">
<!-- <emitChild v-model="content"></emitChild> -->
<emitChild :value="content" @input="content = $event.target.value"></emitChild>
<propsChild :text="content"></propsChild>
</div>
</template>
<script>
import emitChild from './emitChild.vue';
import propsChild from './propsChild.vue';
export default {
components:{
emitChild:emitChild,
propsChild:propsChild,
},
data() {
return {
content:'',
}
},
}
</script>
```
emitChildで子コンポートからの値を取得しています。
また、この書き方はv-modelでも置換可能です。理由とすると、v-modelを解体すると以下の書き方ためです。([公式リファレンス参照](https://jp.vuejs.org/v2/guide/components.html#%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E3%81%A7-v-model-%E3%82%92%E4%BD%BF%E3%81%86))
```
<input
v-bind:value="searchText"
v-on:input="searchText = $event.target.value"
>
```
この$eventはinputタグのインスタンスなため、[inputタグのメソッド](https://developer.mozilla.org/ja/docs/Web/API/HTMLElement/input_event)が利用可能になります。
``````src/components/emitChild.vue
<template>
<div id="emit-area">
<h3>Emit Area</h3>
<textarea :value='context' @input="OnInput"></textarea>
</div>
</template>
<script>
export default {
methods: {
OnInput($event){
this.context = $event.target.value;
// 引数へinputイベントを投げます
this.$emit('input', $event);
}
},
data() {
return {
context:''
}
},
}
</script>
```
```src/components/propsChild.vue
<template>
<div id="props-area">
<h3>Props Area</h3>
<p>{{text}}</p>
</div>
</template>
<script>
export default {
props:{
text:String,
},
}
</script>
```
emitChildとpropsChildのemit、propsの使い方は前章とほぼ同様です。
##5.まとめ
vueのpropsとemitが自分的にわかりにくかったのでまとめてみました。
値の受け渡しによって、コンポーネントの部品としての可用性が上がり、細分化が可能だと思うので積極的に使っていこうと思います。
また記事内で間違いがあればご指摘お願いいたします。
##6.参照
[Vue.jsコンポーネント入門 (5) コンポーネント間のコミュニケーション](https://www.hypertextcandy.com/vuejs-components-introduction-communication-between-components)
[【Vue.js】 コンポーネント間の通信について解説](https://se-tomo.com/2018/11/03/vue-js-%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E9%96%93%E3%81%AE%E9%80%9A%E4%BF%A1/)