基于Vue3组件封装的技巧分享

embedded/2024/9/25 1:17:31/

本文在Vue3的基础上针对一些常见UI组件库组件进行二次封装,旨在追求更好的个性化,更灵活的拓展,提供一些个人的思路见解,如有不妥之处,敬请指出。核心知识点$attrs,$slots

  1. 需求

    需求背景

    日常开发中,我们经常会使用一些UI组件库诸如and design vueelement plus等辅助开发,提升效率。有时我们需要进行个性化封装,以满 足在项目中大量使用的需求。

    错误示范

    基于a-modal封装一个自定义Modal组件:修改modal样式按钮样式每次关闭后销毁渲染到指定元素上等等,后续项目的弹窗全部基于该自定义组件。
javascript"><template><div ref="myModal" class="custom-modal"></div><a-modalv-model:visible="visible"centereddestroyOnClose: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>

代码封装完成,于是乎我们便能在项目中应用带有项目风格的弹窗

javascript"><CustomModal ref="xxxModal" title="xxx" @ok="onXxx" @cancel="onXxx" >content</CustomModal>
  1. $attrs

    问题来了:一切看起来都挺正常。直到有一天同事说:我想要去掉右上角的关闭按钮,能改成自定义的吗
    简单,直接加!
javascript"><!-- 省略不相关代码 -->
<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,还会默认绑定至根元素上。这一点需要注意

javascript"><modalTest :footer="null" :centered="false" :zIndex="999" />
//此时的$attrs
{ "footer": null, "centered": false, "zIndex": 999 }

有了这个组件实例,结合v-bind我们就可以这么写

javascript"><a-modalv-model:visible="visible"centereddestroyOnClose:getContainer="() => $refs.myModal":style="{ width: '560px', ...style }"v-bind="$attrs"
><!---->
</a-modal>

这样一来,我们就可以使用a-modal提供的任意属性和方法了

  1. $slots

    问题来了:插槽怎么办,例如a-modal就提供了许多插槽,是不是要用哪个就先在自定义组件上写好呢?
    错误示例
javascript"><a-modal><!-- default --><slot></slot><!-- title --><template #title>
<slot name="title">{{ title }}</slot></template><!-- other -->
</a-modal>

弊端就像之前的,如果该原生提供了许多插槽,当有需要时岂不是频繁去修改自定义组件添加相应的插槽,其实利用$slots可以解决这个问题
在这里插入图片描述
官网的这段话简明扼要的说出的插槽的原理,我们所传递的插槽最终都是变成

javascript">{'slotName':fn(...args)  //fn返回一个虚拟DOM'defautl': fn(...args) //默认插槽
}

也就是我们传什么插槽进来,$slots就有什么值那么我们可以遍历$slots中的值,有什么插槽我们便动态绑定什么插槽

javascript"><a-modal><template v-for="(_val, name) in $slots" #[name]="options"><slot :name="name" v-bind="options || {}"> </slot></template>
</a-modal>

#[name]="options",我们可以拿到原生a-modalname这个插槽中传递来的一些状态options,并绑定在<slot>上。详情请查看官网:作用域插槽

这样一来,我们原生a-modal怎么使用插槽,自定义组件就怎么使用插槽

javascript"><CustomModal><template #title="{arg1, arg2}">content</template>
</CustomModal>

至此,封装的代码如下

javascript"><template><div ref="myModal" class="custom-modal"></div><a-modalv-model:visible="visible"centered:getContainer="() => $refs.myModal":style="{ width: '560px'}"destroyOnClosev-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>

还有许多优化的空间,例如当前父组件显隐该Modal需使用ref的方式访问visible。在vue3中也可以参考官网的做法这样子写

javascript"><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>

那么使用这个控制这个组件的显示隐藏就方便许多了

javascript"><CustomModal v-model:visible="visible"></CustomModal>

本文中的封装方式是基于vue3来进行实现,不局限在什么UI组件身上。如果使用的是vue2,情况稍有不同,请自行了解。


http://www.ppmy.cn/embedded/116370.html

相关文章

【鸿蒙HarmonyOS NEXT】用户首选项Preference存储数据

【鸿蒙HarmonyOS NEXT】数据存储之用户首选项Preference 一、环境说明二、Preference运作机制三、示例代码加以说明四、小结 一、环境说明 DevEco Studio 版本&#xff1a; API版本&#xff1a;以12为主 二、Preference运作机制 应用场景&#xff1a; 用户首选项为应用提…

分享两个ADG监控脚本

分享两个监控脚本&#xff0c;用于监控Oracle ADG的状态&#xff0c;如果状态异常则发送邮件告警 脚本一&#xff1a; 利用语句查询日志的应用状态&#xff0c;如果长时间未应用则邮件告警&#xff0c;提醒DBA检查ADG的状态是否异常&#xff1b; 阈值条件&#xff1a;最近一…

Git:Git管理

目录 Git 文件管理检测文件状态 status跟踪新文件 add提交更新 commit撤销提交 Commit Git 校验和历史查看 log版本回退 resetgit 忽略文件 Git 分支管理Git 提交对象Git master分支Git 分支管理本地分支管理远程分支管理分支hotfix处理 Git 工作流常见分支冲突处理分支合并冲突…

QT Creator cmake 自定义项目结构, 编译输出目录指定

1. 目的 将不同的源文件放到不同的目录下进行管理&#xff0c; 如下&#xff1a; build: 编译输出目录 include: 头文件目录 rsources: 资源文件目录 src: cpp文件目录 2. 创建完cmake工程后修改CMakeLists.txt 配置 注 &#xff1a; 这里头文件目录是include, 所以在includ…

python调用c++动态链接库,环境是VS2022和vscode2023

目录 前言&#xff1a;配置环境&#xff1a;基础夯实&#xff08;对于ctypes的介绍&#xff09;&#xff1a;1. 加载共享库2. 定义函数原型3. 调用函数4. 处理数据结构5. 处理指针6. 错误处理7. 使用 ctypes.util总结 效果展示&#xff1a;操作步骤(保姆级教学)一在VS中创建dll…

面试金典题2.2

实现一种算法&#xff0c;找出单向链表中倒数第 k 个节点。返回该节点的值。 输入&#xff1a; 1->2->3->4->5 和 k 2 输出&#xff1a; 4 本题的思路其实很简单&#xff0c;总共n个数&#xff0c;找倒数第k个数&#xff0c;其实就是找第n-k个数。主要的问题是这…

Node-RED和物联网分析:实时数据处理和可视化平台

这篇论文的标题是《Node-RED and IoT Analytics: A Real-Time Data Processing and Visualization Platform》&#xff0c;发表在《Tech-Sphere Journal of Pure and Applied Sciences (TSJPAS)》2024年第一期上。论文主要探讨了Node-RED和物联网分析在物联网(IoT)实时数据处理…

SpringSecurity6.x整合手机短信登录授权

前言&#xff1a;如果没有看过我的这篇文章的Springboot3.x.x使用SpringSecurity6(一文包搞定)_springboot3整合springsecurity6-CSDN博客需要看下&#xff0c;大部分多是基于这篇文章的基础上实现的。 明确点我们的业务流程&#xff1a; 需要有一个发送短信的接口&#xff0…