vue3中組件事件和defineEmits示例代碼
1. 事件基礎(chǔ)
- 子組件有時候需要與父組件進(jìn)行交互,子組件需要通知父組件做一些事(比如:prop是單向數(shù)據(jù)量,子組件不應(yīng)該直接修改prop綁定的值,而是應(yīng)該拋出一個事件來通知父組件做出改變)
- 為了解決這個問題,組件實例提供了一個自定義事件系統(tǒng),父組件可以通過v-on或 @ 來選擇性地監(jiān)聽子組件上拋的事件,就像監(jiān)聽原生 DOM 事件那樣,當(dāng)監(jiān)聽到子組件上拋出的事件時,就會執(zhí)行對應(yīng)事件綁定的監(jiān)聽函數(shù)
- 子組件可以通過調(diào)用內(nèi)置的 $emit 方法,通過傳入事件名稱來拋出一個事件
- 子組件使用內(nèi)置的 $emit 方法中,可以使用 $event作為事件參數(shù)
- 我們可以通過 emits 選項來聲明需要拋出的事件(emits聲明選項不是必須的)
- 這聲明了一個組件可能觸發(fā)的所有事件,還可以對事件的參數(shù)進(jìn)行驗證
- 還可以讓 Vue 避免將它們作為原生事件監(jiān)聽器隱式地應(yīng)用于子組件的根元素。
- 如果子組件中聲明了emits:[‘click’]選項,然后在某個按鈕下使用了$emit(‘click’)去觸發(fā)click事件,則父組件中使用該子組件時,使用@click=‘handleClick’去監(jiān)聽click事件時,不會認(rèn)為這是原生dom的點擊事件,只有當(dāng)子組件使用$emit(‘click’)觸發(fā)click事件時,才會觸發(fā)hanleClick函數(shù)。如果不加emits:[‘click’]選項,則只要點擊就會觸發(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'], // 這個emits聲明選項不是必須的 emits: ['enlarge-text'] } </script> <style lang="scss"></style>
2. 觸發(fā)與監(jiān)聽事件
2.1 觸發(fā)事件
在組件的模板表達(dá)式中,可以直接使用 $emit 方法觸發(fā)自定義事件
$emit() 方法在組件實例上也同樣以 this.$emit() 的形式可用:
<templte> <button @click="$emit('someEvent')">click me</button> </template> <script> export default { methods: { submit() { this.$emit('someEvent') } } } </script>
2.2 監(jiān)聽事件
- 父組件可以通過 v-on (縮寫為 @) 來監(jiān)聽事件
- 組件的事件監(jiān)聽器也支持 .once 修飾符- 觸發(fā)事件時建議使用camelCase 形式 (駝峰命名,首字母小寫), 父組件監(jiān)聽是可以使用kebab-case 形式 (短橫線分隔,推薦)
- 組件觸發(fā)的事件沒有冒泡機制,你 只能監(jiān)聽直接子組件觸發(fā)的事件 。
- 平級組件或是跨越多層嵌套的組件間通信,應(yīng)使用一個 外部的事件總線
- 或者使用一個 全局狀態(tài)管理方案 。
<MyComponent @some-event="callback" /> <MyComponent @some-event.once="callback" />
3. 事件參數(shù)
- 有時候會需要在觸發(fā)事件時附帶一個特定的值(事件參數(shù))
- 可以寫一個內(nèi)聯(lián)的箭頭函數(shù)作為監(jiān)聽器,此函數(shù)會接收到事件附帶的參數(shù)
- 也可以用一個組件方法來作為事件處理函數(shù),該方法也會接收到事件所傳遞的參數(shù)
- 所有傳入 $emit() 的額外參數(shù)都會被直接傳向監(jiān)聽器。
- 舉例來說,$emit(‘foo’, 1, 2, 3) 觸發(fā)后,監(jiān)聽器函數(shù)將會收到這三個參數(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ā)自定義事件,父組件使用該子組件時,可以監(jiān)聽該組件的事件,并為監(jiān)聽的事件綁定事件處理函數(shù), 當(dāng)該子組件觸發(fā)了該事件時,父組件對應(yīng)的事件處理函數(shù)就會執(zhí)行。
MyComponent.vue
<template> <!-- 在組件的模板表達(dá)式中,可以直接使用 $emit 方法觸發(fā)自定義事件 --> <!-- 可以通過 v-on (縮寫為 @) 來監(jiān)聽事件, 即:@click --> <!-- 這里可以使用駝峰命名形式觸發(fā)的事件名稱, 而在父組件中使用短橫線命名形式監(jiān)聽 --> <!-- 傳入3個參數(shù), 則在父組件中監(jiān)聽函數(shù)中, 也應(yīng)至少有3個參數(shù), 否則按參數(shù)個數(shù)賦值,也可以在這里傳個對象過去,就只需要一個參數(shù)了, 并且可以使用$event, 將當(dāng)前事件傳過去 --> <!-- 參數(shù)也可以使用模板中拿到的參數(shù), 比如v-for循環(huán)里拿到的每一項數(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表示該事件只會觸發(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> 的頂級作用域下 */ /* 2. 顯式地使用了 setup 函數(shù)而不是 <script setup>,則事件需要通過 emits 選項來定義,emit 函數(shù)也被暴露在 setup() 的上下文對象上 */ const emit = defineEmits(['doUpdate']) /* 可以不需要定義doUpdate, 但建議定義doUpdate, 以此來聲明此組件可以被觸發(fā)的事件 */ function handleClick () { console.log('click'); emit('doUpdate',e,3,4) /* 使用emit,手動觸發(fā)doUpdate事件 */ } const props = defineProps(['person']) onUpdated(()=>{ console.log('---組件渲染更新了---') /* 當(dāng)通過觸發(fā)子組件事件, 父組件監(jiān)聽到該組件的該事件, 修改了person的ref值, 從而組件會更新 */ }) </script> <style lang="scss"> button { margin: 5px; } </style>
Test.vue
<template> <!-- 可以使用短橫線形式監(jiān)聽當(dāng)前這個組件的事件,并指定事件處理函數(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ā)的事件
- 組件可以顯式地通過 emits 選項來聲明它要觸發(fā)的事件
- 這個 emits 選項還支持對象語法,它允許我們對觸發(fā)事件的參數(shù)進(jìn)行驗證
- 盡管事件聲明是可選的,我們還是推薦完整地聲明所有要觸發(fā)的事件。同時,事件聲明能讓 Vue 更好地將事件和透傳 attribute 作出區(qū)分,從而避免一些由第三方代碼觸發(fā)的自定義 DOM 事件所導(dǎo)致的邊界情況。
- 如果一個原生事件的名字 (例如 click) 被定義在 emits 選項中,則監(jiān)聽器只會監(jiān)聽組件觸發(fā)的 click 事件而不會再響應(yīng)原生的 click 事件。
5. 事件校驗
- 和對 props 添加類型校驗的方式類似,所有觸發(fā)的事件 可以使用字符串 ,也 可以使用對象形式
- 要為事件添加校驗,那么事件 可以被賦值為一個函數(shù) ,接受的參數(shù)就是拋出事件時傳入 this.$emit 的內(nè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: { // 沒有校驗 click: null, // 校驗 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({ // 沒有校驗 click: null, // 校驗 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)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
VUE配置proxy代理的開發(fā)測試及生產(chǎn)環(huán)境
這篇文章主要為大家介紹了VUE配置proxy代理的開發(fā)環(huán)境、測試環(huán)境、生產(chǎn)環(huán)境詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08

Vue組件更新數(shù)據(jù)v-model不生效的解決

vue el-table 動態(tài)添加行與刪除行的實現(xiàn)

vue-cli-service不是內(nèi)部或外部命令,也不是可運行的程序或批處理文件問題