基于react的组件库主题设计方案
需求背景
单一的视觉不再满足用户体验需求,为提高用户体验,提高应用体验口碑,同时提高开发者效率,我们希望提高组件库的可定制化,因此提供换肤功能以及多种类组件中的样式定制功能,允许用户将应用切换不同主题风格的皮肤,也允许开发者对指定组件进行样式改造。
设计目标
- 性能 一个方案的落地前提得有性能的保障,不重新初始化视图,避免出现闪屏、卡顿等性能缺陷现象,同时也要保障功能稳定,不能存在部分组件不按预期切换主题现象。
- 可维护性 组件库需不断迭代完善,应避免过多的条件判断,避免在单个组件上有过多的主题特殊逻辑,主题的设置和组件的实现应解耦,保证后续可维护可扩展。
- 可配置 可配置分为两部分,一部分为可配置任意全局统一的样式变量,或者某个组件的局部样式;另一部分为强制模式,即指定部分组件不跟随主题变化而变化,保留着本身一种样式。
- 易用性 提供快捷接入主题的接口,降低学习成本和时间成本。
- 粒度细分 组件层面的主题定制、整套组件库的主题定制。开发者可以修改全局样式,比如更换全局中字号的字体大小,也可以局部修改样式,比如按钮组件A的边框颜色。
- 样式提取 暴露出提取当前整套样式的接口,方便开发者提取指定样式做二次操作。比如开发者需要提取当前主题颜色作为视图背景色,可从组件库中获取。
- 样式可定制内容,包括但不限于: 颜色:品牌色、默认背景色、通用背景色、基本文本颜色、辅助文本颜色、链接色 文本:文本大小,字重,字体间距等 按钮:圆角大小,按钮尺寸,边框尺寸等 图片:图片尺寸,圆角大小等
技术选型
主题定制是大多数组件库都会提供的一个核心样式相关的功能,技术选项上需要考虑的两点:
- 如何生成一份全局样式配置表
- 组件如何获取样式配置表
针对以上两点,我们做了一些分析:
如何生成一份全局样式配置表
目前各类组件库最常用的是以下两种方案:
- 借助gulp/webpack等打包工具相关的插件,配置需要定制的样式变量,在打包时覆盖对应变量值。
- 重写样式,覆盖样式配置表,生成新的全局样式配置表。
在我们实现的hippy-react-ui中我们并没有提供打包的能力,而是把这部分移交到业务侧处理,原因是现在大部分业务发布时都会对业务进行打包处理,业务侧可能灵活设置打包配置内容,而不受限于组件库打包,另一方面是让业务侧使用组件时可以快速定位组件内部结构,方便排查使用过程中遇到的问题。因此我们选用了以上第二种方案,提供一份默认的样式配置表,而业务侧可以写入重新样式对其覆盖。
组件如何获取样式配置表
组件库是基于hippy-react设计开发的,hippy-react提供的数据的传递有两种:
- 通过 props 属性自上而下(由父及子)进行传递
- Context 提供了一种在组件之间共享值的方式,不必显式地通过组件树的逐层传递 props
第一个方案使用简单,只需要将样式从根节点往下一层层传递即可,但它的缺点也是需要一层层传递。我们的组件库中,复合组件很多,比如列表组件中用到了按钮组件,按钮组件中用到了文本组件,这要求每个组件都需要获取一遍props再往下传递,不仅加大开发成本,对影响了后续开发的可维护性。而第二个方案,我们只需要使用context提供主题的提供者和消费者,在需要使用主题的组件中注入即可,但它有个缺点:每次更新context的容,都会将所有消费到主题的组件重新更新一遍。而针对context的缺点,我们可以放下这个顾虑,因为主题本身也是只消费一遍,在切换主题的时候进行消费,而不是高频的去使用。因此组件获取样式配置表是通过context的方式进行获取。
设计方案
通过上面技术的选型,我们确定了两点:
- 重写样式,覆盖样式配置表,生成新的全局样式配置表
- 组件通过Context提高的组件之间共享值的方式,获取样式配置表
生成样式配置表
以上是生成全局样式表的过程,在讲解流程前需补充说明上图中深色/浅色主题:组件库内置两份主题色,主题的切换主要是颜色部分的切换,提供两种主题的原因是我们尽可能通用化配色,比如以下几个例子,背景色/背景图片我们可以随意替换,但作用在其之上的内容,简单分为深/浅两种方案基本可以适用到大部分场景。
样式优先级
组件库自带的样式分为三部分:跟主题相关的深色主题
和浅色主题
,还有与主题切换无关的其他样式
, 在业务侧未指定主题时,组件库默认使用浅色主题的颜色配置表+其他可配置的默认样式值,如字体大小,字重等,业务侧可以重写样式,最终生成的样式表作为提供者Provider给到各个组件使用。
我们暴露一个属性value={}
给业务侧赋值给组件库,业务侧可以在对象中传入指定的主题,比如value={theme: "light"}
或者 value={theme: "dark"}
,我们提供一个便利,业务侧可以直接传入value="light"
或value="dark"
。如果希望针对某个样式值进行重写,可以 value={textBaseColor: "#555555"}
。
在组件库中,我们根据业务侧传入的自定义内容进行判断且合并成新的样式配置表:
- 备库报警邮件的分析案例(二) (r7笔记第15天)
- Gotorch - 多机定时任务管理系统
- 备库报警邮件的分析案例(三)(r7笔记第16天)
- 简单易学的机器学习算法——神经网络之BP神经网络
- 24(02)多线程锁,线程通讯,线程组,线程池,多线程三种方式,匿名内部类,定时器,设计模式,单例模式,Runtime
- Go代码打通HTTPs
- 一个简单的MySQL参数导致的连接问题解惑(r7笔记第33天)
- [基础篇]Go语言变量
- [转载]Golang 编译成 DLL 文件
- [转载]Go JSON 技巧
- 简单易学的机器学习算法——Rosenblatt感知机的对偶解法
- Spring-拾遗
- Golang面试题
- 简单易学的机器学习算法——基于密度的聚类算法DBSCAN
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 骚操作 | 用 Python 实现 GIF 倒放
- TensorFlow学习笔记--CIFAR-10 图像识别
- TensorFlow学习笔记--自定义图像识别
- TensorFlow学习笔记--Deep Dream模型
- Python入门系列第二章--第一节:变量、字符串与数字
- JavaWeb - Filter 和 Listener
- Python入门系列第一章--第二节:我的第一个Python项目
- Python入门系列第二章--第二节:注释
- JavaScript进阶教程(4)-函数内this指向解惑call(),apply(),bind()的区别
- 第三章--第一节:条件判断语句
- 用 Python 实现朋友圈中的九宫格图片
- 第三章--第二节:循环语句
- 第三章--第三节:列表
- 第三章--第三节(补充):列表排序
- 第三章--第四节:字典