Written for the "future" base component library

This is a competing research article that I don't know what the title is. Recently, I have investigated several excellent foreign open source component libraries. One is to make a basic component library for technical reserves in the future, and the other is to jump out of the "comfort zone" of domestic antd and see how foreign teams are doing for component libraries. Designed.

written summary

In order to make it easier for everyone to see the conclusion directly, here I have moved the conclusion that was originally written at the end of the intranet to the beginning. (Because from the intranet, people don't really want to see how I analyze the source code, but just want to see the conclusion and what I will do...)

Architecture implementation

From the perspective of code structure, it can be roughly divided into the following categories:

  • Single package and multiple components: Represented by antd, all components have a unified version;
  • Multi-package and multi-component: Most of the foreign component libraries adopt this structure, each component wants to have a separate version, and the major version is consistent;

component granularity

In terms of component granularity, it can be roughly divided into the following categories:

  • Coarse granularity: Take antd Modal as an example, the custom rendering slot of this type of component is provided in the form of render props;
  • Fine-grained: Take most of the components of Radix-ui as an example, such components are provided in the form of container + children sub-components, which advocates the free combination of sub-components and is highly flexible;
  • Finer granularity: Take React-Spectrum components as an example. Such components are not only provided in a fine-grained form at the UI layer, but also logically split into finer-grained logic hooks;

style system design

From the perspective of style system design, it can be roughly divided into the following categories:

  • No style system: Take rc and headless ui as examples, such component libraries do not provide any style files, and are all implemented externally by the business side;
  • Has its own design language:
    • Style file: Taking antd as an example, the style of this type of component library is provided to the component in the form of an external style file, and the user can override the style externally through className and style;
    • StyledComponents: Take Chakra-ui as an example, the style of this type of component library is built into the component in the form of css in js, and the user can override the style by customizing the theme and className;

My point of view

In fact, when I read the implementation of Charkra-ui, I already had a rough design of the basic component library in my heart.

首先从样式系统来讲,我们重新做组件库的一个目的就是为了避免样式污染,那么假设设计语言已经达成统一的前提下我们是不是可以激进一点的采用 css in js 的形式去编写我们的组件?

接下来从组件的结构来说,做过业务组件的同学可能都知道,业务组件简单来讲就是针对某个场景组合基础组件,而组合的过程中往往只用到基础组件的某一或几个功能(甚至不满足的时候还得很恶心的阉割重写),那么再设计基础组件的时候,是不是可以将本就原子化的基础组件更加原子化的设计?(例如现在国外的那些基础组件)

那么挑战就来了:

  • 是不是更加原子化的设计就意味着使用方需要写更多的组合逻辑在业务代码里?
  • 采用不兼容 antd api 的设计是不是意味着存量业务的使用方吃屎般的兼容迁移成本?
  • ...

不过我倒是想出了一种解决方案,针对上面一些问题,我们是不是可以设计一个适配层来磨平新组件和 antd 之间的 api 差异,适配层根据 antd 的 api 封装一层“皮”来去给存量业务去使用。

那么按照这样子设计,我们的组件库架构是不是就是这个样子:

image.png

首先对于业务组件库,底层全部使用新的基础组件重构,对外不改变原有 api。而对于业务组件库的使用方来说,无感知。而对于存量业务,锁死指定版本的 antd api,并提供兼容 antd api 的适配层来平稳过渡,对于增量业务,建议直接使用基础组件库开发。(当然使用适配层我也不知道...)

(以上只代表我个人的观点...)

写在后面的组件库一览

Ant-Design

  • 仓库:github.com/ant-design/…
  • 官网:ant.design/index-cn
  • 架构模式:UI 实现(antd)+ headless 实现(rc)
    • UI 实现:单仓库、单包、多组件,每个组件基于 headless 组件做 UI 和事件封装,按指定版本引入 headless 组件;
    • headless 实现:多仓库、单包、单组件,MutiRepo,无样式,仅实现 dom 和逻辑;

antd 最大的特点就是 UI 和 dom + 逻辑分离,对于组件内自定义渲染的场景,多以组件 + render props 的形式存在,以 Modal 为例:

image.png

在样式系统上,antd 带给我们的是最常见的一个组件配合一个样式 less 文件。

Chakra-ui

Charkra-ui 的实现上,对于 Input、Select 等基础的 ui 组件和常规的组件库没什么太大差异,而对于 Modal、Tooltip、Popover 等存在较多自定义渲染场景的组件,采用可插拔的子组件组合的形式实现,例如 Modal :

image.png

在实现上,Modal 不作为实际挂载 dom 的 ui 组件,而是作为容器层分发 props(通过 context 实现),而实际渲染 UI 的部分以子组件来承载,每一个子组件作为一个实体接收容器层派发的 props 处理相对应的 ui 展示和事件,以 ModalCloseButton 为例:

image.png

在样式系统的设计上 Charkra-ui 也比较有意思,首先在 css 的使用上,Charkra-ui 采用基于 @emotion/styled 的 StyledComponents。在组件的实现中,通过子包 @chakra-ui/system 下的 factory 函数为每一个原生 dom 元素或者其他组件转换成 StyledComponents。这里以 Input 为例:

image.png

而对于 StyledComponents 所需要的基本样式,Charkra-ui 提供了 theme 这个子包作为组件库默认的样式主题,主题包内以 JS 对象形式定义了每一个组件所需要的样式。

image.png

然后,为了让默认主题和自定义主题注入到组件内,Charkra-ui 提供了 Provider 来注入主题样式

image.png

image.png

而 StyleComponents 的使用也使得我们无法合理的从外部覆盖组件内的样式,Charkra-ui 也给我们提供了 className 的 props。

MUI

在组件的实现上和 Charkra-ui 很类似,在样式系统中也是使用的基于 @emotion/styled 的 StyledComponents。

Headless-ui

Headless-ui 其实相当于 antd 依赖的 react-component(rc),只关心 dom + 逻辑实现,UI 样式实现交给使用方;

Radix-ui

Radix-ui 相比于 Charka-ui 在 Select 这种最基本的数据响应组件也采用了可插拔的子组件形式,也就是说,在 Radix-ui 的实现上,子组件的粒度更轻更原子, 以 Select 为例:

image.png

在实现上,同样采用容器层通过 context 收集 props,子组件作为 UI 响应 容器层派发的 props。

同样,Radix-ui 也是主张无样式的组件库。

React-Spectrum

React-Spectrum 在实现上由三部分组成:

  • React-Aria:将组件的行为、可访问性、国际化等可重用的逻辑分离并以 hooks 的形式提供;
  • React-Stately:将组件内部使用的一部分状态拆分出 hooks ,以便逻辑重用;
  • React-Spectrum:结合 React-Aria、React-Stately、DOM 实现基础组件;

相比于以上组件库,React-Spectrum 不仅将组件从 UI 侧以子组件的形式细粒度的拆分,还在逻辑侧拆分出 React-Aria 和 React-Spectrum 提供 hooks 来实现逻辑拆分解耦。同理,子组件解耦也是通过 context 形式实现。

在样式系统上 React-Spectrum 不建议使用方通过 classNames 或者 styles 来实现覆盖样式的逻辑,因此我们在组件上看不到这两个 props,React-Spectrum 认为由于样式覆盖会导致后续的组件升级带来不可预料的问题。

image.png

Guess you like

Origin juejin.im/post/7085533349725437989