全国免费咨询:

13245491521

VR图标白色 VR图标黑色
X

中高端软件定制开发服务商

与我们取得联系

13245491521     13245491521

2022-11-16_衍生需求:按钮集成图标组件 & 图标选择器

您的位置:首页 >> 新闻 >> 行业资讯

衍生需求:按钮集成图标组件 & 图标选择器 本文为稀土掘金技术社区首发签约文章,14天内禁止转载,14天后未获授权禁止转载,侵权必究! 专栏上篇文章传送门:Web 中的字体和 SVG 图标,你了解多少? 本节涉及的内容源码可在vue-pro-components c4 分支找到,欢迎 star 支持! 前言本文是基于Vite+AntDesignVue打造业务组件库专栏第 5 篇文章【衍生需求:按钮集成图标组件 & 图标选择器】,聊聊实际业务中与图标组件相关的一些衍生需求,例如: 怎么通过一个简单的icon属性就能在a-button中用上我们的图标组件?怎么实现一个可视化的图标选择器?按钮集成图标组件背景介绍按钮中搭配图标一起用,是再常见不过的场景了。ant-design-vue 的 Button 组件具备自定义图标的能力,具体是通过icon插槽实现的。 image.png虽然能实现,但是感觉写起来也挺复杂的,代码量不少,那么能不能简化成这样呢?只要通过一个icon属性(而不是插槽)就能把图标展示出来呢?最理想的状态是还能同时支持我们自己的业务图标。 //比较理想的用法 //既支持 a-buttontype="primary"icon="SearchOutlined"Search/a-button a-buttontype="primary"icon="location"Search/a-button 事实上,ant-design-vue没有支持这种能力。 首先,从字符串到组件,是需要一个解析的过程,这对应resolveComponent,简单看下源码,resolveComponent内部会调用resolveAssets,我们发现,这需要将组件注册好,不管是注册到局部还是全局,都可以。 image.png而 ant-design-vue 是一个通用组件库,它提供的图标都是一个个独立的组件,这些组件都在@ant-design/icons-vue这个包里。如果要实现字符串到组件的解析能力,就要求把图标组件都提前注册好,这就违背了按需加载的初衷。 另外, ant-design-vue 也要考虑用户自定义图标的场景,所以综合来看留个插槽算是比较合理的做法。 然而,对业务方来说,通常考虑的是: 大而全:能力丰富,既要有原始组件本身的能力,还能增加一些定制的能力;用起来方便:提供最简单的用法;性能过得去:没有明显的性能负担即可。那么,我们自己来尝试实现一下这些能力。 封装按钮组件a-button我们也不能改,所以,需要先做一个vp-button,它既有a-button的全部能力,还能支持使用各种图标组件,这样才不至于说封装了一个组件,却牺牲了底层的能力。 image.png我们首先要考虑的是:AButton 本身有很多属性,那么我们怎么让 VpButton 同样支持这些属性呢? 有两条路子可供选择: 利用v-bind="$attrs"透传属性,但是出于性能考虑,通过$attrs透传的这部分 attributes 不像 props 那样具备响应式特性。将 AButton 支持的 props,都列入 VpButton 的 props 中,然后 VpButton 再原样通过属性绑定传递给 AButton,这样就能保证这些 props 的响应式依然有效。路子1虽然是最简单的,但缺失响应式这一缺点有时候会很致命。 路子2是比较靠谱的,但是使用起来很繁琐,需要将 AButton 支持的属性重复定义在 VpButton 中。此外,一旦粗心就可能会遗漏一些属性,这就会导致功能是有缺失的,那么怎么解决这些问题呢? 我的思路是: 想办法把 AButton 的 props 定义取出来,与我们要额外扩展的属性做一个合并,统一作为 VpButton 的 props 定义。这样一来,从外部调用者的视角来看,VpButton 支持的属性就是完整的,给人的直观感觉就是:VpButton 是 AButton 的加强版,我可以放心使用。在 VpButton 内部需要封装 AButton,同时要从所有 props 中将属于 AButton 的那部分 props 挑选出来,传递给 AButton,这样对 AButton 来说就是无感的,因为我们传给 AButton 的属性是完全符合要求的。只要我们封装的 VButton 满足了上面这两点,这个组件就是趋近完美的,它向上对调用者提供了更强大的能力,同时向下又包容了 AButton 的能力。 我们来试试看,大致查阅 ant-desigin-vue 的 Button 组件源码,我们可以发现,AButton 的属性是由这些代码构造出来的。 image.pngimage.png我们新建一个button/props.ts文件,尝试一下下面的代码,看看能不能拿到预期的 AButton 属性定义,如果能成功,那就意味着我们就不必一个一个属性地重复定义了,同时也意味着我们得到了一种扩展属性的基本方法。 importbuttonPropsfrom'ant-design-vue/es/button/buttonTypes' import{initDefaultProps}from'ant-design-vue/es/_util/props-util' const_buttonProps=initDefaultProps(buttonProps(),{ type:'default', }) console.log(_buttonProps) 打印出来发现,这就是我们需要的 AButton 的属性定义: image.png接着我们用一个enhancedProps来定义需要扩展的属性,这里先给出以下几个属性: exportconstenhancedProps={ //对应自定义图标的名称 ico:{ type:String, }, //图标的大小 icoSize:{ type:Number, }, //图标颜色 icoColor:{ type:String, }, //按钮主体颜色,影响边框颜色,背景色 primaryColor:{ type:String, }, } 用ico接收图标名称,是为了避免与AButton的icon插槽冲突。 然后我们把_buttonProps和enhancedProps这两部分组成一个完整的props。 exportconstinnerKeys=Object.keys(_buttonProps) exportconstenhancedKeys=Object.keys(enhancedProps) exportconstprops={ ..._buttonProps, ...enhancedProps, } exporttypeVpButtonProps=ExtractPropTypestypeofprops image.png可以发现属性很完整了,其中框起来的部分是我们扩展的属性,剩下的都是 AButton 支持的属性。 接下来就是看怎么使用这些属性了,直接上组件主体代码,这里用了 tsx 实现。 import { defineComponent } from 'vue' import { Button } from 'ant-design-vue' import IconSvg from '../icon-svg' import { innerKeys, props as buttonProps } from './props' import { usePickedProps } from '../hooks/props' export default defineComponent({ name: 'VpButton', props: buttonProps, setup(props, { slots }) { // 把属于 AButton 的属性挑选出来,再绑定到 AButton 上 const innerProps = usePickedProps(props, innerKeys) return () = ( Button {...innerProps.value} class="vp-button" style={{ backgroundColor: props.primaryColor, borderColor: props.primaryColor }} v-slots={{ ...slots, default: () = ( {props.ico && !props.loading ? IconSvg icon={props.ico} size={props.icoSize} color={props.icoColor} / : null} {slots?.default?.()} ), }} /Button ) }, }) 这里用到了一个usePickedProps方法,将 AButton 支持的属性全部挑选出来,然后绑定到 AButton 上(因为 props 中有我们扩展的属性,而这部分不需要传给 AButton)。 usePickedProps的逻辑也不复杂,主要是基于lodash-es的pick方法进行属性挑选,然后用computed计算属性返回结果,这样才能保证得到的innerProps是具备响应式特性的。 image.png而在图标这块的处理,除了支持通过ico属性直接展示IconSvg图标,我们依然支持通过icon插槽进行自定义的图标展示,这与 AButton 的默认行为是一致的。 当我们在 packageplayground引入这个 VpButton 组件使用时,会发现报了一个错误Uncaught ReferenceError: React is not defined。 image.png这是因为我们的当前环境还不支持jsx,需要引入一个@vitejs/plugin-vue-jsx插件。 //安装一下jsx插件 lernaadd@vitejs/plugin-vue-jsx--scope=playground--dev vite.config.ts增加 jsx 相关配置: image.png由于 VpButton 内部用到了 AButton 和 IconSvg 这两个组件,而这两个组件也是有定义样式的,所以我们在button/style/index.less中引入一下相关的样式依赖。 image.png接着我们测试一下基本效果,基本上可以满足常见使用场景: image.pngico属性支持多种图标源可行吗?那么有没有可能实现上面说的:用一个ico属性,既能支持 ant-design 的内置图标,又能支持由 IconSvg 组件实现的业务图标呢?我们可以尝试做一下看看。 如上文所述,首先需要有一个字符串到组件的解析过程,这需要用到resolveComponent,这部分逻辑可以内置到 VpButton 组件中。与此同时,还需要将 ant-design 的图标注册到组件上下文中,这部分操作放在业务调用方比较合适(这可以支持按需加载),因为我们不可能把所有 ant-design 的图标都注册到 VpButton 组件中,这会让 VpButton 组件变成一个巨型组件。 好,思路清楚后,我们首先实现 VpButton 内部的逻辑。为了减少判断逻辑,我们通过一个icoSource标识图标的来源,默认为"biz",表示展示 IconSvg 支持的业务图标,同时支持"antd",表示展示 ant-design 的图标。 image.png当icoSource的值为"antd"时,我们利用 Vue 提供的resolveComponent和h进行组件解析和渲染,否则逻辑照旧。 image.png接着我们在App.vue调用一下。 引入PlusOutlined图标组件: image.png尝试通过icoSource和ico属性渲染出图标: image.png结果发现,resolveComponent还是找不到 PlusOutlined 组件。 [Vue warn]: Failed to resolve component: PlusOutlined image.png回头看了一下源码resolveComponent的流程,发现它只会在当前组件实例和应用实例中去寻找组件,而resolveComponent是在 Button 组件中使用的,即便我们在App.vue中引入了 PlusOutlined 也是解析不到的,所以只能在应用实例全局注册 PlusOutlined,类似这样: image.png效果就出来了: image.png但是这样用起来也是相当繁琐,虽然实现了功能,但还不如直接用icon插槽简单呢,所以这条路基本上可以选择放弃了。 这部分代码可以见这个版本,相关分支上就不保留这部分代码了。 如果与unplugin-vue-components配套使用,其提供的AntDesignVueResolver也支持自动识别并导入@ant-design/icons-vue中的图标,用起来也算方便。 image.png图标选择器在中后台或者一些低码搭建场景中,很多地方需要动态配置图标,最常见的可能就是给菜单配图标。比较简单的实现方式就是直接用一个文本输入框配置图标名,但是这样并不直观,也容易出错,因为你不确定你输入的图标名是不是对应一个有效的图标。而且这要求操作人员熟知图标的名称,显然不是很方便。 image.png那么能不能提供一个图标选择器进行可视化的配置呢?我们可以来试一试! 要进行图标的选择,首先必须知道有哪些图标,也就是需要有一个图标清单。那么具体怎么做呢? 一个简单粗暴的方法是:项目中维护一个数组,把 icon 名称全部都手动录入。但是这样显得很繁琐,每个业务项目都要手动录,太容易出错了。 另一个方法是:从 iconfont 图标库中寻找有用的信息,基于这些信息编写脚本自动生成一个图标清单。 那么 iconfont 中有哪些我们可以利用的信息呢? 我最开始想的是检查 iconfont 项目调用的接口,从接口中把信息抓出来。确实找到了一个detail.json请求,这里有相关的 icons 数组。 image.png记得前些时间还检查过,iconfont 还没有提供这个 icons 字段,可能最近优化了。 虽然请求是找到了,但是还要考虑调这个请求是不是要验证 token 等身份信息。果不其然,需要验证! image.png这也就意味着,如果我们想用这个能力,需要打通登录流程,先调登录接口,再调这个 detail.json 的请求。 image.png只要模拟一下这个登录请求即可,看着不复杂,其实做起来不简单,首先要搞清楚 password 的加密策略,还有两个 bx- 开头的字段是怎么得来的,这需要研究一下 iconfont 相关的 js 代码。 而且,我们需要把账号密码存在某个配置文件中,不是很安全,所以也不建议这样做。 js 链接 + 正则取得图标清单我们换个思路,既然不想登录,但是又要获得图标清单,那就只能从一些公开的资源上去做文章了。 还好,iconfont 提供的 js 链接是公开的,而且这里面也包含了图标信息。 image.png我们发现,这里面有一些特征可以捕捉到,只要把符合vp-icon-前缀的内容提取出来,就能得到图标清单。 话不多说,直接上代码,主要是一些正则的逻辑: importfsfrom"fs" importaxiosfrom"axios" constSVG_ICON_SCRIPT_URL="https://at.alicdn.com/t/c/font_3736402_d50r1yq40hw.js" constSVG_ICON_PREFIX="vp-icon-" functiongetIcons(str){ constreg=newRegExp(`id="${SVG_ICON_PREFIX}([^"]+)"`); returnstr.match(/id="([^"]*)"/g).map((item)=item.replace(reg,"$1")); } exportasyncfunctiongenIconListJson(){ try{ constres=awaitaxios.get(SVG_ICON_SCRIPT_URL); console.log(res) if(res.status===200){ consticonList=getIcons(res.data); console.log(iconList); fs.writeFile(newURL("../src/assets/json/icons.json",import.meta.url),JSON.stringify(iconList,null,2),function(err){ if(err){ returnconsole.error(err); } console.log("图标清单写入成功!"); }else{ console.error(res.status,res.statusText); } }catch(err){ console.error(err); } } genIconListJson(); 执行脚本后,就能得到一个 json 文件了,这里有全部的图标名称。我们特意去掉了图标前缀,因为 IconSvg 组件的 icon 属性只需要简单的名称即可,其内部会与前缀拼接。 image.png根据图标清单实现选择器拿到了图标清单,剩下的工作就比较简单了,无非是把图标循环渲染出来,让用户选择即可。同时提供一个搜索功能,方便在图标数量很大时能够通过名字检索。 代码并不复杂,感兴趣的可以 fork 源码看一下。 结语本文以实际业务中与图标组件相关的衍生需求为背景,介绍了如何封装一个基础组件,以及如何在封装组件时既能在基础组件之上做扩展,同时又不牺牲掉基础组件的原有能力。总的来说,这不仅仅是在讲解如何开发一个组件,更多的是介绍一种通用的上层组件封装思想,希望对大家有所帮助。如果您对我的专栏感兴趣,欢迎您订阅关注本专栏[1],接下来可以一同探讨和交流组件库开发过程中遇到的问题。 [1]https://juejin.cn/column/7140103979697963045:https://juejin.cn/column/7140103979697963045 阅读原文

上一篇:2022-12-13_年底了!一部混剪告诉你,我们为什么需要“合家欢”? 下一篇:2022-01-27_2021年最火研究!开发者们投票给:AlphaFold2、Swin Transformer、MAE以及

TAG标签:

18
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设网站改版域名注册主机空间手机网站建设网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。
项目经理在线

相关阅读 更多>>

猜您喜欢更多>>

我们已经准备好了,你呢?
2022我们与您携手共赢,为您的企业营销保驾护航!

不达标就退款

高性价比建站

免费网站代备案

1对1原创设计服务

7×24小时售后支持

 

全国免费咨询:

13245491521

业务咨询:13245491521 / 13245491521

节假值班:13245491521()

联系地址:

Copyright © 2019-2025      ICP备案:沪ICP备19027192号-6 法律顾问:律师XXX支持

在线
客服

技术在线服务时间:9:00-20:00

在网站开发,您对接的直接是技术员,而非客服传话!

电话
咨询

13245491521
7*24小时客服热线

13245491521
项目经理手机

微信
咨询

加微信获取报价