Vue3實(shí)現(xiàn)組件二次封裝的小技巧分享
雙向數(shù)據(jù)綁定
我們以 input 組件作為例子
雙向數(shù)據(jù)綁定的原理及實(shí)現(xiàn)想必大家已經(jīng)爛熟于心了直接看官網(wǎng)吧!
子組件接受一個(gè) modelValue 的 prop, 通過(guò) emit 觸發(fā) update:modelValue 事件完成數(shù)據(jù)的更新
父組件直接 v-model="xxxx"
嫌麻煩官方還提供了 defineModel()
用于簡(jiǎn)化上邊的步驟
向子組件傳遞插槽
我們以 input 組件作為例子,創(chuàng)建一個(gè) WrapInput.vue
組件
未學(xué)習(xí)之前
WrapInput.vue
常規(guī)的做法,遍歷 $slots
來(lái)實(shí)現(xiàn)
<script setup lang="ts"> const model = defineModel() </script> <template> <el-input v-model="model" placeholder="Please input" > <template v-for="(_, slot) in $slots" :key="solt" v-slot:[slot]="slotProps"> <slot :name="slot" v-bind="slotProps"></slot> </template> </el-input> </template> <style lang='scss' scoped></style>
在 app.vue
中引入并傳遞 prepend、append 插槽
<script setup lang="ts"> import { ref } from "vue"; import WrapInput from "./components/WrapInput.vue"; const inputText = ref('') </script> <template> <WrapInput v-model="inputText"> <template #prepend>Http://</template> <template #append>.com</template> </WrapInput> <div> {{inputText}} </div> </template> <style scoped> </style>
正確渲染了插槽
學(xué)習(xí)之后
讓我們來(lái)修改下 WrapInput.vue
<script setup lang="ts"> import { h } from "vue"; import { ElInput } from "element-plus"; const model = defineModel() </script> <template> <component :is="h(ElInput, $attrs, $slots)" v-model="model"></component> </template> <style lang='scss' scoped></style>
app.vue
的代碼不做任何修改
插槽正常傳遞、數(shù)據(jù)更新正常,看到這種寫法的時(shí)候有點(diǎn)震驚的
component 組件為什么可以傳入 h 函數(shù)
看下 h 函數(shù)的文檔, h(ElInput, $attrs, $slots)
是創(chuàng)建了一個(gè)虛擬 dom 節(jié)點(diǎn)
而 component
組件的 is 屬性則可以接收
- 被注冊(cè)的組件名
- 導(dǎo)入的組件對(duì)象
- 一個(gè)返回上述值之一的函數(shù)
當(dāng) component
組件的 is 屬性接收到一個(gè)函數(shù)時(shí),Vue 會(huì)調(diào)用這個(gè)函數(shù)并使用其返回值作為要渲染的組件。
在這種情況下,h(ElInput, $attrs, $slots)
會(huì)立即執(zhí)行并返回一個(gè) VNode,這個(gè) VNode 描述了如何渲染 ElInput
組件。
獲取子組件的 ref
未學(xué)習(xí)之前
之前的自己的寫法有點(diǎn)蠢的具體的做法是在子組件創(chuàng)建一個(gè) getRef 的函數(shù)把 ref 暴露出去,父組件調(diào)用 getRef 方法后在執(zhí)行子組件方法的調(diào)用,大概是下邊這樣
WrapInput1.vue
<script setup lang="ts"> import { h, ref} from "vue"; import { ElInput } from "element-plus"; const model = defineModel() const inputRef = ref() function getRef () { return inputRef.value } defineExpose({ getRef }) </script> <template> <component ref="inputRef" :is="h(ElInput, $attrs, $slots)" v-model="model"></component> </template> <style lang='scss' scoped></style>
學(xué)習(xí)之后
WrapInput.vue
<script setup lang="ts"> import { h, ref } from "vue"; import { ElInput } from "element-plus"; const model = defineModel() const inputRef = ref() defineExpose(new Proxy({}, { get(_target, prop) { return inputRef.value?.[prop] }, has (_target, prop) { return prop in inputRef.value } })) </script> <template> <component :is="h(ElInput, $attrs, $slots)" v-model="model" ref="inputRef"></component> </template> <style lang='scss' scoped></style>
使用 Proxy
代理暴露出去的方法,是有點(diǎn)震驚的,還能這么寫
App.vue
<script setup lang="ts"> import { ref } from "vue"; import WrapInput from "./components/WrapInput.vue"; const inputText = ref('') const prependSlotText = ref('Http://') const appendSlotText = ref('.com') function updateSlotInfo (){ prependSlotText.value = 'https://' appendSlotText.value = `${new Date().getTime()}` } const wrapInputRef = ref() function setWrapInputFocus () { wrapInputRef.value?.focus() } </script> <template> <WrapInput v-model="inputText" ref="wrapInputRef"> <template #prepend>{{ prependSlotText }}</template> <template #append>{{ appendSlotText }}</template> </WrapInput> <div style="margin: 20px 0;"> {{inputText}} </div> <el-button type="primary" @click="updateSlotInfo">更新插槽內(nèi)容</el-button> <el-button type="primary" @click="setWrapInputFocus">set input focus</el-button> </template> <style scoped> </style>
調(diào)用組件的 focus
方法讓 WrapInput.vue
組件獲取焦點(diǎn)
總結(jié)
本文實(shí)踐了在 vue3 中在二次封裝組件時(shí)如何實(shí)現(xiàn) v-model、插槽傳遞、子組件 ref 獲取
插槽傳遞通過(guò)向 component
組件的 is 屬性傳遞 h 函數(shù)創(chuàng)建虛擬 dom 來(lái)實(shí)現(xiàn)
獲取子組件的 ref 則是使用 new Proxy
的方式來(lái)實(shí)現(xiàn)
以上就是Vue3實(shí)現(xiàn)組件二次封裝的小技巧分享的詳細(xì)內(nèi)容,更多關(guān)于Vue3組件二次封裝的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
一文詳解Vue.js生產(chǎn)環(huán)境文件及優(yōu)化策略
隨著 Vue.js 在前端開發(fā)中的普及,如何高效地將 Vue 項(xiàng)目部署到生產(chǎn)環(huán)境成為了開發(fā)者關(guān)注的重點(diǎn),本文將詳細(xì)解析 Vue.js 生產(chǎn)環(huán)境文件的使用方法、優(yōu)缺點(diǎn)以及優(yōu)化策略,需要的朋友可以參考下2024-12-12解決VantUI popup 彈窗不彈出或無(wú)蒙層的問(wèn)題
這篇文章主要介紹了解決VantUI popup 彈窗不彈出或無(wú)蒙層的問(wèn)題。具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-11-11vue前端重構(gòu)computed及watch組件通信等實(shí)用技巧整理
這篇文章主要為大家介紹了vue前端重構(gòu)computed及watch組件通信等實(shí)用技巧整理,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05vue3?騰訊地圖設(shè)置簽到范圍并獲取經(jīng)緯度的實(shí)現(xiàn)代碼
本文給大家介紹vue3?騰訊地圖設(shè)置簽到范圍并獲取經(jīng)緯度的實(shí)現(xiàn)代碼,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2022-05-05vue3.0?setup中使用vue-router問(wèn)題
這篇文章主要介紹了vue3.0?setup中使用vue-router問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10