vue3组件二次封装-另外一种思路

在前端开发中,二次封装第三方组件十分常见。当现有的第三方组件无法完全满足项目需求时,我们会对其进行定制化封装,以增强功能、简化用法或适配特定业务场景。相信大家在使用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>Ï

上面这样的封装写法,代码简洁了很多,但是却有些难以理解。

  1. 首先我们使用函数模版引用的方式,拿到popup的组件实例。
  1. 通过exposed把popup组件方法暴露给父组件。popup的组件实例上有组件方法,把组件实例赋值给exposed属性后,exposed就有了该组件的方法。

  2. exposeProxy是exposed属性的代理,父组件实例访问的是exposeProxy的属性。

通常情况下我们会使用defineExpose暴露组件的方法给父组件使用,而exposed 属性存储了通过 defineExpose 显式暴露给父组件的属性和方法,仅对父组件可见。我们可以反向思考一下,给exposed直接赋值,父组件也是可见的。

上面仅仅是提供了一种vue3组件二次封装的方式,代码比较精简,但是比较难理解。当有更多要求时,还需要再优化。比如外层dom不需要父组件透传的属性,我们可以设置defineOptions({inheritAttrs: false}),我们还可以通过useAttrs访问所有透传的属性值。

6. 团队介绍

三翼鸟数字化技术平台-应用软件框架开发」主要负责设计工具的研发,包括营销设计工具、家电VR设计和展示、水电暖通前置设计能力,研发并沉淀素材库,构建家居家装素材库,集成户型库、全品类产品库、设计方案库、生产工艺模型,打造基于户型和风格的AI设计能力,快速生成算量和报价;同时研发了门店设计师中心和项目中心,包括设计师管理能力和项目经理管理能力。实现了场景全生命周期管理,同时为水,空气,厨房等产业提供商机管理工具,从而实现了以场景贯穿的B端C端全流程系统。

相关推荐
老华带你飞2 小时前
宠物商城销售|基于Java+ vue宠物商城销售管理系统(源码+数据库+文档)
java·开发语言·数据库·vue.js·spring boot·宠物
计算机学姐2 小时前
基于Python的在线考试系统【2026最新】
开发语言·vue.js·后端·python·mysql·django·flask
live丶4 小时前
从零实现一个低代码 H5 页面编辑器(Vue3 + 拖拽)
前端·vue.js
码界奇点4 小时前
基于Django REST framework与Vue的前后端分离后台管理系统设计与实现
vue.js·后端·python·django·毕业设计·源代码管理
松莫莫4 小时前
Vue 3 项目搭建完整流程(Windows 版 · 避坑指南)
前端·vue.js·windows
涔溪4 小时前
深入理解 Vue Router 中 Hash 模式和 History 模式 的核心区别、底层原理、使用场景及部署注意事项
vue.js·哈希算法·history
San304 小时前
破茧成蝶:Web 前端开发的三次革命与架构演进史
javascript·vue.js·ecmascript 6
性野喜悲4 小时前
<script setup lang=“ts“>+element-plus模拟required 展示星号*且不触发 Element UI 的校验规则
javascript·vue.js·elementui
GIS好难学5 小时前
2025年华中农业大学暑期实训优秀作品(5):智慧煤仓监控系统平台——重塑煤炭仓储管理新模式
前端·vue.js·信息可视化·gis开发·webgis