vue3中組件事件和defineEmits示例代碼
1. 事件基礎(chǔ)
- 子組件有時(shí)候需要與父組件進(jìn)行交互,子組件需要通知父組件做一些事(比如:prop是單向數(shù)據(jù)量,子組件不應(yīng)該直接修改prop綁定的值,而是應(yīng)該拋出一個(gè)事件來(lái)通知父組件做出改變)
- 為了解決這個(gè)問(wèn)題,組件實(shí)例提供了一個(gè)自定義事件系統(tǒng),父組件可以通過(guò)v-on或 @ 來(lái)選擇性地監(jiān)聽(tīng)子組件上拋的事件,就像監(jiān)聽(tīng)原生 DOM 事件那樣,當(dāng)監(jiān)聽(tīng)到子組件上拋出的事件時(shí),就會(huì)執(zhí)行對(duì)應(yīng)事件綁定的監(jiān)聽(tīng)函數(shù)
- 子組件可以通過(guò)調(diào)用內(nèi)置的 $emit 方法,通過(guò)傳入事件名稱來(lái)拋出一個(gè)事件
- 子組件使用內(nèi)置的 $emit 方法中,可以使用 $event作為事件參數(shù)
- 我們可以通過(guò) emits 選項(xiàng)來(lái)聲明需要拋出的事件(emits聲明選項(xiàng)不是必須的)
- 這聲明了一個(gè)組件可能觸發(fā)的所有事件,還可以對(duì)事件的參數(shù)進(jìn)行驗(yàn)證
- 還可以讓 Vue 避免將它們作為原生事件監(jiān)聽(tīng)器隱式地應(yīng)用于子組件的根元素。
- 如果子組件中聲明了emits:[‘click’]選項(xiàng),然后在某個(gè)按鈕下使用了$emit(‘click’)去觸發(fā)click事件,則父組件中使用該子組件時(shí),使用@click=‘handleClick’去監(jiān)聽(tīng)click事件時(shí),不會(huì)認(rèn)為這是原生dom的點(diǎn)擊事件,只有當(dāng)子組件使用$emit(‘click’)觸發(fā)click事件時(shí),才會(huì)觸發(fā)hanleClick函數(shù)。如果不加emits:[‘click’]選項(xiàng),則只要點(diǎn)擊就會(huì)觸發(fā)handleClick事件。
示例

Blog.vue
<template>
<div :style="{ fontSize: postFontSize + 'em' }">
<BlogPost
v-for="post in posts"
:key="post.id"
:title="post.title"
@enlarge-text="postFontSize += 0.1"/>
</div>
</template>
<script >
import BlogPost from './BlogPost.vue'
export default {
name: 'Blog',
components: {
BlogPost
},
data() {
return {
posts: [
{ id: 1, title: 'My journey with Vue' },
{ id: 2, title: 'Blogging with Vue' },
{ id: 3, title: 'Why Vue is so fun' }
],
postFontSize: 1
}
}
}
</script>
<style lang="scss"></style>BlogPost.vue
<template>
<div class="blog-post">
<h4>{{ title }}</h4>
<button @click="$emit('enlarge-text')">Enlarge text</button>
</div>
</template>
<script>
export default {
name: 'BlogPost',
props: ['title'],
// 這個(gè)emits聲明選項(xiàng)不是必須的
emits: ['enlarge-text']
}
</script>
<style lang="scss"></style>2. 觸發(fā)與監(jiān)聽(tīng)事件
2.1 觸發(fā)事件
在組件的模板表達(dá)式中,可以直接使用 $emit 方法觸發(fā)自定義事件
$emit() 方法在組件實(shí)例上也同樣以 this.$emit() 的形式可用:
<templte>
<button @click="$emit('someEvent')">click me</button>
</template>
<script>
export default {
methods: {
submit() {
this.$emit('someEvent')
}
}
}
</script>2.2 監(jiān)聽(tīng)事件
- 父組件可以通過(guò) v-on (縮寫為 @) 來(lái)監(jiān)聽(tīng)事件
- 組件的事件監(jiān)聽(tīng)器也支持 .once 修飾符- 觸發(fā)事件時(shí)建議使用camelCase 形式 (駝峰命名,首字母小寫), 父組件監(jiān)聽(tīng)是可以使用kebab-case 形式 (短橫線分隔,推薦)
- 組件觸發(fā)的事件沒(méi)有冒泡機(jī)制,你 只能監(jiān)聽(tīng)直接子組件觸發(fā)的事件 。
- 平級(jí)組件或是跨越多層嵌套的組件間通信,應(yīng)使用一個(gè) 外部的事件總線
- 或者使用一個(gè) 全局狀態(tài)管理方案 。
<MyComponent @some-event="callback" /> <MyComponent @some-event.once="callback" />
3. 事件參數(shù)
- 有時(shí)候會(huì)需要在觸發(fā)事件時(shí)附帶一個(gè)特定的值(事件參數(shù))
- 可以寫一個(gè)內(nèi)聯(lián)的箭頭函數(shù)作為監(jiān)聽(tīng)器,此函數(shù)會(huì)接收到事件附帶的參數(shù)
- 也可以用一個(gè)組件方法來(lái)作為事件處理函數(shù),該方法也會(huì)接收到事件所傳遞的參數(shù)
- 所有傳入 $emit() 的額外參數(shù)都會(huì)被直接傳向監(jiān)聽(tīng)器。
- 舉例來(lái)說(shuō),$emit(‘foo’, 1, 2, 3) 觸發(fā)后,監(jiān)聽(tīng)器函數(shù)將會(huì)收到這三個(gè)參數(shù)值。
3.1 示例1

Blog.vue
<template>
{{ count }}
<BlogPost @increase-by="(n) => count += n" />
<BlogPost @increase-by="increaseCount" />
</template>
<script >
import BlogPost from './BlogPost.vue'
export default {
name: 'Blog',
components: {
BlogPost
},
data() {
return {
count: 0
}
},
methods: {
increaseCount(n,m,e) {
console.log(n,m,e); // 1, 2, PointerEvent {isTrusted: true, _vts: 1682821628803, pointerId: 1, width: 1, height: 1, …}
this.count += n
}
}
}
</script>
<style lang="scss"></style>BlogPost.vue
<template>
<div class="blog-post">
<button @click="$emit('increaseBy', 1, 2, $event)">
Increase by 1
</button>
</div>
</template>
<script>
export default {
name: 'BlogPost',
}
</script>
<style lang="scss"></style>3.2 示例2(defineEmits)
子組件可以觸發(fā)自定義事件,父組件使用該子組件時(shí),可以監(jiān)聽(tīng)該組件的事件,并為監(jiān)聽(tīng)的事件綁定事件處理函數(shù), 當(dāng)該子組件觸發(fā)了該事件時(shí),父組件對(duì)應(yīng)的事件處理函數(shù)就會(huì)執(zhí)行。

MyComponent.vue
<template>
<!-- 在組件的模板表達(dá)式中,可以直接使用 $emit 方法觸發(fā)自定義事件 -->
<!-- 可以通過(guò) v-on (縮寫為 @) 來(lái)監(jiān)聽(tīng)事件, 即:@click -->
<!-- 這里可以使用駝峰命名形式觸發(fā)的事件名稱, 而在父組件中使用短橫線命名形式監(jiān)聽(tīng) -->
<!-- 傳入3個(gè)參數(shù), 則在父組件中監(jiān)聽(tīng)函數(shù)中, 也應(yīng)至少有3個(gè)參數(shù),
否則按參數(shù)個(gè)數(shù)賦值,也可以在這里傳個(gè)對(duì)象過(guò)去,就只需要一個(gè)參數(shù)了,
并且可以使用$event, 將當(dāng)前事件傳過(guò)去 -->
<!-- 參數(shù)也可以使用模板中拿到的參數(shù), 比如v-for循環(huán)里拿到的每一項(xiàng)數(shù)據(jù) -->
<button v-on:click="$emit('doUpdate',$event,1,2)">觸發(fā)doUpdate事件</button> <br/>
<button v-on:click="$emit('refresh', {name:'zzhua',age:Math.floor(Math.random() * 20)})">觸發(fā)refresh事件</button>
子組件中person: {{ person }}<br/>
<!-- 可以使用事件修飾符, once表示該事件只會(huì)觸發(fā)1次 -->
<button v-on:click.once="handleClick()">觸發(fā)click事件</button>
</template>
<script lang="ts" setup>
import { ref,reactive,onUpdated } from 'vue'
/* 1. defineEmits() 宏不能在子函數(shù)中使用。它必須直接放置在 <script setup> 的頂級(jí)作用域下 */
/* 2. 顯式地使用了 setup 函數(shù)而不是 <script setup>,則事件需要通過(guò) emits 選項(xiàng)來(lái)定義,emit 函數(shù)也被暴露在 setup() 的上下文對(duì)象上 */
const emit = defineEmits(['doUpdate']) /* 可以不需要定義doUpdate, 但建議定義doUpdate, 以此來(lái)聲明此組件可以被觸發(fā)的事件 */
function handleClick () {
console.log('click');
emit('doUpdate',e,3,4) /* 使用emit,手動(dòng)觸發(fā)doUpdate事件 */
}
const props = defineProps(['person'])
onUpdated(()=>{
console.log('---組件渲染更新了---') /* 當(dāng)通過(guò)觸發(fā)子組件事件, 父組件監(jiān)聽(tīng)到該組件的該事件, 修改了person的ref值, 從而組件會(huì)更新 */
})
</script>
<style lang="scss">
button {
margin: 5px;
}
</style>Test.vue
<template>
<!-- 可以使用短橫線形式監(jiān)聽(tīng)當(dāng)前這個(gè)組件的事件,并指定事件處理函數(shù) -->
<my-component :person="person" v-on:do-update="handleUpdate" @refresh="handleRefresh" ></my-component><br/>
父組件中person: {{ person }}
</template>
<script lang="ts" setup>
import { ref,reactive,computed } from 'vue'
import MyComponent from './MyComponent.vue';
const person = ref({ name: 'demoName', age: NaN})
function handleUpdate(event,payload1,payload2) {
console.log(event); // PointerEvent {isTrusted: true, _vts: 1678062697219, pointerId: 1, width: 1, height: 1, …}
console.log(payload1, payload2);
}
function handleRefresh( p) {
console.log(p); // {name: 'zzhua', age: 18}
person.value = p
}
</script>
<style lang="scss">
</style>4. 聲明觸發(fā)的事件
- 組件可以顯式地通過(guò) emits 選項(xiàng)來(lái)聲明它要觸發(fā)的事件
- 這個(gè) emits 選項(xiàng)還支持對(duì)象語(yǔ)法,它允許我們對(duì)觸發(fā)事件的參數(shù)進(jìn)行驗(yàn)證
- 盡管事件聲明是可選的,我們還是推薦完整地聲明所有要觸發(fā)的事件。同時(shí),事件聲明能讓 Vue 更好地將事件和透?jìng)?attribute 作出區(qū)分,從而避免一些由第三方代碼觸發(fā)的自定義 DOM 事件所導(dǎo)致的邊界情況。
- 如果一個(gè)原生事件的名字 (例如 click) 被定義在 emits 選項(xiàng)中,則監(jiān)聽(tīng)器只會(huì)監(jiān)聽(tīng)組件觸發(fā)的 click 事件而不會(huì)再響應(yīng)原生的 click 事件。
5. 事件校驗(yàn)
- 和對(duì) props 添加類型校驗(yàn)的方式類似,所有觸發(fā)的事件 可以使用字符串 ,也 可以使用對(duì)象形式
- 要為事件添加校驗(yàn),那么事件 可以被賦值為一個(gè)函數(shù) ,接受的參數(shù)就是拋出事件時(shí)傳入 this.$emit 的內(nèi)容, 返回一個(gè)布爾值來(lái)表明事件是否合法
示例1

Blog.vue
<template>
{{ count }}
<BlogPost @submit="handleSubmit" />
</template>
<script >
import BlogPost from './BlogPost.vue'
export default {
name: 'Blog',
components: {
BlogPost
},
data() {
return {
count: 0
}
},
handleSubmit(payload) {
console.log(payload);
}
}
</script>
<style lang="scss"></style>BlogPost.vue
<template>
<div class="blog-post">
<button @click="$emit('submit', {password:'123456'})">
submit
</button>
</div>
</template>
<script>
export default {
name: 'BlogPost',
emits: {
// 沒(méi)有校驗(yàn)
click: null,
// 校驗(yàn) submit 事件
submit: ({ email, password }) => {
if (email && password) {
return true
} else {
console.warn('Invalid submit event payload!')
return false
}
}
},
methods: {
submitForm(email, password) {
this.$emit('submit', { email, password })
}
}
}
</script>
<style lang="scss"></style>示例2(defineEmits)
MyComponent.vue
<template>
<button @click="submitForm(123,456)">觸發(fā)submitForm事件</button>
</template>
<script lang="ts" setup>
import { ref, reactive } from 'vue'
const emit = defineEmits({
// 沒(méi)有校驗(yàn)
click: null,
// 校驗(yàn) submit 事件
submit: ({ email, password }) => {
if (email && password) {
console.log(email, password);
return true
} else {
console.warn('Invalid submit event payload!')
return false
}
}
})
function submitForm(email, password) {
emit('submit', { email, password })
}
</script>
<style lang="scss">
button {
margin: 5px;
}
</style>Test.vue
<template>
<MyComponent></MyComponent>
</template>
<script lang="ts" setup>
import { ref,reactive,computed } from 'vue'
import MyComponent from './MyComponent.vue';
</script>
<style lang="scss">
</style>總結(jié)
到此這篇關(guān)于vue3中組件事件和defineEmits的文章就介紹到這了,更多相關(guān)vue3組件事件和defineEmits內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
VUE配置proxy代理的開(kāi)發(fā)測(cè)試及生產(chǎn)環(huán)境
這篇文章主要為大家介紹了VUE配置proxy代理的開(kāi)發(fā)環(huán)境、測(cè)試環(huán)境、生產(chǎn)環(huán)境詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08
Vue組件更新數(shù)據(jù)v-model不生效的解決
vue el-table 動(dòng)態(tài)添加行與刪除行的實(shí)現(xiàn)
vue-cli-service不是內(nèi)部或外部命令,也不是可運(yùn)行的程序或批處理文件問(wèn)題
Vue實(shí)現(xiàn)上拉加載下一頁(yè)效果的示例代碼
Vue中input被賦值后,無(wú)法再修改編輯的問(wèn)題及解決
vue-cli3 DllPlugin 提取公用庫(kù)的方法

