这是一篇产品研究文章,不知道标题应该是什么。最近调研了几个国外优秀的开源组件库。一是为以后可能的基础组件库做技术储备,二是跳出国内antd的“舒适区”,看看国外团队对组件库的态度。设计。总结写在前面,是为了方便大家直接看结论。这里我把原本在内网末尾写的结论挪到了开头。(因为从内网来看,大家并不是很想看我是怎么分析源码的,只想看结论,我会怎么做。。。)架构实现大致可以分为以下几类代码架构:单包多组件:以antd为代表,所有组件版本统一;多包多组件:国外的组件库大多采用这种架构,每个组件都希望有一个独立的版本,并且主版本是一致的;组件粒度从组件粒度的角度,大致分为以下几类:粗粒度:以antdModal为例,此类组件的自定义渲染槽以renderprops的形式提供;细粒度:以Radix-ui的大部分组件为例,此类组件以容器+子组件的形式提供,提倡子组件的自由组合,灵活性高;更细粒度:以React-Spectrum组件为例,此类组件不仅在UI层以细粒度的形式提供,还在逻辑上拆分成更细粒度的逻辑钩子;样式系统设计从样式系统设计上大致可以分为以下几类:无样式系统:以rc、headlessui为例,此类组件库不提供任何样式文件,由业务方对外实现;它有自己的设计语言:样式文件:以antd为例,这类组件库的样式以外部样式文件的形式提供给组件,用户可以通过className和style样式在外部覆盖;StyledComponents:以Chakra-ui为例,该类组件库的样式在js中以css的形式内置到组件中,用户可以通过自定义theme和className来覆盖样式;我的看法其实是在我读到Charkra-ui的实现之后,我已经对基本的组件库有了一个粗略的设计。首先从样式系统的角度来说,我们重新创建组件库的目的之一就是为了避免样式污染,那么假设设计语言已经统一了,我们是不是可以在js中使用css的形式更积极地编写我们的组件?接下来从组件结构来看,做过业务组件的同学可能都知道,业务组件简单来说就是针对某个场景的基础组件的组合,在组合的过程中往往只用到其中的一个或几个基础组件.function(不满足甚至恶心的阉割重写),那么在设计基础组件的时候,是不是可以把已经原子化的基础组件设计得更原子化一些呢?(比如现在国外的那些基础组件)那么挑战来了:更原子化的设计是否意味着用户需要在业务代码中写更多的组合逻辑?采用不兼容的antdapi设计,是否意味着存量业务的用户就要吃兼容迁移成本之类的屎?......但我想出了一个解决方案。对于上面的一些问题,我们是否可以设计一个适配层来平滑新组件和antd之间的API差异?适配层根据antd的API封装了“皮”层来来去去,供存量业务使用。那么按照这样的设计,我们的组件库架构是不是是这样的:首先,对于业务组件库,所有的底层都是用新的基础组件进行重构,外部不改变原有的API。对于业务组件库的使用者来说,是没有感知的。对于存量业务,锁定指定版本的antdapi,并提供兼容antdapi的适配层,平滑过渡。对于增量业务,建议直接使用基础组件库进行开发。(当然,适配层的使用我也不知道。。。)(以上仅代表个人观点。。。)后台Ant-Design仓库编写的组件库列表:https://github.com/蚂蚁设计。..官网:https://ant.design/index-cn架构模式:UI实现(antd)+headless实现(rc)UI实现:单仓库,单包,多组件,每个组件都是基于headless组件进行UI和Event封装,根据指定版本导入headless组件;headless实现:多仓库,单包,单组件,MutiRepo,没有样式,只有dom和逻辑;antd最大的特点是UI和dom+逻辑分离,对于组件内部的自定义渲染场景,大多以组件+renderprops的形式存在。以Modal为例:在样式系统上,antd给我们带来了最常见的组件,它有一个styleless文件。Chakra-ui仓库:https://github.com/chakra-ui/...官网:https://chakra-ui.com/架构模式:单仓库、多包、多组件、MonoRepo;Charkra-ui在实现上,Input、Select等基础ui组件与常规组件库没有太大区别。对于Modal、Tooltip、Popover等自定义渲染场景较多的组件,采用可插拔的子组件组合。实现,比如Modal:在实现上,Modal并不是作为真正挂载dom的ui组件,而是作为容器层分发props(通过context实现),真正渲染UI的部分由子组件承载,而每个子组件作为一个实体接收容器层派发的props来处理相应的ui显示和事件。以ModalCloseButton为例:Charkra-ui在样式系统的设计上也很有意思。首先,在css的使用上,Charkra-ui使用了@emotion/styled的StyledComponents。在组件的实现中,通过子包@chakra-ui/system下的工厂函数,将每个原生的dom元素或者其他组件转化为StyledComponents。这里以Input为例:对于StyledComponents需要的基本样式,Charkra-ui提供了主题分包作为组件库的默认样式主题,主题包以JS对象的形式定义了各个组件需要的样式.然后,为了给组件注入默认主题和自定义主题,Charkra-ui提供了Provider来注入主题样式,而StyleComponents的使用也使得我们无法从外部合理地覆盖组件中的样式。Charkra-ui还为我们提供了className的props。MUI仓库:https://github.com/mui/materi...官网:https://mui.com/zh/架构模式:单仓库、单包、多组件;在组件的实现上和Charkra-ui很相似,样式系统中也使用了基于@emotion/styled的StyledComponents。Headless-ui仓库:https://github.com/tailwindla...官网:https://headlessui.dev/架构模式:单仓库,单包,多组件;Headless-ui其实相当于react-component(rc),只关心dom+逻辑实现,UI样式实现交给用户;Radix-ui仓库:https://github.com/radix-ui/p...官网:https://www.radix-ui.com/架构模式:单仓库,多包多组件,MonoRepo;与Charka-ui相比,Radix-ui在最基础的数据响应组件Select中也采用了可插拔的子组件,也就是说,在Radix-ui的实现中,子组件的粒度更轻,更原子化。以Select为例:在实现中,容器层也通过context收集props,子组件作为UI响应容器层分发的props。同样,Radix-ui也是一个提倡无样式的组件库。React-Spectrum仓库:https://github.com/tailwindla...官网:https://react-spectrum.adobe...架构模式:单仓库、多包、多组件、MonoRepo;React-Spectrum中的实现由三部分组成:React-Aria:将组件行为、可访问性、国际化等可重用逻辑分离出来,以钩子的形式提供;React-Stately:将组件内部使用的部分state拆分成hooks,用于逻辑复用;React-Spectrum:结合React-Aria、React-Stately、DOM实现基础组件;与上述组件库相比,React-Spectrum不仅将UI端的组件细粒度的拆分成子组件,还在逻辑端拆分出React-Aria和React-Spectrum,提供hooks实现逻辑拆分和解耦。同样,子组件的解耦也是通过上下文来实现的。在样式系统上,React-Spectrum不建议用户通过classNames或styles来实现覆盖样式的逻辑,所以我们在组件上看不到这两个props。React-Spectrum认为,由于样式覆盖,后续的组件升级会带来不可预见的问题。
