在前端开发中,二次封装第三方组件十分常见。当现有的第三方组件无法完全满足项目需求时,我们会对其进行定制化封装,以增强功能、简化用法或适配特定业务场景。相信大家在使用vue3进行开发时,都有二次封装经验。最近在一次学习中,看到了一种让我眼前一亮的二次封装的方式,下面跟大家分享一下。
1. 如何封装组件
在封装一个组件前,我们要思考一些问题:
- props如何透传给第三方组件
- 插槽如何透传给第三方组件
- 第三方组件的方法如何暴露出去
2. 透传props
vue3的组件实例有个对象 <math xmlns="http://www.w3.org/1998/Math/MathML"> a t t r s ,包含了组件所有透传 a t t r i b u t e s 。不仅包含 p r o p s , v − o n 事件监听器,还有 c l a s s , s t y l e , i d 等。所以可以通过 v − b i n d ,传递 attrs,包含了组件所有透传 attributes。不仅包含props,v-on事件监听器,还有class,style,id等。所以可以通过v-bind,传递 </math>attrs,包含了组件所有透传attributes。不仅包含props,v−on事件监听器,还有class,style,id等。所以可以通过v−bind,传递attrs给第三方组件,来实现透传props的目的。下面以vant-ui 的popup为例,做一个二次封装。
xml
// myPopup.vue
<template>
<div>
<div>自己封装的popup,一些自定义内容</div>
<van-popup v-bind="$attrs">
</van-popup>
</div>
</template>Ï
<script setup>
</script>
// App.vue
<template>
<my-popup
v-model:show="isShow"
:style="{ padding: '0px', borderRadius: '12px' }"
position="bottom"
:round="true"
>
</my-popup>
</template>
<script setup>
import { ref } from 'vue'
import MyPopup from './myPopop.vue'
const isShow = ref(true)
</script>
这样,在App.vue里传递的参数,都可以透传给
3. 插槽怎么传
我们想到vue3组件实例有 <math xmlns="http://www.w3.org/1998/Math/MathML"> s l o t s , 通过遍历 slots,通过遍历 </math>slots,通过遍历slots,透传给第三方组件。示例如下:
xml
// myPopup.vue
<template>
<div>
<div>自己封装的popup,一些自定义内容</div>
<van-popup v-bind="$attrs">
<template v-for="(slot, name) in $slots" :key="name" #[name]="slotsProps">
<slot :name="name" v-bind="slotsProps"></slot>
</template>
</van-popup>
</div>
</template>Ï
<script setup>
</script>
4. 第三方组件的方法如何暴露出去
一般在vue3中,子组件通过defineExpose暴露方法给父组件,所以通过defineExpose可以把第三方组件的方法一个个暴露出去。
xml
<template>
<div>
<div>自己封装的popup,一些自定义内容</div>
<van-popup ref="popupRef" v-bind="$attrs">
<template v-for="(slot, name) in $slots">
<slot :name="name"></slot>
</template>
</van-popup>
</div>
</template>Ï
<script setup>
import { ref } from 'vue'
const popupRef = ref(null)
defineExpose({
close() {
popupRef.value.close()
},
open(){
popupRef.open()
},
click(){
popupRef.click()
}
// 这里就不一一写出来了
})
</script>
这样写当然没有问题,那有没有更简单的方法呢?下面我将为大家提供另外一个种封装组件的思路,可以让代码看起来更简洁。
5. 动态组件
大家在写路由时,经常会用到动态组件<component>,要渲染的实际组件由 is prop 决定。被传给 :is 的值可以是被注册的组件名或者导入的组件对象,但是还有一种方式,就是vnode即虚拟dom。而在vue中,虚拟节点由h函数创建,我们可以先简单看一下虚拟函数h的用法:
less
h(
// 必选:组件名、组件对象或标签名
'div' | Component,
// 可选:数据对象(包含 props、attrs、class、style 等)
{
props: { ... },
attrs: { ... },
on: { click: () => {} },
class: { ... },
style: { ... }
},
// 可选:子节点(字符串、数组或其他 VNode)
'Hello' | [h('span', 'World')] | ...
);
vue官方文档的介绍:
php
// 完整参数签名
function h(
type: string | Component,
props?: object | null,
children?: Children | Slot | Slots
): VNode
h函数的第一个参数是组件名、组件对象或标签名,第二个参数是要传递给组件的props,属性,事件,chass等等,第三个参数是子节点。前两个参数很好理解,我们来重点关注一下第三个参数,可以看到第三个参数可以传插槽。上面我们透传插槽时用是的for循环遍历 <math xmlns="http://www.w3.org/1998/Math/MathML"> s l o t s ,而在使用 h 函数时, slots,而在使用h函数时, </math>slots,而在使用h函数时,slots恰好是第三个参数,是不是简单很多。下面我们来修改一下我们的代码:
xml
<template>
<div>
<div>自己封装的popup,一些自定内容</div>
<component
:is="h(Popup, { ...$attrs }, $slots)"
></component>
</div>
</template>Ï
<script setup>
import { ref, h } from 'vue'
import { Popup } from 'vant'
</script>Ï
下面我们来解决暴露方法的问题,上面的例子,我们是通过defineExpose将第三方组件的方法一一暴露出去的,这样写有一点麻烦。实际上在 Vue 3 中,getCurrentInstance() 返回的组件实例对象包含一个 exposed 属性,和defineExpose的功能一样,都是暴露组件的属性给父组件使用。那我们就可以把Popup组件实例通过ref赋值给exposed,从而暴露方法出去。
xml
<template>
<div>
<div>自己封装的popup,一些自定义内容</div>
<component
:is="h(Popup, { ...$attrs,ref: changeRef }, $slots)"
></component>
</div>
</template>Ï
<script setup>
import { ref,getCurrentInstance,h } from 'vue'
import { Popup } from 'vant'
const vm = getCurrentInstance()
const changeRef = (instance) => {
// instance为组件实例
vm.exposeProxy = vm.exposed = instance || {}
}
</script>Ï
上面这样的封装写法,代码简洁了很多,但是却有些难以理解。
- 首先我们使用函数模版引用的方式,拿到popup的组件实例。

-
通过
exposed把popup组件方法暴露给父组件。popup的组件实例上有组件方法,把组件实例赋值给exposed属性后,exposed就有了该组件的方法。 -
exposeProxy是exposed属性的代理,父组件实例访问的是exposeProxy的属性。
通常情况下我们会使用defineExpose暴露组件的方法给父组件使用,而exposed 属性存储了通过 defineExpose 显式暴露给父组件的属性和方法,仅对父组件可见。我们可以反向思考一下,给exposed直接赋值,父组件也是可见的。
上面仅仅是提供了一种vue3组件二次封装的方式,代码比较精简,但是比较难理解。当有更多要求时,还需要再优化。比如外层dom不需要父组件透传的属性,我们可以设置defineOptions({inheritAttrs: false}),我们还可以通过useAttrs访问所有透传的属性值。
6. 团队介绍
「三翼鸟数字化技术平台-应用软件框架开发」主要负责设计工具的研发,包括营销设计工具、家电VR设计和展示、水电暖通前置设计能力,研发并沉淀素材库,构建家居家装素材库,集成户型库、全品类产品库、设计方案库、生产工艺模型,打造基于户型和风格的AI设计能力,快速生成算量和报价;同时研发了门店设计师中心和项目中心,包括设计师管理能力和项目经理管理能力。实现了场景全生命周期管理,同时为水,空气,厨房等产业提供商机管理工具,从而实现了以场景贯穿的B端C端全流程系统。