Taro多端组件库的设计与实现

6,955 阅读10分钟

本篇文章基于业务中搭建多端组件库的经验撰写

image.png

一、背景

1.1 背景

  • 团队业务:C端商城,需要支持小程序、H5
  • 团队技术栈:Taro+React多端 打怪过程如下: image.png step1: 部门刚成立的时候,业务跑的很快,从0-1建设多端商城,给我们搞技术的时间几乎没有,所以一上来,我们选用了taro-ui组件库,使用下来我们发现,好用的组件库很少,且问题很多,官方也停止更新了,最后一个版本在20年4月份

step2: 为了满足业务,我们开始直接在业务中直接写组件,暴露的问题自然更明显:组件难复用、难扩展

step3: 所以在21年10月,当我们又新增了两块多端商城的业务时,可预见一个组件库来帮助团队提高开发效率是很有必要的,所以我们趁着做业务的同时自建组件库,并与21年12月发布了第一版(同事当时写的文章)

step4: 时隔一个季度,我们在技术实现了一些突破,比如用 hook 代替 class,用 css变量 全量替换 less变量,新增ConfigProvider全局配置组件,支持自定义样式前缀动态切换主题...(在第三部分组件库设计与实现会详细介绍)

1.2 业内Taro-React组件库分析

在开始动手之前,我们搜索了全网Taro-React的组件库 (缺少搭建组件库的经验只能多看别人的!!!)

image.png

二、组件库介绍

Alt

定位:一套基于 Taro 框架开发的多端 React UI 组件库

Tard 取名自 Taro React Design 简写,发音特的

先贴几张组件库效果图

  1. 组件库官网 image.png

2.2 组件示例

image.png

2.3 组件分类

目前31个组件(都是我们业务中用到的)+ ConfigProvider全局配置组件,分为6类,具体可见下图 image.png

组件库更多可见官网 url:tard-ui.selling.cn/introduce

三、组件库设计与实现

image.png 作为一个组件库,最基本需要包括三个部分

  • 组件:npm包,业务真正使用的
  • demo:组件库h5、小程序demo;组件的使用效果
  • 文档:组件库官网和文档,告诉别人怎么用

3.1 组件库规范

开始动手写代码之前,一定要制定一套规范,除非一个人写组件,不然多个人最后的组件库写出来可能五花八门;

开发过程中我们也经常因为意见不合,争吵过很多次,讨论后都达成一致,这是一个不容易的过程;所以尽可能的提前制定规范,不然中途来改成本很大,毕竟谁也不想反复修改代码

3.1.1 代码管理方式

1、monorepo

packages包括以下三块

  • ui:组件库
  • sites:组件库官网和文档
  • demo:包包括h5、小程序demo image.png 2、ui(组件库)代码组织

我们研究了其他各个组件库怎么组织组件的代码,一个组件包括的文件有组件的类型ts文件样式文件demo使用文档;最终决定将一个组件涉及的代码集中在一个文件夹下,一定程度上避免重复切换文件夹,提高开发效率

image.png

3.1.2 组件设计规范

1、组件编码

统一使用Hook+TypeScript

2、组件命名

大驼峰,多组件以.的形式命名,对外一个组件只暴露一个组件,如下

  • 按钮:Button
  • 下拉菜单:DropdownMenu,DropdownMenu.Item
  • 对话框:Modal,Modal.Header,Modal.Content,Modal.Action

3、组件API

  • 每个组件最外层需要包一个CompContainer组件,来实现组件有一些通用的参数,如下
    • className
    • style
  • 不同组件相同类型参数命名统一,比如
    • 元素的文字内容:text
    • 是否展示遮照:overlay
    • ... 4、组件样式 less+BEM+css变量
  • 使用统一前缀(默认tard),效果如下
    • css:@--css-prefix: tard
    • js:cssPrefix(){return 'tard'}
  • 遵循BEM规范
  • 样式变量:分为以下两种,组件变量依赖基础变量,提供更友好的方式修改组件样式,而非传统的强制改样式
  • 多端兼容
    • :root, page{...}

css变量h5挂在 :root 下可生效,小程序需挂在 page 下生效

image.png

3.2 自定义样式前缀

可以调用 ConfigProvider.config 配置方法设置组件的class前缀名,默认是 tard,比如下面我们设置为 custom

// 入口js文件
import { ConfigProvider } from 'tard'
ConfigProvider.config({
    cssPrefix: 'custom'
});

同时需要配置样式文件前缀名,在入口样式文件按如下配置

@--css-prefix: 'custom'

注意:ConfigProvider.config 配置的 cssPrefix 一定要与样式配置的 @--css-prefix 一致,否则class名与style不对应,样式失效

3.3 定制主题

主题定制作为组件库最基本的功能必不可少,分为两类

  • 编译时定制主题;这类最常见,基本业内组件库都支持,一般通过自定义样式变量覆盖,或者结合打包工具比如webpack配置loader处理
  • 运行时定制主题:一般是前端提供集中模式供用户切换或者通过接口拿主题数据;前者业内常用的方案有前端内置几套样式变量去切换,而后者一般是装修场景下的需求,我们业务涉及到这块,因此我们提供了 ConfigProvider实现运行时切换,具体可见3.3.2

3.3.1 自定义CSS变量

Button 组件为例,官网文档可以查看组件的样式,可以看到 Button 类名上存在以下变量

:root, page {
  --button-height: 76px;
  --button-default-v-padding: 40px;
  --button-min-width: 192px;
  --button-min-width-mini: 120px;
  --button-height-mini: 32px;
  --button-mini-text-size: 24px;
  --button-mini-v-padding: 6px;
  --button-min-width-small: 144px;
  --button-height-small: 56px;
  --button-small-text-size: var(--font-size-base);
  --button-small-v-padding: 24px;
  --button-min-large-width: 360px;
  --button-large-height: 96px;
  --button-large-text-size: var(--font-size-lg);
  --button-large-v-padding: 48px;
  --button-radius: var(--border-radius-md);
}

(1) CSS 覆盖

你可以直接在代码中覆盖这些 CSS 变量,Button 组件的样式会随之发生改变:

:root, page {
    --button-radius: 10px;
    --color-primary: 'green'
}

这些变量的默认值被定义在 root 节点上,HTML 文档的任何节点都可以访问到这些变量

这也是编译时定制主题常用的解决方案

(2)通过 ConfigProvider 覆盖

ConfigProvider 组件提供了覆盖 CSS 变量的能力,你需要在根节点包裹一个 ConfigProvider 组件,并通过 style 属性来配置一些主题变量

这使得可以在运行时动态的更改主题

 // 比如 button-radius 会转换成 `--button-radius`
<ConfigProvider style={{ 'button-radius': '0px', 'color-primary': 'green' }}>
  <Button className="button-box__item" type="primary">主要按钮</Button>
</ConfigProvider>

注意:ConfigProvider 仅影响它的子组件的样式,不影响全局 root 节点

3.3.2 运行时动态主题

在官网ConfigProvider 全局配置下可以体验在线动态切换主题 image.png 调用 ConfigProvider.config 配置方法设置主题色

ConfigProvider.config({
    theme: {
        'color-primary': 'purple',
        "button-radius": '20px'
    }
});

3.4 文档实现

image.png

3.5 打包实现

image.png

四、组件库建设经验

这一部分主要写组件库建设经验,如果说上面是技术上的经验,这部分是人跟事上的经验,毕竟一个组件库要做出来并能持续扩展还真不容易,甚至说比技术更难

4.1 诞生之路

其实刚开始做这个组件库过程很艰难,没有专业的UI支持;一方面UI资源紧张,另一方面团队没有相应的KPI是建设组件库,组件库仅仅是用来满足业务的

我们负责封装组件给其他同事用,写业务的同事组件催的急,我们是利用业务的设计稿中去实现的组件,比如业务设计稿中下拉菜单张这样,那我们就按照业务设计稿实现,没有办法...

但是组件库脱离了UI很难帮助业务,毕竟设计稿是UI给到前端的;难道就满足这一个业务,后面还有类似的场景呢?所以导致前期组件库处于一个不规范的局面

转机出现在,在写业务的过程中我们发现同一个业务的不同页面设计搞不统一,比如按钮圆角、筛选列表的筛选条件...因为我们将其封装成一个组件,那么相应的样式肯定是相对固定的,因为一个系统的设计风格肯定是需要保持一致的,除了可以通过样式变量更改,所以我们与UI同学讨论,在讨论的过程中,我们透露了组件库的思想,视觉规范一致的必要性,可以提高前端开发效率、UI设计效率

然后有一位UI同事比较感兴趣,于是我们针对聊了很多,私下花时间专门支持我们,官网也是UI同事帮忙设计的

4.2 寻找突破

中途我们尝试跟主管沟通过组件库没有UI,能不能协调下我们做的组件库需要UI支持,但是目前没有UI能支持,且重点是满足业务,所以组件库并没有投入资源

兴趣使然或者说出于对技术的学习心态吧,我们3个前端+一个UI一心想把组件库给做好,于是我们开始借鉴业内组件库的技术实现,设计语言与前端代码如何结合

就好比设计给出了一个按钮的样式,她得知道这个按钮元素应该包括哪些内容(高宽、高度、字体、内间距等),这些就是设计规范,那这些规范对应的值就是设计语言。再更深层次的设计这些内容背后的逻辑是什么,规范的原理什么的就可以抽象出设计原则

这里的设计语言对应到前端也就是样式变量

UI同事开始给每个组件出稿,每个组件不同形态都有设计;前端开始制定规范,实现组件的同时思考API、样式变量定义的是否规范,组件的demo、文档都补充起来,并进行code review

制定了每周开会机制,一个组件从设计稿设计、前端开发、code review、UI走查到达到要求的路径,如下 image.png

开始设计组件库的官网,想把组件库做大最强,真正帮助大部门多端的业务(梦想还是要有的)

大家热火朝天,虽然频频争吵,好不快乐!

我们深知组件库的视觉规范需要大部门都认同的前提下,才能发挥作用,于是当我们组件库做的有模有样了之后,我们将我们的成果汇报给了主管,希望主管能协调去大部门的UI与前端,统一规范

这些汇报(或者说组件库的未来规划)也算是我们在做组件库的时候,对组件库(或者说更高一层C端视觉规范统一)价值整体的一个认知吧,这些也是我们参考了很多资料总结的,大概如下

可能写的都是比较理想的,大家可以理性参考...这当初的确是我们的目标,不过现在实在没时间 image.png image.png 未来规划 image.png image.png image.png

4.3 歧途

本来技术能真正帮助业务是一件好事,理想是美好的,现实的艰难的,Q1后期吧,可能资源协调确实困难,加上组织架构频频调整,视觉统一共建的事情迟迟难以落地,所以说目前组件库确实满足业务就是最好的选择

写在最后

写这篇文章以及组件库开源的原因是希望我们踩过的坑能给大家一些经验,以及如果你对开源感兴趣可以加入我们一起打造组件库,或者需要taro-react多端组件库的话可以尝试使用tard

GitHub地址: github.com/jd-antelope…