vue3如何封装统一的弹窗

在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>

封装技巧总结

  1. 使用$attrs传递属性 - 自动绑定父组件传递所有属性内部组件
  2. 使用$slots动态处理插槽 - 支持所有原生插槽不需要显式声明
  3. 支持v-model - 提供更直观的双向绑定方式
  4. 保持组件简洁 - 避免过度封装,只添加必要的默认值

这种方法使你的封装组件能够:

  • 支持UI库组件所有原生属性事件
  • 支持所有插槽用法
  • 易于维护和扩展
  • 保持与原生组件几乎相同的API

通过这种方式封装组件,你可以在保持项目统一风格的同时,享受到UI库组件的全部功能。

相关推荐
To_OC16 小时前
写了三遍 Todo List,我终于搞懂了 React 父子组件到底怎么通信
前端·javascript·react.js
勇往直前plus17 小时前
Vue3(篇一) 核心概念——响应式模板语法与组件基础
前端·javascript·vue.js
咩咩啃树皮17 小时前
第43篇:Vue3计算属性(computed)完全精讲——缓存机制、依赖计算、业务最优解
前端·vue.js·缓存
徐小夕19 小时前
花了一周,3亿tokens,我开源了一款 Word 文档智能审查平台,文稿自动质检+可视化分析,告别低效人工审核
前端·算法·github
kyriewen20 小时前
别再乱用useEffect了——你写的10个里有8个不该存在
前端·javascript·react.js
Ivanqhz20 小时前
Rust &‘static str浅析
java·前端·javascript·rust
IT_陈寒21 小时前
SpringBoot这个分页坑,我踩了三天才爬出来
前端·人工智能·后端
颜酱21 小时前
05 | 召回前置准备:根据业务数据库生成各数据库(读取配置阶段)
前端·人工智能·后端
zandy101121 小时前
衡石 Agentic BI的ReAct 推理框架在 Agentic BI 中的工程化实践
前端·javascript·react.js
罗超驿21 小时前
JavaEE进阶之路:从Web架构原理到HTML标签全解析
前端·html·web·javaee