乐趣区

关于前端:全新的-React-组件设计理念-Headless-UI

作者:彭瑞(皮尔)

其实,最早接触 Headless UI 是在去年,碰巧看到了一个十分前沿且优良的组件库 —- Chakra UI,这个组件库自身就是 Headless UI 的实践者,同时也是 CSS-IN-JS 的集大成者。

我过后看过之后,就对该理念产生了很大的趣味,同时工作中也正好有机会实际(着手公司开源组件库大版本重构),因而对该理念也有肯定的实践经验。

那么明天,也是想和大家分享介绍下这项还算前沿的技术,另一方面是也算是集体的一份技术总结,这里也心愿感兴趣的小伙伴能够在评论区探讨。

契机:React Hooks 的诞生

React Hooks 能够说是 Headless UI 得以实现的基石,为什么这么说,这里咱们首先聊聊 React Hooks。

React Hooks 是什么

咱们都晓得,React Hooks 是在 V16.8 版本诞生了,是它让咱们的函数组件真正领有了状态。如下图,咱们以数字累加这个性能举例,能够看到对于同样的性能,React Hooks 的写法绝对于过来类组件的写法从代码上会缩小一丢丢。

但仅仅是因为如此才反对它吗?

咱们要晓得,在 React v16.8 之前,个别状况下,一般的 UI 渲染间接应用函数组件就好,须要应用 state 或者其余副作用之类性能时,才会应用类组件。

两者分工也算正当,那么 hooks 的诞生又是为何?仅仅是为函数组件赋能吗?从使用者的角度来说,这显然说不过去,徒增了学习老本不说,还多了一个纠结选项(函数组件 vs 类组件)。

React Hooks 的意义

所以,事件并没有那么简略。咱们能够推断,对于 hooks 它必定解决一些“类组件存在的有余或痛点”,这里就不卖关子,列举 2 点:

1、状态逻辑在组件之间难以复用

在过来,状态逻辑的复用往往会采纳高阶组件来实现。但劣势也非常明显,须要 在原来的组件外再包裹一层父容器。 导致层级冗余,甚至嵌套天堂,引来了很多吐槽点:

  • 加强调试的难度
  • 拉低运行的效率

置信应用 Redux 的同学都晓得,为了疾速状态治理到组件的注入,会应用 connect 对组件进行包裹,然而随着我的项目迭代,关上 DevTools 查看时发现 DOM 往往臃肿不堪。

2、简单组件变得难以了解和保护

简单组件自身就很简单,然而类组件让其变得更加难以了解和保护。比方:在一个生命周期函数中往往存在 不相干的逻辑混淆 在一起,或者 一组相干的逻辑扩散 在不同的生命周期函数中,这里别离举个例子:

  • componentWillReceiveProps 中往往写入不相干 props 更新渲染的判断逻辑,对于一次更新,往往会多出一些有效的执行,拉低执行效率
  • componentDidMount 中注册事件,在 componentWillUnmount 中卸载该事件,往往容易遗记甚至写出 bug。

长此以往咱们的代码只会变得蹩脚难懂。

React Hooks 对组件开发的影响

通过 React Hooks,咱们能够把组件的状态逻辑抽离成自定义 hooks,相干的逻辑放在一个 Hook 里,不相干的拆分成不同的 hook,最终在组件须要时引入,从而实现状态逻辑在不同组件之间复用。

正是因为 React Hooks 的诞生,使 Headless UI 组件在技术上成为可能,这也是它为什么最近才开始风行的起因。

所以,接下来咱们介绍下什么是 Headless UI。

什么是 HeadLess UI

Headless UI 的定义

Headless UI 目前社区还在摸索实际阶段,这里我对它做了个简略定义:Headless UI 一套基于 React Hooks 的组件开发设计理念,强调只负责组件的状态及交互逻辑,而不论标签和款式。 其本质思维其实就是 关注点拆散:将组件的“状态及交互逻辑”和“UI 展现层”实现解耦

Headless UI 组件

从实体上看,Headless UI 组件就是一个 React Hook。

从表象上来看,Headless UI 组件其实就是一个什么也不渲染的组件。

为什么会有 Headless UI

那么咱们为什么会须要一个啥也不渲染的组件呢?

这里咱们还是以数字加减这个性能举例,先思考设计实现一个数字加减器 Counter 组件。

传统版组件的设计痛点

依照传统的模式,咱们可能会间接去编写导出一个名字叫 Counter 组件,而后应用上间接渲染它即可,对于组件的性能通过 props 设置,比方非受控初始数字值。

那么这么做有什么满足不了的痛点呢?咱们这里轻易举个场景,而后别离来从 组件的使用者、维护者以及服务的产品 三个角度来剖析下。

1. 使用者 – 高定制业务场景如何实现满足?

当初咱们业务有这样的诉求:左右两个加减按钮要求反对长按后悬浮展现 Tooltip 提醒。

其实从产品角度这个需要很浮夸,晋升交互体验嘛。然而如果依照之前传统的组件设计,那就头疼了。它一整个都是组件库外面裸露进去的(假如哈),怎么去侵入到外面给加减按钮加 Tooltip 呢?

其实,对于组件这样定制业务场景的诉求,咱们个别解决思路可能是这样:

然而随着计划越往后抉择,咱们的代价是越来越高的,脸上的苦楚面具也越来越显著。

2. 维护者 – 组件 API 日趋简单,性能扩大 & 向下兼容的苦恼?

对于维护者而言,如果要去满足这样的诉求,那么他可能会这么做。

一开始,需要比较简单,咱们能够通过新增 API 动静注入要实现的性能,对于下面的诉求,咱们可能会新增 xxxButtonTooltipText 之类的 API 来实现 Tooltip 文案的配置;

一周后,又须要加减按钮反对 Icon 自定义,咱们可能会增加 xxxButtonText 之类的 API 来满足;

又过了 2 周,咱们又想反对 Tooltip 展现方位配置,防止遮挡核心内容展现,咱们可能会增加 xxxButtonTooltipPlacement。。。

日复一日,组件 API 数疾速扩大,最初,维护者发现切实忍耐不了了,决定尝试应用 Render Props 设计,以此一劳永逸,于是新增了 xxxButtonRender 反对加减按钮自定义函数渲染。

咱们发现,通过这么做,一个简略的组件变得日趋简单,不仅仅存在性能冗余实现,而且前面还要思考性能扩大以及向下兼容,脸上的苦楚面具也逐步显著。

另外,对于使用者,当想应用一个组件发现有几页的 API 数量时,也会浅叹一声,性能难以检索到,而且大部分可能都不须要,面对性能优化也难以动手。

3. 产品:如何疾速打造好用定制的品牌 UI?

对于一个产品,最重要的一点就是塑造产品自身的品牌形象和产品特色。对于用户最间接接触的 UI 交互,那更是尤为重要。那么 如何疾速打造好用定制的品牌 UI 呢?

还是以数字加减器举例,那么,它的 好用 可能体现在它具备较为欠缺且好用的能力。

  • 点击加减按钮:数字加减步长
  • Accessibility 可拜访性
  • 数字值最大最小值管制
  • … …

对于它的 定制,可能体现在它 UI 视图层上的差异化。如下图,仅仅是 Counter 这种小组件,就有形形色色的 UI 状态,更别说其它更简单的组件了。

Headless UI 的解法

从下面的剖析咱们能够看到,UI 是一个自由度十分高的玩意,而构建 UI 又是一种十分品牌化和定制化的体验。

那么,咱们能不能 只需复用组件的交互逻辑,布局和款式齐全自定义 呢?显然,Headless UI 就是干这件事件的。

对于 Headless UI 组件,咱们要做到第一件事,就是剖析和抽离组件的状态以及交互逻辑。对于 Counter 组件,它的状态逻辑大抵如下:

咱们把这些状态逻辑收敛到一个叫 useCounter 的 React Hook 中。它接管用户传入的性能 API 设置,而后返回一套已解决过的全新 API。

对于用户而言,咱们只需把返回的 API 赋予到想赋予的标签上,那么就失去了一个 只带交互能力的无头组件。

最初,咱们联合设计稿进行 UI 还原,对编写自定义款式,最终就能实现一个全新数字加减器组件了;

另外,咱们还能够将标签从新排版,而后款式改吧改吧,将按钮相对定位一下,最终就能实现一个数字输入框组件;

除此之外,咱们还能够基于它封装,比方本来的最大值示意总页数,插入到标签两头,款式再改吧改吧,就能实现了一个迷你版的分页器组件了。

能够看到,通过 Headless UI 的设计思路,咱们最终产出了一个叫 useCounter 的 React Hook,通过它,咱们不必关怀组件最为简单且最通用的局部 —- 交互逻辑,而是把它交给组件维护者治理;而对于常常变动须要定制的 UI 局部齐全由咱们自由发挥,从而实现最大化地满足业务高定制扩大的诉求,同时,也尽可能实现代码的充沛复用。

Headless UI 的优与劣

这里咱们简略梳理下 Headless UI 的劣势和劣势,以及目前倡议的实用场景,不便大家做技术选型和学习。

劣势

  • 有极弱小的 UI 自定义施展空间,反对高定制扩大

能够看到 headless 的劣势也非常明显,因为它更形象,所以它领有十分弱小的 定制扩大能力:反对标签排版、元素组合,内容插入、款式定义等等都能满足。

  • 最大化代码复用,减小包体积

从下面能够看到,组件的状态逻辑能够尽可能达到最大化复用,帮忙咱们减小包体积,加强整体可维护性。

  • 对单测编写敌对

因为根本都是逻辑,对于事件回调、React 运行治理等都能够疾速模仿实现单测编写和回归;而 UI 局部,个别容易变动,且不容易出 bug,能够防止测试。

劣势

  • 对开发者能力要求高,须要较强的组件形象设计能力

抽象层次越高,编写难度越大。对于这样 headless 组件,咱们关注的组件 API 设计和交互逻辑抽离,这十分考验开发者的组件设计能力。

  • 应用老本大,不倡议简略业务场景下铺开应用

UI 层齐全自定义,存在肯定开发成本,因而须要评估好投入产出,对于没有特地高要求的 2b 业务的话,还是倡议应用 Ant Design 这样自带 UI 标准的组件库进行开发。

Headless UI 的生态与瞻望

社区生态

对于组件,目前在国外曾经有些摸索和实际的案例,比方 React-Popper、React-Hook-Form、TanStack-Table,三个是组件库“三大难”,它们 stars(均上万)和活跃度都十分高,将来基于 headless UI 设计实际的组件只会越来越多。

对于组件库,我目前看到的比拟不错的实际就是 Chakra-UI 组件库,整个组件库采纳分层架构(这里以数字输入框组件为例):

  • 底层 应用 Headless UI 那一套模式,对外裸露相干的 React Hook,保障整个组件的高定制扩大的诉求能失去最大化满足。
  • 下层 则提供了相似于 Ant Design 这样的组件,自带默认的 UI,但不同的是每个组件都是由颗粒度更小且必要的原子组件形成,能够间接引入它们应用,这样又 保障大部分简略或一般的场景能够疾速实现并满足。

留神:其实一个组件拆分成多个必要的原子组件形成,其实也算是 Headless UI 的一种实际状态,把交互逻辑失效的 API 间接绑定在必要的元素标签上,而后以原子组件裸露进去,标签的排版和款式批改也齐全能够由用户自定义。

另外,在 React Next 2022 大会上,也有嘉宾分享介绍 Headless UI 相干的理念,整个社区目前都处在继续发酵的阶段。

将来瞻望

集体认为 Headless UI 是将来 React 组件库底层的最佳实际。

对于组件库而言,可能大家都不须要“读书借鉴”了,而是都应用同一套组件底层的状态以及交互逻辑,而在 UI 层以及细节上再进行品牌、场景定制化扩大。

总结

那么,以上就是对于 headless 设计理念的全部内容。通过 Headless UI,咱们能够疾速复用组件的状态以及交互逻辑,对于布局和款式实现齐全自定义

另外,Headless UI 是一个组件库设计的新思路,也是将来组件库必然的趋势。对于前端同学而言,学习理解它也显得尤为重要。

值得一提的是,在日常开发中,咱们也能够尝试借鉴这样的思路,将通用状态逻辑抽离进来,不便复用,帮忙咱们在日常开发提效 。比方:常见的筛选过滤、分页申请列表数据的逻辑等;甚至,咱们还能够将业务逻辑同 UI 交互进行抽离,比方:在 多端场景(Web PC 端、小程序端、RN 端)下复用同一套业务逻辑代码,实现业务逻辑复用和对立,以此大大提高咱们的生产力

退出移动版