从arco-design的collapse组件分析如何吸收开源项目的知识

2,393 阅读11分钟

一、前言

“故不积跬步,无以至千里;不积小流,无以成江海”。--《荀子·劝学》

1.1 为什么要看开源项目?

一个高质量,有保证的开源项目其实是非常值得去学习的,我认为它即是一个藏宝图。也是一个"照妖镜"。

站在巨人的肩膀上去眺望自然是视野更佳。例如我经常有遇到很多初期进入开发的同学都会问类似的问题:"我想学xxx技术,哪里有好的视频资料呀"、"我没什么项目经验,什么教程的项目比较好呀",等等之类的。

其实github就一个很好的学习平台,多去窥探高start或者优秀的技术团队或者优秀的大牛去维护的项目,自然是含金量满满,而阅读的时候遇到看不懂的代码自然也能映射出当前技术的短板既能立刻去补充知识再回来阅读。 (本人可能经验尚浅,观点不代表多数人观点,掘金的同学们可以稍做参考哦🤓)

1.2 为什么挑选了arco-desing的Collapse组件来看?

在上个星期的开发计划里,遇到一个组件的功能的体验优化,而该组件是一个映射文章标题等级的大纲组件。

之前由于开发周期的影响,该组件暂且只有展示标题的的功能并无折叠层级的功能,导致在标题过多的场景下以及因为产品需求上不需要内容和大纲的同步滚动,导致操作滚动条的下拉是十分累人的。

所以简单的来说就是需要增加一个折叠的小功能而已,哈哈哈哈🤪。但是本身是一个简单到不能再简单的任务了,我还是认真的思考🤔了一下。不然就单单显示隐藏也太low了,毫无丝滑感。

此时我便想到了一些ui库的一些组件的折叠交互,譬如ant-designtree组件menu组件collapse组件。但是可能认识我的人就知道我是比较趋向于项目驱动型学习的,我当然是想看完一个组件再去完成我的任务。所以我也借此机会把目光投到了比较新的一个ui库arco-design一个由字节团队开源的项目。当然我选择了复杂度更低的collapse组件去探索丝滑折叠的奥秘。

二、揭开arco-design源码

2.1 打开项目代码

来到项目对应的github地址 -- arco-design。打开项目地址之后,老手们应该都知道怎么弄下来的吧🤓。但这里也"兼容"可能有一些新同学来看到这篇文章。我也把命令放在下面吧。

# 这里推荐使用ssh方式去下载项目,实践证明确实比https方式要快

git clone git@github.com:arco-design/arco-design.git
# 进入项目里面之后,就可以安装依赖

 cd arco-desing
 
# 这里我采用了yarn的包管理方式,是因为我看到项目里面有yarn.lock文件。
# 当然你也可以使用npm的方式。

 yarn install

在安装依赖之余,我们可以过一遍package.json看看是否有熟悉或者陌生的依赖,以及项目配备了什么脚本命令。

这里我可以分享一个小技巧如果是我本人平常啃一个项目的情况下,我看到package.json的依赖库发现有陌生的依赖我会去查找这个依赖的用途,并且记录到我的笔记里去,如下图: (图1)

图1:

image.png

看完了package.json,可以再来看看一些根目录下的md的内容   --图2,图3因为一个项目的主要介绍和特色和如何调试等一般都会记录在readme.md等一些根目录下的md里面。浏览之后便能发现在CONTRIBUTING.zh-CN.md里面可以发现项目是如何跑起来并且去调试的。细心的同学可以发现注释上有一个名词叫storybook的东西。第一次看到的小伙伴就可以搜起来了,这个就是一个隐藏的知识点了🧑‍🏫。(图4)

图2:

image.png

图3:

image.png

图4:

image.png

Storybook: 是一个开源工具,用于单独构建 UI 组件和页面。它简化了 UI 开发、测试和文档。-- 引用于官方简述。大白话就是这个工具是给我们在开发组件或者页面的时候调试页面用的。不然就要一个个组件的塞到一个页面里面去调试,或者去配置路由来切换看不同的组件,这些都太过麻烦。

项目地址:github.com/storybookjs…

项目文档:storybook.js.org/

所以还没进入到组件内部我们就已经获取一个知识点了 🔭。然后通过(图3)里的命令我们就可以对照着操作来跑起这个项目   -- (图5)运行页面

图5:

image.png

2.2 进入collapse文件夹。

在看组件之前我们最好把一个组件的功能有什么给摸清楚,所以我们可以打开文档查看collapse的api方法都有些什么东西。

image.png

image.png

2.2.1 发现自动化生成组件文档

进入目录components/collapse就看到了Collapse组件的真容(图6)

图6:

image.png

分别看到一些tsxtsmd文件以及四个命名奇怪的文件夹。所以我们可以深究一下这样的命名的好处是什么有什么作用🦹‍♂️。

命名前后的双下划线的意义,都是泛指有特殊用途的意思,并且区分开正常在C++里都是特定的系统库函数和宏才这样命名,在python里也意为特殊方法的意思。所以图6的命名必然有它的用处,而这里除了__test__文件夹外其余三个里面的内容都是md文件,而这里我就直接说了,它是被自动生成的组件文档。

如何知道他是自动生成的,当然他必然有他触发的命令或者逻辑,回到package.json我们看到脚本命令里面有一串这样的命令

image.png

是由这个依赖(图7)去执行的脚本,那我们可以直接进入到node_modules文件夹里找到arco-scripts文件夹。

图7:

image.png

一路追踪到docgen/index.js里,根据经验我发现了一个config的路径 (图8)是完全指向到我们的跟目录的config文件夹的。

图8:

image.png

所以我们回到项目里面就能发现一些端倪了(图9)这不正好发现了docgen.config.js的配置生成的md格式正是符合我们所看到的格式但是这里提醒一下,直接从这里看的源码都是进行打包后的产物,自然十分的难以理解,我们想了解里面的全部内容,最方便的还是去找到对应的源码。

图9:

image.png

然后后续他的逻辑如何实现的这里我就不展开了,因为内容太多了,但是我可以告诉大家我是如何找到源码的。进入到githubacro-desing组织里找到acro-cli项目,也就是脚手架的意思,因为这里凭经验我断定里面应该是有集成好这些开发脚本的,并且由此打包成的多包依赖。

图10:

image.png

图11:

image.png

2.2.2 打开collapse组件代码

进入到index.tsx页面我可以发现这只是一个引入collapse组件Item组件,和对应的类型接口导出的作用。

image.png

这个是项目常见的做法,通过index导出主要逻辑以及组件,使用的地方只需要引入这个主文件即可,这样可以减少页面引入偏多的问题。

实际来到collaspe.tsx代码页面,我通常读代码都是从第一行,慢慢的往下读,这样可以保证知识的吸收是滴水不漏的。从文件顶部的全部import的引入。可以学到React的常用api createContext, ReactNode, useContext, PropsWithChildren刚好不懂的同学就要先去学懂了再回来了🤓,而且还有一系列封装的工具方法和hook等,如下内容。

  • isFunction:该工具函数用于判断传入值是否为函数并且返回boolean。
  • cs:一个自写的className工具函数,用于集成组件的className,并且方便类名的动态,类似classNames库。
  • omit: 和Typescript自带的Omit工具类型一样的道理,一个自封装工具函数,用于去除对象的键值。
  • ConfigContext: 组件全局配置的context,可以用于全局组件的props,通常为全局配置。
  • IconCaretRight: 一个箭头指右的Icon组件
  • IconCaretLeft: 一个箭头指左边的Icon组件
  • useMergeValue: 一个自定义的hook, 维护内部与外部的value,实际在当前组件就是维护activeKeys正在展开的数组key状态。(从组件文档也能看的出来有自定义展开的功能)
  • useMergeProps: 一个自定义的hook,在当前组件就是维护合并由外部传入的配置与全局配置和默认配置的合并状态。

而这些大多数方法其实我们都可以学会并为自己的项目使用。我们继续往下探索,可以发现有一个类似于如下图的一个方法。这个方法其实看清楚他的方法之后就知道,其实这是一个会返回全局类名的方法,这个的封装可以很轻松的就通过修改顶层主类名而,达到全局组件类名的替换。是许多ui库都会使用的一个手法。

image.png

紧邻下方法就有一个onItemClick的方法,从参数签名来看能看出来,这个应该就是折叠的时候要触发的点击方法了,它里面判断了props里的accordion是否存也就是文档的手风琴模式是否开启。开启的话它就永远的只保留当前传进来的key值,所以数组永远只有长度1反之就是收集多个展开的key了。他绑定在与包裹collapse组件的CollapseContext.Providervalue里的onToggle属性上,自然就是用于把props共享到children里。而value里还有一些expandIconPosition的props属性,判断icon的位置去返回刚刚我们引入的到底是使用左指向图标还是右指向图标。由此看来其实collapse组件里其实只是一个传值的地方,真正的核心是在我们下面要进入的Item.tsx页面里。

2.2.3 打开Item组件代码

进入组件之后就发现一个我要完成我的需求的一个很关键的库react-transition-group

image.png

在我们用React实现一个组件的挂载和卸载的时候有没有发现挂载和卸载的过程是一瞬间完成的,我们很难给其添加进场和出场动画。那么react-transition-group就来帮我们解决这个问题了,我们只用告诉它什么时候去挂载一个组件、什么时候去卸载一个组件,以及挂载、卸载这个组件要耗费多久,他就会自动帮我们去管理挂载、卸载的各个阶段,并把每个阶段暴露给我们,从而我们可以在不同的阶段做不同的操作以实现动画效果。 --引用自字节跳动大力教育前端团队的文章。

浏览到下面继续发现了一个小细节操作,折叠面板的小箭头图标我们记得只引入了明明是向左向右两个图标但是展开之后图标就指向下方并且十分的流畅,而实现也是十分简单,使用cs可以动态类名的规则,为有旋转样式的类名通过isExpanded变量来控制是否旋转。而transform天生利用GPU的优势所以这种动画表现的十分顺畅。

image.png

重点来了,终于发现了流畅的秘密。正如上面对react-transition-group的描述一样,它帮我们在各个阶段内去触发事件,让我们可以对组件的有着更细粒度的使用动画样式,并且依然是借助了transform对高度的展开使用了cubic-bezier函数调整贝塞尔曲线的参数让Item的展开如此顺滑

image.png

image.png

所以我们可以在我们的项目组件利用改方法去完成比较流畅的体验了。

其实大家还可以继续看文档的api在里面是如何实现的,譬如举个例子:Collapse.Item里的destroyOnHide的props参数是怎么样做到折叠后销毁组件的呢?

image.png

其实秘密就在这里恰好借助了Transition组件提供的两个退出回调方法,去判断是否卸载组件。

image.png

三、总结

其实本文章的主要目的其实也是为了分享给大家如何认真的看完一段组件,不错漏细节,这样往往才能啃的更深吸收的更多,其实有好的方案或者优化都是值得去深究的。当前的例子虽小,但是收获的东西可不少。值得大家亲身去阅读源码方能体会。

🤩👏 这里我确实要称赞一下大厂,大厂就是大厂写的东西确实比一般人要周到。🤑🦄