在Vue3开发中,我们经常需要对UI组件库进行二次封装以满足项目需求。
下面我将分享一些实用的封装技巧,帮助你创建更灵活、可维护的组件。
初始封装方案的问题
js
<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: String,
style: Object,
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>
这种封装方式存在明显问题:每次新增需求都需要修改组件代码
,导致组件越来越臃肿
。
优化方案:使用$attrs
和$slots
1. 利用$attrs
传递属性
js
<template>
<div ref="myModal" class="custom-modal"></div>
<a-modal
v-model:visible="visible"
centered
destroyOnClose
:getContainer="() => $refs.myModal"
:style="{ width: '560px' }"
v-bind="$attrs"
>
<template v-for="(_, name) in $slots" #[name]="slotData">
<slot :name="name" v-bind="slotData || {}"></slot>
</template>
</a-modal>
</template>
<script setup>
const visible = ref(false);
defineExpose({ visible });
</script>
2. 支持v-model控制显隐
js
<script setup>
const props = defineProps({
visible: Boolean
});
const emit = defineEmits(['update:visible']);
watch(
() => props.visible,
(newVal) => emit('update:visible', newVal)
);
</script>
完整优化后的组件
js
<template>
<div ref="myModal" class="custom-modal"></div>
<a-modal
:visible="internalVisible"
centered
:getContainer="() => $refs.myModal"
:style="{ width: '560px' }"
destroyOnClose
v-bind="$attrs"
@update:visible="handleVisibleChange"
>
<template v-for="(_, name) in $slots" #[name]="slotData">
<slot :name="name" v-bind="slotData || {}"></slot>
</template>
</a-modal>
</template>
<script setup>
import { ref, watch } from 'vue';
const props = defineProps({
visible: {
type: Boolean,
default: false
}
});
const emit = defineEmits(['update:visible']);
const internalVisible = ref(props.visible);
watch(
() => props.visible,
(newVal) => {
internalVisible.value = newVal;
}
);
const handleVisibleChange = (value) => {
internalVisible.value = value;
emit('update:visible', value);
};
</script>
<style lang="less" scoped>
.custom-modal {
:deep(.ant-modal) {
// 自定义样式
}
}
</style>
使用示例
js
<CustomModal
v-model:visible="visible"
title="自定义标题"
:closable="false"
:footer="null"
>
<template #title>
<h2>自定义标题内容</h2>
</template>
<p>这是模态框的内容</p>
</CustomModal>
封装技巧总结
- 使用
$attrs
传递属性 -自动绑定父组件传递
的所有属性
到内部组件
- 使用
$slots
动态处理插槽 -支持所有原生插槽
而不需要显式声明
- 支持v-model - 提供更直观的
双向绑定
方式 - 保持组件简洁 -
避免过度封装
,只添加必要的默认值
这种方法使你的封装组件
能够:
- 支持
UI库组件
的所有原生属性
和事件
- 支持
所有插槽
用法 - 易于维护和扩展
- 保持与原生组件
几乎相同的API
通过这种方式封装组件,你可以在保持项目统一风格的同时,享受到UI库组件的全部功能。