Vue3 子傳父全解析實(shí)戰(zhàn)
在 Vue3 開發(fā)中,組件通信是繞不開的核心場景,而子傳父作為最基礎(chǔ)、最常用的通信方式之一,更是新手入門必掌握的知識(shí)點(diǎn)。不同于 Vue2 的 $emit 寫法,Vue3 組合式 API(<script setup>)簡化了子傳父的實(shí)現(xiàn)邏輯,但也有不少細(xì)節(jié)和進(jìn)階技巧需要注意。
本文將拋開 TypeScript,用最通俗的語言 + 可直接復(fù)制的實(shí)戰(zhàn)代碼,從基礎(chǔ)用法、進(jìn)階技巧、常見場景到避坑指南,全方位講解 Vue3 子傳父,新手看完就能上手,老手也能查漏補(bǔ)缺。
一、核心原理:子組件觸發(fā)事件,父組件監(jiān)聽事件
Vue3 子傳父的核心邏輯和 Vue2 一致:子組件通過觸發(fā)自定義事件,將數(shù)據(jù)傳遞給父組件;父組件通過監(jiān)聽該自定義事件,接收子組件傳遞的數(shù)據(jù)。
關(guān)鍵區(qū)別在于:Vue3 <script setup> 中,無需通過 this.$emit 觸發(fā)事件,而是通過 defineEmits 聲明事件后,直接調(diào)用 emit 函數(shù)即可,語法更簡潔、更直觀。
先記住核心流程,再看具體實(shí)現(xiàn):
- 子組件:用 defineEmits 聲明要觸發(fā)的自定義事件(可選但推薦);
- 子組件:在需要傳值的地方(如點(diǎn)擊事件、接口回調(diào)),調(diào)用 emit('事件名', 要傳遞的數(shù)據(jù));
- 父組件:在使用子組件的地方,通過 @事件名="處理函數(shù)" 監(jiān)聽事件;
- 父組件:在處理函數(shù)中,接收子組件傳遞的數(shù)據(jù)并使用。
二、基礎(chǔ)用法:最簡潔的子傳父實(shí)現(xiàn)(必學(xué))
我們用一個(gè)「子組件輸入內(nèi)容,父組件實(shí)時(shí)顯示」的簡單案例,講解基礎(chǔ)用法,代碼可直接復(fù)制到項(xiàng)目中運(yùn)行。
1. 子組件(Child.vue):聲明事件 + 觸發(fā)事件
<template>
<div class="child">
<h4>我是子組件</h4>
<!-- 輸入框輸入內(nèi)容,觸發(fā)input事件,傳遞輸入值 -->
<input
type="text"
v-model="childInput"
@input="handleInput"
placeholder="請輸入要傳遞給父組件的內(nèi)容"
/>
<!-- 按鈕點(diǎn)擊,傳遞固定數(shù)據(jù) -->
<button @click="handleClick" style="margin-top: 10px;">
點(diǎn)擊向父組件傳值
</button>
</div>
</template>
<script setup>
// 1. 聲明要觸發(fā)的自定義事件(數(shù)組形式,元素是事件名)
// 可選,但推薦聲明:增強(qiáng)代碼可讀性,IDE會(huì)有語法提示,避免拼寫錯(cuò)誤
const emit = defineEmits(['inputChange', 'btnClick'])
// 子組件內(nèi)部數(shù)據(jù)
const childInput = ref('')
// 輸入框變化時(shí),觸發(fā)事件并傳遞輸入值
const handleInput = () => {
// 2. 觸發(fā)事件:第一個(gè)參數(shù)是事件名,第二個(gè)參數(shù)是要傳遞的數(shù)據(jù)(可選,可多個(gè))
emit('inputChange', childInput.value)
}
// 按鈕點(diǎn)擊時(shí),觸發(fā)事件并傳遞固定對(duì)象
const handleClick = () => {
emit('btnClick', {
name: '子組件',
msg: '這是子組件通過點(diǎn)擊按鈕傳遞的數(shù)據(jù)'
})
}
</script>
2. 父組件(Parent.vue):監(jiān)聽事件 + 接收數(shù)據(jù)
<template>
<div class="parent">
<h3>我是父組件</h3>
<p>子組件輸入的內(nèi)容:{{ parentMsg }}</p>
<p>子組件點(diǎn)擊傳遞的數(shù)據(jù):{{ parentData }}</p>
<!-- 3. 監(jiān)聽子組件聲明的自定義事件,綁定處理函數(shù) -->
<Child
@inputChange="handleInputChange"
@btnClick="handleBtnClick"
/>
</div>
</template>
<script setup>
// 引入子組件
import Child from './Child.vue'
import { ref, reactive } from 'vue'
// 父組件接收數(shù)據(jù)的容器
const parentMsg = ref('')
const parentData = reactive({
name: '',
msg: ''
})
// 4. 處理子組件觸發(fā)的inputChange事件,接收傳遞的數(shù)據(jù)
const handleInputChange = (val) => {
// val 就是子組件emit傳遞過來的值(childInput.value)
parentMsg.value = val
}
// 處理子組件觸發(fā)的btnClick事件,接收傳遞的對(duì)象
const handleBtnClick = (data) => {
// data 是子組件傳遞的對(duì)象,直接解構(gòu)或賦值即可
parentData.name = data.name
parentData.msg = data.msg
}
</script>
3. 核心細(xì)節(jié)說明
defineEmits是 Vue3 內(nèi)置的宏,無需導(dǎo)入,可直接使用;- emit 函數(shù)的第一個(gè)參數(shù)必須和
defineEmits中聲明的事件名一致(大小寫敏感),否則父組件無法監(jiān)聽到; - emit 可傳遞多個(gè)參數(shù),比如
emit('event', val1, val2),父組件處理函數(shù)可對(duì)應(yīng)接收(val1, val2) => {}; - 父組件監(jiān)聽事件時(shí),可使用
@事件名(簡寫)或v-on:事件名(完整寫法),效果一致。
三、進(jìn)階用法:優(yōu)化子傳父的體驗(yàn)(實(shí)戰(zhàn)常用)
基礎(chǔ)用法能滿足簡單場景,但在實(shí)際開發(fā)中,我們還會(huì)遇到「事件校驗(yàn)」「雙向綁定」「事件命名規(guī)范」等需求,這部分進(jìn)階技巧能讓你的代碼更規(guī)范、更健壯。
1. 事件校驗(yàn):限制子組件傳遞的數(shù)據(jù)類型
通過 defineEmits 的對(duì)象形式,可對(duì)事件傳遞的數(shù)據(jù)進(jìn)行類型校驗(yàn),避免子組件傳遞錯(cuò)誤類型的數(shù)據(jù),提升代碼可靠性(類似 props 校驗(yàn))。
<script setup>
// 對(duì)象形式聲明事件,key是事件名,value是校驗(yàn)函數(shù)(參數(shù)是子組件傳遞的數(shù)據(jù),返回boolean)
const emit = defineEmits({
// 校驗(yàn)inputChange事件傳遞的數(shù)據(jù)必須是字符串
inputChange: (val) => {
return typeof val === 'string'
},
// 校驗(yàn)btnClick事件傳遞的數(shù)據(jù)必須是對(duì)象,且包含name和msg屬性
btnClick: (data) => {
return typeof data === 'object' && 'name' in data && 'msg' in data
}
})
// 若傳遞的數(shù)據(jù)不符合校驗(yàn),控制臺(tái)會(huì)報(bào)警告(不影響代碼運(yùn)行,僅提示)
const handleInput = () => {
emit('inputChange', 123) // 傳遞數(shù)字,不符合校驗(yàn),控制臺(tái)報(bào)警告
}
</script>
2. 雙向綁定:v-model 簡化子傳父(高頻場景)
很多時(shí)候,子傳父是為了「修改父組件的數(shù)據(jù)」,比如表單組件、開關(guān)組件,這時(shí)可使用 v-model 簡化代碼,實(shí)現(xiàn)父子組件雙向綁定,無需手動(dòng)聲明事件和處理函數(shù)。
Vue3 中,v-model 本質(zhì)是「語法糖」,等價(jià)于 :modelValue="xxx" @update:modelValue="xxx = $event"。
優(yōu)化案例:子組件開關(guān),父組件顯示狀態(tài)
<!-- 子組件(Child.vue) -->
<template>
<div class="child">
<h4>子組件開關(guān)</h4>
<button @click="handleSwitch">
{{ isOpen ? '關(guān)閉' : '打開' }}
</button>
</div>
</template>
<script setup>
// 1. 接收父組件通過v-model傳遞的modelValue
const props = defineProps(['modelValue'])
// 2. 聲明update:modelValue事件(固定命名,不可修改)
const emit = defineEmits(['update:modelValue'])
// 子組件內(nèi)部使用父組件傳遞的值
const isOpen = computed(() => props.modelValue)
// 開關(guān)切換,觸發(fā)事件,修改父組件數(shù)據(jù)
const handleSwitch = () => {
emit('update:modelValue', !isOpen.value)
}
</script>
<!-- 父組件(Parent.vue) -->
<template>
<div class="parent">
<h3>父組件:{{ isSwitchOpen ? '開關(guān)已打開' : '開關(guān)已關(guān)閉' }}</h3>
<!-- 直接使用v-model,無需手動(dòng)監(jiān)聽事件 -->
<Child v-model="isSwitchOpen" />
</div>
</template>
<script setup>
import Child from './Child.vue'
import { ref } from 'vue'
const isSwitchOpen = ref(false)
</script>
擴(kuò)展:多個(gè) v-model 雙向綁定
Vue3 支持給同一個(gè)子組件綁定多個(gè) v-model,只需給 v-model 加后綴,對(duì)應(yīng)子組件的props 和 emit 即可。
<!-- 父組件 -->
<Child
v-model:name="parentName"
v-model:age="parentAge"
/>
<!-- 子組件 -->
<script setup>
// 接收多個(gè)v-model傳遞的props
const props = defineProps(['name', 'age'])
// 聲明對(duì)應(yīng)的update事件
const emit = defineEmits(['update:name', 'update:age'])
// 觸發(fā)事件修改父組件數(shù)據(jù)
emit('update:name', '新名字')
emit('update:age', 25)
</script>
3. 事件命名規(guī)范:提升代碼可讀性
在實(shí)際開發(fā)中,遵循統(tǒng)一的事件命名規(guī)范,能讓團(tuán)隊(duì)協(xié)作更高效,推薦以下規(guī)范:
- 事件名采用「kebab-case 短橫線命名」(和 HTML 事件命名一致),比如
input-change而非inputChange; - 事件名要語義化,體現(xiàn)事件的用途,比如
form-submit(表單提交)、delete-click(刪除點(diǎn)擊); - 雙向綁定的事件固定為
update:xxx,xxx 對(duì)應(yīng) props 名,比如update:name、update:visible。
四、實(shí)戰(zhàn)場景:子傳父的常見應(yīng)用
結(jié)合實(shí)際開發(fā)中的高頻場景,給大家補(bǔ)充 3 個(gè)常用案例,覆蓋大部分子傳父需求。
場景1:子組件表單提交,父組件接收表單數(shù)據(jù)
<!-- 子組件(FormChild.vue) -->
<template>
<div class="form-child">
<input v-model="form.name" placeholder="請輸入姓名" />
<input v-model="form.age" type="number" placeholder="請輸入年齡" />
<button @click="handleSubmit">提交表單</button>
</div>
</template>
<script setup>
import { reactive } from 'vue'
const emit = defineEmits(['form-submit'])
const form = reactive({
name: '',
age: ''
})
const handleSubmit = () => {
// 表單校驗(yàn)(簡化)
if (!form.name || !form.age) return alert('請?zhí)顚懲暾畔?)
// 提交表單數(shù)據(jù)給父組件
emit('form-submit', form)
// 提交后重置表單
form.name = ''
form.age = ''
}
</script>
場景2:子組件關(guān)閉彈窗,父組件控制彈窗顯示/隱藏
<!-- 子組件(ModalChild.vue) -->
<template>
<div class="modal" v-if="visible">
<div class="modal-content">
<h4>子組件彈窗</h4>
<button @click="handleClose">關(guān)閉彈窗</button>
</div>
</div>
</template>
<script setup>
const props = defineProps(['visible'])
const emit = defineEmits(['close-modal'])
const handleClose = () => {
// 觸發(fā)關(guān)閉事件,通知父組件隱藏彈窗
emit('close-modal')
}
</script>
場景3:子組件列表刪除,父組件更新列表
<!-- 子組件(ListChild.vue) -->
<template>
<div class="list-child">
<div v-for="item in list" :key="item.id">
{{ item.name }}
<button @click="handleDelete(item.id)">刪除</button>
</div>
</div>
</template>
<script setup>
const props = defineProps(['list'])
const emit = defineEmits(['delete-item'])
const handleDelete = (id) => {
// 傳遞要?jiǎng)h除的id給父組件,由父組件更新列表
emit('delete-item', id)
}
</script>
五、常見坑點(diǎn)避坑指南(新手必看)
很多新手在寫子傳父時(shí),會(huì)遇到「父組件監(jiān)聽不到事件」「數(shù)據(jù)傳遞失敗」等問題,以下是最常見的 4 個(gè)坑點(diǎn),幫你快速避坑。
坑點(diǎn)1:事件名大小寫不一致
子組件 emit('inputChange'),父組件 @inputchange="handle"(小寫),會(huì)導(dǎo)致父組件監(jiān)聽不到事件。
解決方案:統(tǒng)一采用 kebab-case 命名,子組件 emit('input-change'),父組件 @input-change="handle"。
坑點(diǎn)2:忘記聲明事件(defineEmits)
子組件直接調(diào)用 emit('event'),未用 defineEmits 聲明事件,雖然開發(fā)環(huán)境可能不報(bào)錯(cuò),但生產(chǎn)環(huán)境可能出現(xiàn)異常,且 IDE 無提示。
解決方案:無論事件是否需要校驗(yàn),都用 defineEmits 聲明(數(shù)組形式即可)。
坑點(diǎn)3:傳遞復(fù)雜數(shù)據(jù)(對(duì)象/數(shù)組)時(shí),父組件修改后影響子組件
子組件傳遞對(duì)象/數(shù)組給父組件,父組件直接修改該數(shù)據(jù),會(huì)影響子組件(因?yàn)橐妙愋蛡鬟f的是地址)。
解決方案:父組件接收數(shù)據(jù)后,用 JSON.parse(JSON.stringify(data)) 深拷貝,或用 reactive + toRaw 處理,避免直接修改原始數(shù)據(jù)。
坑點(diǎn)4:v-model 雙向綁定時(shí)報(bào)錯(cuò),提示「modelValue 未定義」
原因:子組件未接收 modelValue props,或未聲明 update:modelValue 事件。
解決方案:確保子組件 defineProps(['modelValue']) 和 defineEmits(['update:modelValue']) 都聲明。
六、總結(jié):子傳父核心要點(diǎn)回顧
Vue3 子傳父的核心就是「事件觸發(fā) + 事件監(jiān)聽」,記住以下 3 個(gè)核心要點(diǎn),就能應(yīng)對(duì)所有場景:
- 基礎(chǔ)寫法:
defineEmits聲明事件 →emit觸發(fā)事件 → 父組件@事件名監(jiān)聽; - 進(jìn)階優(yōu)化:事件校驗(yàn)提升可靠性,
v-model簡化雙向綁定,遵循 kebab-case 命名規(guī)范; - 避坑關(guān)鍵:事件名大小寫一致、必聲明事件、復(fù)雜數(shù)據(jù)深拷貝、v-model 對(duì)應(yīng) props 和 emit 命名正確。
子傳父是 Vue3 組件通信中最基礎(chǔ)的方式,掌握它之后,再學(xué)習(xí)父傳子(props)、跨層級(jí)通信(provide/inject)、全局通信(Pinia)會(huì)更輕松。
到此這篇關(guān)于Vue3 子傳父全解析實(shí)戰(zhàn)的文章就介紹到這了,更多相關(guān)Vue3 子傳父內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
VUEJS實(shí)戰(zhàn)之修復(fù)錯(cuò)誤并且美化時(shí)間(2)
這篇文章主要為大家詳細(xì)介紹了VUEJS實(shí)戰(zhàn)之修復(fù)錯(cuò)誤并且美化時(shí)間,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-06-06
elementui實(shí)現(xiàn)表格自定義排序的示例代碼
本文主要介紹了elementui實(shí)現(xiàn)表格自定義排序的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07
el-select 點(diǎn)擊按鈕滾動(dòng)到選擇框頂部的實(shí)現(xiàn)代碼
本文通過實(shí)例代碼給大家分享el-select 點(diǎn)擊按鈕滾動(dòng)到選擇框頂部效果,主要代碼是在visibleChange在這個(gè)popper里面找到.el-select-dropdown__list,感興趣的朋友跟隨小編一起看看吧2024-05-05
淺析webpack-bundle-analyzer在vue-cli3中的使用
這篇文章主要介紹了webpack-bundle-analyzer在vue-cli3中的使用,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-10-10
Vue實(shí)現(xiàn)倒計(jì)時(shí)小功能
這篇文章主要為大家詳細(xì)介紹了Vue實(shí)現(xiàn)倒計(jì)時(shí)小功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09

