本文在Vue3的基础上针对一些常见UI组件库组件进行二次封装,旨在追求更好的个性化,更灵活的拓展,提供一些个人的思路见解,如有不妥之处,敬请指出。核心知识点
$attrs
,
$slots
1、需求说明
需求背景
:日常开发中,我们经常会使用一些UI组件库
诸如and design vue、element plus等
辅助开发,提升效率。有时我们需要进行个性化封装,以满足在项目中大量使用的需求。
错误示范
:基于
a-modal
封装一个自定义Modal组件:
修改modal样式,按钮样式、每次关闭后销毁、渲染到指定元素上等等
,后续项目的弹窗全部基于该自定义组件。
<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>
2.如果父组件传递了style,class,那么这这些值不仅会存在于$attrs
,还会默认绑定至根元素上。这一点需要注意
"null"
:centered="false" :zIndex="999" />
//此时的$attrs
{ "footer": null, "centered": false, "zIndex": 999 }
有了这个组件实例,结合v-bind
我们就可以这么写
v-model:visible="visible"
centered
destroyOnClose
:getContainer="() => $refs.myModal"
:style="{ width: '560px', ...style }"
v-bind="$attrs"
>
</a-modal>
这样一来,我们就可以使用a-modal
提供的任意属性和方法了
3、$slots
问题来了:插槽怎么办,例如a-modal
就提供了许多插槽,是不是要用哪个就先在自定义组件上写好呢
错误示例:
<slot>slot>
<template #title>
<slot name="title">{{ title }}slot>
template>
</a-modal>
弊端就像之前的,如果该原生提供了许多插槽,当有需要时岂不是频繁去修改自定义组件添加相应的插槽
其实利用$slots
可以解决这个问题
官网的这段话简明扼要的说出的插槽的原理,我们所传递的插槽最终都是变成
{
'slotName':fn(...args) //fn返回一个虚拟DOM
'defautl': fn(...args) //默认插槽
}
也就是我们传什么插槽进来,$slots
就有什么值
那么我们可以遍历$slots
中的值,有什么插槽我们便动态绑定什么插槽
<template v-for="(_val, name) in $slots" #[name]="options">
<slot :name="name" v-bind="options || {}"> slot>
template>
</a-modal>
#[name]="options"
,我们可以拿到原生a-modal
在name
这个插槽中传递来的一些状态options
,并绑定在
上。详情请查看官网:作用域插槽[1]。
这样一来,我们原生a-modal
怎么使用插槽,自定义组件就怎么使用插槽
<template #title="{arg1, arg2}">
content
template>
</CustomModal>
至此,封装的代码如下