本文为稀土掘金技术社区首发签约文章,14天内禁止转载,14天后未获授权禁止转载,侵权必究!
- 前言
大家好,我是若川。我倾力持续组织了一年每周大家一起学习200行左右的源码共读活动,感兴趣的可以点此扫码加我微信 ruochuan02
参与。另外,想学源码,极力推荐关注我写的专栏《学习源码整体架构系列》,目前是掘金关注人数(4.1k+人)第一的专栏,写有20余篇源码文章。
我们开发业务时经常会使用到组件库,一般来说,很多时候我们不需要关心内部实现。但是如果希望学习和深究里面的原理,这时我们可以分析自己使用的组件库实现。有哪些优雅实现、最佳实践、前沿技术等都可以值得我们借鉴。
相比于原生 JS
等源码。我们或许更应该学习,正在使用的组件库的源码,因为有助于帮助我们写业务和写自己的组件。
如果是 Vue
技术栈,开发移动端的项目,大多会选用 vant
组件库,目前(2022-11-13) star
多达 20.4k
。我们可以挑选 vant
组件库学习,我会写一个组件库源码系列专栏,欢迎大家关注。
vant 组件库源码分析系列:
- 1.vant 4 即将正式发布,支持暗黑主题,那么是如何实现的呢
- 2.跟着 vant 4 源码学习如何用 vue3+ts 开发一个 loading 组件,仅88行代码
- 3.分析 vant 4 源码,如何用 vue3 + ts 开发一个瀑布流滚动加载的列表组件?
- 4.分析 vant 4 源码,学会用 vue3 + ts 开发毫秒级渲染的倒计时组件,真是妙啊
- 5.vant 4.0 正式发布了,分析其源码学会用 vue3 写一个图片懒加载组件!
学完本文,你将学到:
1 | bash复制代码1. 学会如何用 vue3 + ts 开发一个 List 组件 |
- 准备工作
看一个开源项目,第一步应该是先看 README.md 再看贡献文档 github/CONTRIBUTING.md。
2.1 克隆源码 && 跑起来
You will need Node.js >= 14 and pnpm.
1 | bash复制代码# 推荐克隆我的项目 |
我们先来看 pnpm dev
最终执行的什么命令。
vant
项目使用的是 monorepo
结构。查看根路径下的 package.json
。
vant/package.json => "dev": "pnpm --dir ./packages/vant dev"
vant/packages/vant/package.json
=> "dev": "vant-cli dev"
pnpm dev
最终执行的是:vant-cli dev
执行测试用例。本文主要是学习 List 组件 的实现,所以我们就不深入 vant-cli dev
命令了。
- List 组件
瀑布流滚动加载,用于展示长列表,当列表即将滚动到底部时,会触发事件并加载更多列表项。
从这个描述和我们自己体验 demo
来。
至少有以下三个问题值得去了解学习。
- 如何监听滚动
- 如何计算滚动到了底部
- 如何触发事件加载更多
带着问题我们直接找到 list demo 文件:vant/packages/vant/src/list/demo/index.vue
。为什么是这个文件,我在上篇文章跟着 vant4 源码学习如何用 vue3+ts 开发一个 loading 组件,仅88行代码分析了其原理,感兴趣的小伙伴点击查看。这里就不赘述了。
3.1 利用 demo 调试
组件源码中的 TS
代码我不会过多解释。没学过 TS
的小伙伴,推荐学这个TypeScript 入门教程。
另外,vant
使用了 @vue/babel-plugin-jsx 插件来支持 JSX、TSX
。
1 | js复制代码// vant/packages/vant/src/list/demo/index.vue |
- 入口文件
主要就是导出一下类型和变量等。
1 | js复制代码// vant/packages/vant/src/list/index.ts |
withInstall
函数在上篇文章5.1 withInstall 给组件对象添加 install 方法 也有分析,这里就不赘述了。
我们可以在这些文件,任意位置加上 debugger
调试源码。
- 主文件
1 | js复制代码import { |
1 | js复制代码export default defineComponent({ |
debugger
调试截图。
接着我们来看其他一些事件。
5.1 一些事件 useExpose、useEventListener
1 | js复制代码// 省略若干代码 |
由上面代码可以看出,check
函数非常重要,我们在下文分析它。
我们先分析上面代码用到的 useExpose
、useEventListener
组合式 API
。
5.2 useExpose 暴露
1 | js复制代码import { getCurrentInstance } from 'vue'; |
通过 ref
可以获取到 List
实例并调用实例方法,详见组件实例方法。
Vant
中的许多组件提供了实例方法,调用实例方法时,我们需要通过ref
来注册组件引用信息,引用信息将会注册在父组件的$refs
对象上。注册完成后,我们可以通过this.$refs.xxx
访问到对应的组件实例,并调用上面的实例方法。
5.3 useEventListener 绑定事件
方便地进行事件绑定,在组件 mounted
和 activated
时绑定事件,unmounted
和 deactivated
时解绑事件。
1 | js复制代码import { Ref, watch, isRef, unref, onUnmounted, onDeactivated } from 'vue'; |
- steup check 函数
1 | js复制代码const check = () => { |
从 check
函数可以看出,主要就是利用滚动高度,接下来我们看这个函数中,使用到的组合式 API
,useTabStatus
、useScrollParent
、useRect
。
6.1 useTabStatus tab 组件的状态
1 | js复制代码import { inject, ComputedRef, InjectionKey } from 'vue'; |
代码根据 commit
可以发现 useTabStatus
有这样一次提交。
fix(List): skip check when inside an inactive tab
主要是在 van-tabs
组件中,provide(TAB_STATUS_KEY, active);
提供了一个状态。tab
不活跃时,跳过 check
函数,不执行。
6.2 useScrollParent 获取元素最近的可滚动父元素
获取元素最近的可滚动父元素。
给定参数 el, root
节点,遍历父级节点查找 style
包含 scroll|auto|overlay
的元素,如果没找到,返回第二个 root
参数(没有第二个参数则是 window
)。
1 | js复制代码import { ref, Ref, onMounted } from 'vue'; |
6.3 useRect 获取元素的大小及其相对于视口的位置
vant-contrib.gitee.io/vant/#/zh-C…
获取元素的大小及其相对于视口的位置,等价于 Element.getBoundingClientRect。
1 | js复制代码// vant/packages/vant-use/src/useRect/index.ts |
6.4 isHidden 是否隐藏
1 | js复制代码// vant/packages/vant/src/utils/dom.ts |
接着我们来分析开头的插槽部分。
- 插槽
插槽部分基本都是有插槽用插槽没有则用默认的。
插槽是函数,比如 slots.default()
。
1 | js复制代码// setup 函数 |
7.1 renderFinishedText 渲染加载完成文字
1 | js复制代码const renderFinishedText = () => { |
7.2 renderErrorText 渲染加载失败文字
1 | js复制代码const clickErrorText = () => { |
7.3 renderLoading 渲染 loading
1 | js复制代码const renderLoading = () => { |
- 总结
我们主要分析了 List
组件 实现原理。
原理:使用 addEventListener
监听父级元素的 sroll
事件,用 Element.getBoundingClientRect 获取元素的大小及其相对于视口的位置,(滚动父级元素和占位元素计算和组件属性 offset(默认300)
属性比较),检测是否触底,触底则加载更多。
1 | js复制代码emit('update:loading', true); |
同时分析了一些相关组合式 API
useExpose
暴露接口供this.$refs.xxx
使用useEventListener
绑定事件useTabStatus
当前tab
是否激活的状态useScrollParent
获取元素最近的可滚动父元素useRect
获取元素的大小及其相对于视口的位置
组件留有四个插槽,分别是:
default
列表内容loading
自定义底部加载中提示finished
自定义加载完成后的提示文案error
自定义加载失败后的提示文案
至此,我们就分析完了 List
组件,主要与 DOM
操作会比较多。List 组件
主文件的代码仅有 100
多行,但封装了很多组合式 API
。看完这篇源码文章,再去看 List 组件文档,可能就会有豁然开朗的感觉。再看其他组件,可能就可以猜测出大概实现的代码了。
如果是使用 react
、Taro
技术栈,感兴趣也可以看看 taroify
List
组件的实现 文档,源码。
如果看完有收获,欢迎点赞、评论、分享支持。你的支持和肯定,是我写作的动力。
- 加源码共读群交流
最后可以持续关注我@若川。我会写一个组件库源码系列专栏,欢迎大家关注。
我倾力持续组织了一年每周大家一起学习200行左右的源码共读活动,感兴趣的可以点此扫码加我微信 ruochuan02
参与。
另外,想学源码,极力推荐关注我写的专栏《学习源码整体架构系列》,目前是掘金关注人数(4.1k+人)第一的专栏,写有20余篇源码文章。
本文转载自: 掘金