玩转代码|组件封装之Slots、Emit和Props穿透

目录

背景

准备工作

Slots穿透方案-单子组件

Slots穿透方案-多子组件

Props和Emit穿透方案-单子组件

Props和Emit穿透方案-多子组件

最后


背景

组内多人共同开发时免不了基于某UI库二次封装组件来适应项目业务场景的情况,但不知道大家有没有遇到过需要兼容部分或者穿透子组件全部Props或者Slots的情况,这种时候如果针对每一个Props或者Slots去单独处理穿透不仅费时费力而且代码会越来越臃肿难以维护,所以想在这里通过一个简单的例子来对比一下Slots、Props、Emit的各种穿透方案

准备工作

首先新建我们需要用到的子组件,如下

Card.vue

<template><div class="card-container"><div @click="handleClose" class="card-close"><!-- 先用X来代替 --><span>X</span></div><div class="card-title"><slot name="title"><!-- 默认使用props作为title,有slot则优先slot -->{{props.title}}</slot></div><div class="card-content"><slot><!-- content这里也是,一切都以slot优先 -->{{props.content}}</slot></div><div class="card-footer"><slot name="footer"><!-- footer这里也是,一切都以slot优先 -->{{props.footer}}</slot></div></div>
</template><script lang="ts" setup>
import { defineProps, defineEmits } from 'vue'interface ChildrenProps {title?: StringhandleClose?: Function
}const props = defineProps<ChildrenProps>()const emits = defineEmits(['close'])// 响应点击事件
const handleClose = () => {// 这边演示方便,直接调props之后跟上emit调用props.handleClose && props.handleClose()emits('close')}
</script>
...css部分略过

再来准备一个Button.vue

<template><button @click="handleClick"><slot name="prefix"></slot><slot>{{props.title}}</slot><slot name="suffix"></slot></button>
</template><script lang="ts" setup>
import { withDefaults, defineProps, defineEmits } from 'vue'
interface ButtonProps {title?: string,handleClick?: Function
}const emits = defineEmits(['click'])const props = withDefaults(defineProps<ButtonProps>(), {title: 'DONE'
})const handleClick = () => {emits('click')props.handleClick && props.handleClick()
}</script>

以及我们需要实现的ProCard.vue

Slots穿透方案-单子组件

使用Vue提供的Dynamic directive arguments结合v-slot指令
Dynamic directive arguments部分文档链接
在单子组件的情况下穿透Slot比较简单,不需要考虑太多的Slot覆盖问题,只需要关注封装组件自身Slot命名即可,如有命名重复情况可参考多子组件方案解决,比如下面这个ProCard.vue,只用到了Card组件。

<template><div class="procard-container"><PureCard><templatev-for="(slotKey, slotIndex) in slots":key="slotIndex" v-slot:[slotKey]><slot :name="slotKey"></slot></template></PureCard></div>
</template><script lang="ts" setup>
import { useSlots } from 'vue'
import PureCard from '../Card/Card.vue'const slots = Object.keys(useSlots())
</script>

使用

<template><div><ProCard><template #title><span>CardSlot标题</span></template></ProCard></div>
</template>

Slots穿透方案-多子组件

通常我们封装业务组件时一般不至于一个子组件,但多个子组件的情况下就要特别注意Slot命名情况了,这边分享一个在平时开发时我们选择的一个方案:使用不同前缀来区分不同slot,props也是同理。在ProCard.vue中我们加入一个Button组件,协商约定c-xxxCard组件Slot,b-xxxButton组件Slot,这样在经过分解之后就可以区分出应该往哪个组件穿透Slot了。

ProCard.vue中取的所有slots并且理好各个组件所需slots

// 首先还是取到所有Slots的key
const slots = Object.keys(useSlots())// 定义一个buttonSlots,用来组装需要用到的Button组件的slots
const buttonSlots = ref<string[]>([])// 定义一个cardSlots,用来组装需要用到的Card组件的slots
const cardSlots = ref<string[]>([])// 找出各自组件需要的slot组装好push进去就可以在template中穿透到指定组件了
for (let slotIndex = 0; slotIndex < slots.length; slotIndex++) {const slotKey = slots[slotIndex];if (slotKey.indexOf('c-') > -1) {cardSlots.value.push(slotKey.slice(2, slotKey.length))continue}if (slotKey.indexOf('b-') > -1) {buttonSlots.value.push(slotKey.slice(2, slotKey.length))}
}

接下来就可以在template中直接使用了

引入一下ProCard组件来看一下效果吧

<template><div><ProCard title="123"><template #c-title><span>CardSlot标题</span></template><template #c-default><span>CardSlot内容</span></template><template #c-footer><span>CardSlot底部</span></template><template #b-default>按钮</template></ProCard></div>
</template>

成功实现了多组件Slots穿透

Props和Emit穿透方案-单子组件

Props和Emit的穿透方式与Slots的方案类似,使用v-bind直接绑定组件Attributes是最方便的穿透方式,但缺点也很明细,直接v-bind所有Attributes可能会导致命名重复所带来的各种连锁问题,如果像上文slots一样通过前缀来区分组装又有点繁琐,所以如果是多子组件的情况下推荐使用下面的props+v-bind方案。

单子组件这边在ProCard中使用useAttrs来得到子组件上所有的attributes,再使用v-bind直接转发到ProCard的子组件,这样就可以直接穿透Props和Emit了非常方便好用

// 获取到组件所有的attributes
const attrs = useAttrs()

template中转发到子组件

<PureCard@close="onEmitClose":handleClose="handleClose"v-bind="attrs"
><!-- 使用组装好的cardSlots --><templatev-for="(slotKey, slotIndex) in cardSlots":key="slotIndex"v-slot:[slotKey]><slot :name="`c-${slotKey}`"></slot></template>
</PureCard>

父组件调用ProCard

<script setup lang="ts">
import ProCard from './components/ProCard/ProCard.vue'const handleClose = () => {console.log('parent handleClose')
}
const onClose = () => {console.log('parent onClose')
}
</script>
<template><ProCardtitle="123"@close="onClose":handleClose="handleClose"><template #c-title><span>CardSlot标题</span></template><template #c-default><span>CardSlot内容</span></template><template #c-footer><span>CardSlot底部</span></template><template #b-default>按钮</template></ProCard>
</template>

Props和Emit穿透方案-多子组件

多子组件的情况下Props和Emit穿透的解决方案也很多,比如和Slots一样采用前缀的方式来分别组装,但是这种方式较为繁琐,这里比较推荐使用Props分组的方案,在传入的时候就直接把

ProCard

interface ProCardProps {title: StringcardProps: Object // 新增cardProps,用来转发外部传入用于card组件的propsbuttonProps: Object // 新增buttonProps,用来转发外部传入用于button组件的props
}// 获取到组件所有的attributes
const attrs = useAttrs()const props = defineProps<ProCardProps>()// 在template中使用如下,注意替换Card组件和Button组件的v-bind为各自需要接收的props<template><div class="procard-container"><PureCard@close="onEmitClose":handleClose="handleClose"v-bind="props.cardProps"><!-- 使用组装好的cardSlots --><templatev-for="(slotKey, slotIndex) in cardSlots":key="slotIndex"v-slot:[slotKey]><slot :name="`c-${slotKey}`"></slot></template></PureCard><PureButton@click="onButtonClick":handleClick="handleButtonClick"v-bind="props.buttonProps"><!-- 使用组装好的buttonSlots --><templatev-for="(slotKey, slotIndex) in buttonSlots":key="slotIndex"v-slot:[slotKey]><slot :name="`b-${slotKey}`"></slot></template></PureButton></div>
</template>

最后

希望本文可以让你有所收获,Slots、Emit、Props穿透的方案有很多,本文介绍的是我在项目中实际使用到的几种方法,尤其是在重度依赖第三方UI组件库的的情况下特别适用,既能很好的兼顾三方组件库的原生Api,也能在此基础上进行增量扩展。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/212580.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

伦茨科技宣布ST17H6x芯片已通过Apple Find My「查找」认证

深圳市伦茨科技有限公司&#xff08;以下简称“伦茨科技”&#xff09;发布ST17H6x Soc平台。成为继Nordic之后全球第二家取得Apple Find My「查找」认证的芯片厂家&#xff0c;该平台提供可通过Apple Find My认证的Apple查找&#xff08;Find My&#xff09;功能集成解决方案。…

DevOps搭建(一)-之swappiness安装详细步骤

1、安装swappiness yum install procps 修改配置 vim /etc/sysctl.conf 在配置文件中添加参数 vm.swappiness10 使生效 sysctl -p 如何确认swap分区是否开启 # free -mtotal used free shared buff/cache available Mem: 971 …

【React + Typescript】使用WebPack包管理、各种扩展插件组成的初始模板,开源协议:CC-BY-4.0

React Typescript Webpack 模板 模板展示项目结构使用的部分扩展包页面配置代码Layout 公共容器组件路由Jspackage.json 开源模板下载TIP 模板展示 项目结构 使用的部分扩展包 &#x1f4c2; System ├── &#x1f4c2; Plugin │ ├── &#x1f4c4; file-loader | 在处…

为什么SSL证书要设置有效期?如何避免SSL证书过期?

作为企业网站安全防护的基础设施之一&#xff0c;SSL证书对于验证网站真实性和保障网站数据传输安全具有重要作用。然而&#xff0c;当SSL证书过期时&#xff0c;用户与网站之间的传输过程就会处于暴露状态&#xff0c;很容易造成数据被监听、篡改和泄露。那么为什么SSL证书要设…

vue 前端实现login页登陆 验证码

实现效果 // template <el-form :model"loginForm" :rules"fieldRules" ref"loginForm" label-position"left" label-width"0px" class"login-container"><span class"tool-bar"></sp…

Docker本地部署Drupal内容管理框架并实现公网远程访问

文章目录 前言1. Docker安装Drupal2. 本地局域网访问3 . Linux 安装cpolar4. 配置Drupal公网访问地址5. 公网远程访问Drupal6. 固定Drupal 公网地址7. 结语 前言 Dupal是一个强大的CMS&#xff0c;适用于各种不同的网站项目&#xff0c;从小型个人博客到大型企业级门户网站。它…

AtCoder ABC周赛2023 11/4 (Sat) D题题解

目录 原题截图&#xff1a; 题目大意&#xff1a; 主要思路&#xff1a; 注意事项&#xff08;很多人再这个地方掉坑&#xff09;&#xff1a; 代码&#xff1a; 原题截图&#xff1a; 题目大意&#xff1a; 给你两个数组&#xff08;A和B)长度都为n&#xff0c;然你求出一…

C语言——指针(五)

&#x1f4dd;前言&#xff1a; 上篇文章C语言——指针&#xff08;四&#xff09;更加深入的介绍了不同类型指针的特点&#xff0c;这篇文章主要想记录一下函数与指针的结合运用以及const和assert关于指针的用法&#xff1a; 1&#xff0c;函数与指针 2&#xff0c;const 3&am…

域名与SSL证书

域名是互联网上的地址标识符&#xff0c;它通过DNS&#xff08;Domain Name System&#xff09;将易于记忆的人类可读的网址转换为计算机可以理解的IP地址。当用户在浏览器中输入一个网址时&#xff0c;实际上是通过DNS解析到对应的服务器IP地址&#xff0c;从而访问到相应的网…

【DBeaver】驱动添加-Hive和星环

驱动 Hive驱动 hive驱动可以直接去官网下载官网地址&#xff0c;填一下个人信息。 如果想直接下载可以去我上次的资源下地址&#xff0c;需要用zip解压。 星环驱动 星环驱动是我第一次接触&#xff0c;是国产的基于开源Hive驱动自研的产品&#xff0c;我看到官网上有很多类…

SL1581降压恒压 耐压4V-30V降压5V 2A电流 外围简单,四个元器件

SL1581是一款专为降压恒压应用而设计的芯片&#xff0c;具有耐压4V-30V、降压5V、2A电流输出等特点&#xff0c;外围电路简单&#xff0c;仅需四个元器件。 一、芯片介绍 SL1581是一款专为降压恒压应用而设计的芯片&#xff0c;它采用先进的PWM控制技术&#xff0c;具有高效率、…

【PTA-C语言】编程练习4 - 数组Ⅰ

如果代码存在问题&#xff0c;麻烦大家指正 ~ ~有帮助麻烦点个赞 ~ ~ 编程练习4 - 数组Ⅰ&#xff08;1~7&#xff09; 7-1 评委打分&#xff08;分数 10&#xff09;7-2 组合数的和&#xff08;分数 10&#xff09;7-3 找不同&#xff08;分数 15&#xff09;7-4 利用二分查找…

【Flink系列四】Window及Watermark

3.1、window 在 Flink 中 Window 可以将无限流切分成有限流&#xff0c;是处理有限流的核心组件&#xff0c;现在 Flink 中 Window 可以是时间驱动的&#xff08;Time Window&#xff09;&#xff0c;也可以是数据驱动的&#xff08;Count Window&#xff09;。 Flink中的窗口…

ELK(四)—els基本操作

目录 elasticsearch基本概念RESTful API创建非结构化索引&#xff08;增&#xff09;创建空索引&#xff08;删&#xff09;删除索引&#xff08;改&#xff09;插入数据&#xff08;改&#xff09;数据更新&#xff08;查&#xff09;搜索数据&#xff08;id&#xff09;&…

倚天屠龙:Github Copilot vs Cursor

武林至尊&#xff0c;宝刀屠龙。号令天下&#xff0c;莫敢不从。倚天不出&#xff0c;谁与争锋&#xff01; 作为开发人员吃饭的家伙&#xff0c;一款好的开发工具对开发人员的帮助是无法估量的。还记得在学校读书的时候&#xff0c;当时流行CS架构的RAD&#xff0c;Delphi和V…

xcode swiftui项目添加依赖

打开项目targets——Build Phases 点击“” 属于Apple SDKs的依赖可以直接添加 其他依赖需要在 Add Other中添加&#xff0c;在右上角用名字搜索或者URL地址(如GitHub上插件的地址)搜索,然后添加&#xff0c;也可添加本地文件

USB Type-C一拖二线缆制作方法

1 实现方法 Figure 1-1 Type-C Socket(母口) Figure 1-2 Type-C Plug(公头) Table 1-1 Type-C Socket Pin连接描述 Type-C Plug连接&#xff0c; 需要做一个一拖二的线&#xff0c;一根的一端是USB&#xff0c; 另外一根的一端是USB转UART&#xff0c; 参考Table 1-2。 Table 1…

css 修改滚动条样式,解决Windows浏览器中滚动条不美观问题

Windows环境中的浏览器中滚动条默认是直接显示了&#xff0c;不管光标是否进入该区域&#xff0c;这样就很不美观&#xff0c;如下图&#xff1a; 之前样式为 .well {display: block;background-color: #f2f2f2;border: 1px solid #ccc;margin: 5px;width: calc(100% - 12px);h…

spark sql基于RBO的优化

前言 这里只对RBO优化进行简单的讲解。讲解RBO之前必须对spark sql的执行计划做一个简单的介绍。 这个里讲解的不是很清楚&#xff0c;需要结合具体的执行计划来进行查看 1、执行计划 在spark sql的执行计划中&#xff0c;执行计划分为两大类&#xff0c;即逻辑执行计划、物…

uniapp 打开文件管理器上传(H5、微信小程序、android app三端)文件

H5跟安卓APP 手机打开的效果图&#xff1a; Vue页面&#xff1a; <template><view class"content"><button click"uploadFiles">点击上传</button></view> </template><script>export default {data() {return…