读读antd源码之通用组件

3,480 阅读6分钟

① ui组件库

如果说ui开发框架是高速的生产线,那么ui组件库就是一成套模具,大大提高了生产效率。

ui组件库无论对设计风格、基础功能、操作交互等都做了高度的统一,为项目开发提供了开箱即用的便捷。在React领域,中后台项目用的较多的ui组件库,非 Ant Design(antd)莫属了。

这里准备对 antd 组件库进行一系列的源码分享,看看我们习以为常的功能背后是做了怎样的逻辑处理。

阅读的过程,会遵循前面分享的《阅读前端源码的思路》,文章忽略具体细节,直接分享重点。

② 组件的逼格

一个没有设计理念的ui组件库,都不好意思说是ui组件库

看下官方文档,可以看到第一个栏目是“设计”,可想而之ui组件库的灵魂就是设计,它包含自身的价值观和模式,也遵循一系列的规范。

再展开一下“组件”栏目,可以看到左侧菜单是对组件进行了分类的,包括有:

  1. 通用组件
  2. 布局组件
  3. 导航组件
  4. 数据录入组件
  5. 数据展示组件
  6. 反馈组件
  7. 其他组件

这篇文章,先分享阅读的第1个,通用组件的源码。

 一个一个来

关注你想关注的

通用组件包含三个:Button,Icon,Typography。

这里先看下文档关于这几个组件,看下有什么值得思考的功能。

可以看到,一些类型,状态等展示的基本根据props来切换,没必要深究。

关于Button,这里感觉有些看点的是点击动作的反馈,外围波浪散开的动效。

关于Icon,可以关注下它内部是如何封装图标的引用。

关于Typography,可以关注下它内容一些文本处理或功能的封装。

④ 说干就干

说实话,antd的组件源码比较乱

拉源码打开components文件夹,可以找到button组件,只有三百行代码,可以完整看一下,先看看前面的引用

omit方法就是剔除对象中某个键值对;

Group就是为了挂载在Button的button-group,可以看完button再看;

Configcontext是一个全局配置的上下文,使用了React.createContext创建上下文然后Consumer来获取,提供了诸如类名前缀配置的功能,基本所有组件都要注入这个上下文,没特别看点;

tuple和devWarning看名字都知道干什么的;

Wave就是我想看的东西,一会再看。

接着往下看,可以看到这个:

上面截图那是判断按钮文字如果是两个汉字,就中间给你插入空格,接着可以看到内容劫持的过程。

一堆类型定义后,可以看到它引入的全局类名前缀,也是意料之中的:

接着看下去,可以看到如果类型是没有边框的文字按钮,它就不给Wave包住了,所以重点还是要看Wave:

⑤ 看下Wave的实现

无非就是transition和animation

很容易找到Wave的代码,容易知道componentDidMount执行的是bindAnimationEvent函数,也可以看到主要对node节点添加click监听注入方法,这些代码都是意料以内的:

这里主要就细看一下绑定的 onClick 方法,可以看到,根本就在于102到111行的 updateCSS 方法,先动态添加一些样式,然后于116到119行动态添加 transition 和 animation :

这里再进去细看一下 updateCSS 方法,需要找到另一个库:react-component/util,github上就是这个文件:github.com/react-compo…,如果我们需要动态注入css文件,可以参考一下。

这里可以看到就是获取容器、注入、缓存等等的内容,基本就是动态创建标签写入css样式,另外这里我关注到它配置的节点的nonce属性,可以参考下MDN:

这是csp范畴的内容,也可以看到它说后面的实现会改成script标签才暴露这个属性。

好了,Button组件看得差不多了。

 看下Icon的实现

icon搬到了@ant-design/icons

antd 的 icon 搬出去一个 @ant-design/icons 库来实现了,可以找到这个github仓库:GitHub - ant-design/ant-design-icons: ⭐ Ant Design SVG Icons。主要看下ant-design-icons/packages/icons-react/src/components下的 AntdIcon.tsx 等文件。

发觉就是一些样式,事件,和 icon 的各种引用处理,没有什么想看的。

各个不同 svg 的 icon 就是基于组件传参,也是意料之中了:

 最后再看下Typography

排版组件是统一了一些内容展示方式,目前看还是比较鸡肋

这里找到文件夹,可以看到,它提供了好几个类型:Text,Link,Title,Paragraph

匹配一系列文本样式的内容就不细看了,这里主要看两个内容吧:一个文本拷贝,一个文本省略处理。

先看下Base/index.tsx里,可以看到文本拷贝就是用了一个库copy-to-clipboard。

那再进去这个库里看下吧:github.com/sudodoki/co…,代码也没有多少行,主要还是用了 document.createRange 和 document.getSelection 方法,和我们自己写的也差不多,不再展开。

再重点关注下文本溢出处理部分,可以看到定义了几个状态值:

然后知道它需要计算溢出部分的条件:

再看下这里可以知道,只有在处理多行文本溢出的时候兼容 webkitLineClamp 或者单行文本溢出的时候才能用定义的css,这也符合我们的认知,处理多行溢出需要考虑兼容性问题,也就是需要用 js 实现。

这里跳过一些代码,直接看到对应Ellipsis组件的使用:

可以知道enableMeasure就是启动内部计算溢出算法,而 width 就是需要知道容器的宽度,而 rows 是设置内容不能超过多少行,具体怎样获取数值传参的代码不用看也知道了。

主要是看它怎么计算溢出的,这里进入Ellipsis组件再看下,可以看到它用了几个状态值来控制渲染流程的:

然后可以看到它先计算文本长度,如果是文本类就直接累加length,不然就一个node节点算1然后累加:

拿到totalLen之后,就开始计算切割的位置,开始、中间、结束,然后设置状态为 PREPARE:

然后可以看到,它会继续计算单行文本的高度:

当你奇怪它是如何计算单行高度的时候,会发觉它利用了一个非常巧妙的方法,就是设置 whiteSpace 为单行显示一个“lg”文本,渲染一遍获取渲染后的单行高度。

同时,它会根据 midLen 的数据来进行文本数据的渲染,然后拿到 midRowRef 去拿虚拟高度。

当然,前面这些虚拟渲染的文本是设置不可见的。

然后,就会看到它是如何处理找到切割位置的,这里是最关键的部分,可以看到下面这个 hook 的依赖项包含 startLen 和 endLen,可以知道如果里面的 walkingState 是 WALKING 的时候,函数就会类似二分法的递归,最后在满足不超过 rows 行数的高度的时候,不断将 startLen 和 endLen 靠近,直接拿到行尾的切割位置。

至于怎么切的,最后可以看下这个:

好了,关于antd源码之通用组件大概如上几个。