基于Vue3實現組件封裝的技巧分享
需求
需求背景
日常開發(fā)中,我們經常會使用一些UI組件庫諸如and design vue、element plus等輔助開發(fā),提升效率。有時我們需要進行個性化封裝,以滿 足在項目中大量使用的需求。
錯誤示范
基于a-modal封裝一個自定義Modal組件:修改modal樣式,按鈕樣式、每次關閉后銷毀、渲染到指定元素上等等,后續(xù)項目的彈窗全部基于該自定義組件。
<template>
<div ref="myModal" class="custom-modal"></div>
<a-modal
v-model:visible="visible"
centered
destroyOnClose
:getContainer="() => $refs.myModal"
@ok="handleOk"
@cancel="handleCancel"
:style="{ width: '560px', ...style }"
:cancelText="cancelText"
:okText="okText"
>
<!-- 以上皆為該組件的默認屬性 -->
<slot></slot>
</a-modal>
</template>
<script setup>
const props = defineProps({
title: {
type: String,
default: "",
},
style: {
type: Object,
default: () => ({}),
},
cancelText: {
type: String,
default: "取消",
},
okText: {
type: String,
default: "確定",
},
});
const emits = defineEmits(["handleOk", "handleCancel"]);
const visible = ref(false);
const handleOk = () => {
emits("handleOk");
};
const handleCancel = () => {
emits("handleCancel");
};
defineExpose({ visible });
</script>
<style lang="less" scoped>
.custom-modal {
:deep(.ant-modal) {
//省略幾百行樣式代碼
}
}
</style>
代碼封裝完成,于是乎我們便能在項目中應用帶有項目風格的彈窗
$attrs
問題來了:一切看起來都挺正常。直到有一天同事說:我想要去掉右上角的關閉按鈕,能改成自定義的嗎
簡單,直接加!
<!-- 省略不相關代碼 -->
<a-modal :closable="closable"></a-modal>
<script setup>
const props = defineProps({
//...
closable:{
type: Boolean,
default: false
}
});
</script>
另一位同事說:我不想讓它是居中的,能改成自定義的嗎,還有一位同事說…
思考:這樣的情況多了,就有點難頂。每次一有新的需求,我就得改這個組件,導致這個組件代碼越來越冗余。那么是否有一種方式能夠將傳進來的屬性自動綁定給a-modal呢,有,那兒就是attrs

注意:
1.vue提供了$attrs這么一個屬性用于接收父組件傳遞下來的屬性,$attrs不包括已經寫入props的值
2.如果父組件傳遞了style,class,那么這這些值不僅會存在于$attrs,還會默認綁定至根元素上。這一點需要注意
<modalTest :footer="null" :centered="false" :zIndex="999" />
//此時的$attrs
{ "footer": null, "centered": false, "zIndex": 999 }
有了這個組件實例,結合v-bind我們就可以這么寫
<a-modal
v-model:visible="visible"
centered
destroyOnClose
:getContainer="() => $refs.myModal"
:style="{ width: '560px', ...style }"
v-bind="$attrs"
>
<!-- 略 -->
</a-modal>
這樣一來,我們就可以使用a-modal提供的任意屬性和方法了
$slots
問題來了:插槽怎么辦,例如a-modal就提供了許多插槽,是不是要用哪個就先在自定義組件上寫好呢?
錯誤示例
<a-modal>
<!-- default -->
<slot></slot>
<!-- title -->
<template #title>
<slot name="title">{{ title }}</slot>
</template>
<!-- other -->
</a-modal>
弊端就像之前的,如果該原生提供了許多插槽,當有需要時豈不是頻繁去修改自定義組件添加相應的插槽,其實利用$slots可以解決這個問題

官網的這段話簡明扼要的說出的插槽的原理,我們所傳遞的插槽最終都是變成
{
'slotName':fn(...args) //fn返回一個虛擬DOM
'defautl': fn(...args) //默認插槽
}
也就是我們傳什么插槽進來,$slots就有什么值那么我們可以遍歷$slots中的值,有什么插槽我們便動態(tài)綁定什么插槽
<a-modal>
<template v-for="(_val, name) in $slots" #[name]="options">
<slot :name="name" v-bind="options || {}"> </slot>
</template>
</a-modal>
#[name]="options",我們可以拿到原生a-modal在name這個插槽中傳遞來的一些狀態(tài)options,并綁定在<slot>上。詳情請查看官網:作用域插槽。
這樣一來,我們原生a-modal怎么使用插槽,自定義組件就怎么使用插槽
<CustomModal>
<template #title="{arg1, arg2}">
content
</template>
</CustomModal>
至此,封裝的代碼如下
<template>
<div ref="myModal" class="custom-modal"></div>
<a-modal
v-model:visible="visible"
centered
:getContainer="() => $refs.myModal"
:style="{ width: '560px'}"
destroyOnClose
v-bind="$attrs"
>
<template v-for="(_val, name) in $slots" #[name]="ops">
<slot :name="name" v-bind="ops || {}"> </slot>
</template>
</a-modal>
</template>
<script setup>
const visible = ref(false);
defineExpose({ visible });
</script>
<style lang="less" scoped>
.custom-modal {
//style
}
</style>
還有許多優(yōu)化的空間,例如當前父組件顯隱該Modal需使用ref的方式訪問visible。在vue3中也可以參考官網的做法這樣子寫
<template>
<div ref="myModal" class="custom-modal"></div>
<a-modal
:visible="visible"
//....
v-bind="$attrs"
>
<!-- ... -->
</a-modal>
</template>
<script setup>
defineProps(['visible'])
const emit = defineEmits(); // 不用寫"update:visible",vue會自動加上
watch(
() => props.visible,
(newVal) => emit("update:visible", newVal);
);
</script>
那么使用這個控制這個組件的顯示隱藏就方便許多了
<CustomModal v-model:visible="visible"></CustomModal>
本文中的封裝方式是基于vue3來進行實現,不局限在什么UI組件身上。如果使用的是vue2,情況稍有不同,請自行了解。
以上就是基于Vue3實現組件封裝的技巧分享的詳細內容,更多關于Vue3組件封裝的資料請關注腳本之家其它相關文章!
相關文章
vue執(zhí)行配置選項npm?run?serve的本質圖文詳解
本地開發(fā)一般通過執(zhí)行npm run serve命令來啟動項目,那這行命令到底存在什么魔法?下面這篇文章主要給大家介紹了關于vue執(zhí)行配置選項npm?run?serve的本質的相關資料,需要的朋友可以參考下2023-05-05
解決vue.js中settimeout遇到的問題(時間參數短效果不穩(wěn)定)
這篇文章主要介紹了解決vue.js中settimeout遇到的問題(時間參數短效果不穩(wěn)定),具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-07-07

