0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Vue.jsで孫 → 親コンポーネントへの値の受け渡しを楽に行う方法

Last updated at Posted at 2021-11-12

サマリ

Vue.jsで孫 から 親のコンポーネントへ値を渡す際、$emitを使いますが、中間のコンポーネントが増えてくるとその分emitを記述しないといけません。

この中間のコンポーネントでのemitの記述を減らすのに$listenersが使えますので、その紹介をします。

$emitと$listenersの比較

$emitを使った場合

// Parent.vue
<template>
	<Child @click="onClick" />
</template>

<script>
export default {
	components: {
		Child,
	},
	methods: {
		onClick(value) {
			alert(value);
		},
	},
}
<script>
// Child.vue
<template>
	<GrandChild @click="onClick" />
</template>

<script>
export default {
	components: {
		GrandChild
	},
	methods: {
		onClick(value) {
			this.$emit('click', value);
		}
	},
}
<script>
// GrandChild.vue
<template>
	<button @click="onClick">Hello World</button>
</template>

<script>
export default {
	methods: {
		onClick(event) {
			this.$emit('click', event.target.textContent);
		},
	},
}
<script>

※ 上記のコードはサンプルのため実際の案件コードとは異なります。

今回はバケツリレーの例のためご了承ください。

こんなのがさらに何階層も続くと、中間のコンポーネントを作成する際に以下の状態になってしまいます。

「また$emitを書かないといけないのか、、」

「あー、このコンポーネントにも$emit書かなきゃ、、、」

しまいには記載漏れしてる子コンポーネントがないか心配になってしまいます、。

(;・∀・)ダ、ダイジョウブ…?

そんな時に$listenersが使えます!

$listenersを使った場合

// Parent.vue
<template>
	<Child @click="onClick" />
</template>

<script>
export default {
	components: {
		Child,
	},
	methods: {
		onClick(value) {
			alert(value);
		},
	},
}
<script>
// Child.vue
<template>
	<GrandChild @click="$listeners['click']" />
</template>
// GrandChild.vue
<template>
	<button @click="onClick">Hello World</button>
</template>

<script>
export default {
	methods: {
		onClick(event) {
			this.$emit('click', event.target.textContent);
		},
	},
}
<script>

中間のコンポーネントでemitさせる際の記述が減りましたね!

どうですか!

楽ですよね!?

書き方の説明

@clickclickの部分は、子コンポーネントから$emitされたイベント名を記載し、

$listeners['']の中に記載するのは親で受け取りたいイベント名を記載します

<template>
	<GrandChild @click="$listeners['click']" />
</template>

まとめ

バケツリレーの際、中間に挟まれてるコンポーネントでは積極的に$listenersを使って、記述量を減らしていきましょう!

※ 実は$listenersはVue3で削除されてます、、!

※ ただ、Vue3はIE未対応のため、来年の6月まではこのwikiは役に立つ想定です、、

おまけ

また、この$listenersの挙動の面白いところがありまして、子コンポーネントにv-on="$listeners"と記載すると、子のイベントリスナを親側で直接参照できるんですね〜

// Parent.vue
<template>
	<Child @click="onClick" />
</template>

<script>
import Child from './Child.vue';

export default {
	components: {
		Child,
	},
	methods: {
		onClick(value) {
			console.log('くりっく');
		},
	},
}
<script>
// Child.vue
<template>
	<button v-on="$listener"></button>
</template>

ただ、上記の状態だと@click.nativeという記法に書き換えられますし、なんなら.nativeを使う際は子コンポーネントに$listenersを書く必要を感じられません!(.nativeは子コンポーネント のルート要素のイベントを指します)

ただし.nativeに書き換えたとして、以下の例だとどうでしょう?

// Parent.vue
<template>
	<Child @click.native="onClick" />
</template>
// Child.vue
<template>
	<div>
		<input type="radio">
	</div>
</template>

ルート要素はdivなので、divのクリックイベントを取得してしまいますね...

DOMの構造に依存してしまいます、、。

そこで$listenersを使えばいいのです!

// Parent.vue
<template>
	<Child @click="onClick" />
</template>
// Child.vue
<template>
	<div>
		<input type="radio" v-on="$listeners">
	</div>
</template>

これでDOMの構造に依存することなく、inputのイベントを参照できます!

※ 参照できるイベントはclickに限らず、mouseoverといった他のイベントも参照できます!

もっと学びたい人向け

この辺のもう少しお得な書き方が↓に載ってますので、興味のある方はご覧ください。
https://qiita.com/shinobu_shiva/items/e0c458aa6c1e683a9881

0
1
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
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?