本文为稀土掘金技术社区首发签约文章,14天内禁止转载,14天后未获授权禁止转载,侵权必究!
- 前言
大家好,我是若川。我倾力持续组织了一年每周大家一起学习200行左右的源码共读活动,感兴趣的可以点此扫码加我微信 ruochuan02
参与。另外,想学源码,极力推荐关注我写的专栏《学习源码整体架构系列》,目前是掘金关注人数(4.1k+人)第一的专栏,写有20余篇源码文章。
我们开发业务时经常会使用到组件库,一般来说,很多时候我们不需要关心内部实现。但是如果希望学习和深究里面的原理,这时我们可以分析自己使用的组件库实现。有哪些优雅实现、最佳实践、前沿技术等都可以值得我们借鉴。
相比于原生 JS
等源码。我们或许更应该学习,正在使用的组件库的源码,因为有助于帮助我们写业务和写自己的组件。
如果是 Vue
技术栈,开发移动端的项目,大多会选用 vant
组件库,目前(2022-11-20)star
多达 20.5k
,最新版本是 v4.0.0-rc7
。我们可以挑选 vant
组件库学习,我会写一个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 写一个图片懒加载组件!
这次我们来学习倒计时组件,countdown
。
学完本文,你将学到:
1 | bash复制代码1. 如何开发一个更优雅的毫秒级渲染的倒计时组件 |
- 准备工作
看一个开源项目,我们可以先看 README.md 再看 github/CONTRIBUTING.md
2.1 克隆源码
You will need Node.js >= 14 and pnpm.
1 | bash复制代码# 推荐克隆我的项目 |
执行 pnpm dev
后,这时我们打开倒计时组件 http://localhost:5173/#/zh-CN/count-down
。
- 倒计时组件可谓是十分常用
在各种电商类或者其他的移动端页面中,倒计时真的是太常见了。我们自己也基本能够快速的写一个倒计时组件。代码实现参考这里,主要是 JavaScript
。码上掘金倒计时初步代码@若川
代码中,我直接使用的 setInterval
和每秒钟执行一次。把倒计时的时候减去1s
,当倒计时毫秒数不足时用 clearInterval
清除停止定时器。
但如果要实现毫秒级的倒计时这种方法行不通。
另外 setInterval
这种做法,并不是最优的。
那么,vant
倒计时组件中,是如何处理毫秒级和实现倒计时呢。
带着问题我们直接找到 countdown demo
文件:vant/packages/vant/src/count-down/demo/index.vue
。为什么是这个文件,我在之前文章跟着 vant4 源码学习如何用 vue3+ts 开发一个 loading 组件,仅88行代码分析了其原理,感兴趣的小伙伴点击查看。这里就不赘述了。
- 利用 demo 调试源码
组件源码中的 TS
代码我不会过多解释。没学过 TS
的小伙伴,推荐学这个TypeScript 入门教程。
另外,vant
使用了 @vue/babel-plugin-jsx 插件来支持 JSX、TSX
。
1 | js复制代码// vant/packages/vant/src/count-down/demo/index.vue |
从 demo
文件中,我们可以看出 import VanCountDown, { type CountDownInstance } from '..';
,引入自 vant/packages/vant/src/count-down/index.ts
。我们继续来看入口 index.ts
。
- 入口 index.ts
主要就是导出一下类型和变量等。
1 | js复制代码// vant/packages/vant/src/count-down/index.ts |
withInstall
函数在之前文章5.1 withInstall 给组件对象添加 install 方法 也有分析,这里就不赘述了。
我们可以在这些文件,任意位置加上 debugger
调试源码。
截两张调试图。
调试 Countdown
setup
。
调试 useCountDown
。
我们跟着调试,继续分析 Countdown
。
- 主文件 Countdown
1 | js复制代码// vant/packages/vant/src/count-down/CountDown.tsx |
6.1 setup 部分
这一部分主要使用了useCountDown
。
1 | js复制代码setup(props, { emit, slots }) { |
6.2 useExpose 暴露
1 | js复制代码import { getCurrentInstance } from 'vue'; |
通过 ref
可以获取到 Countdown
实例并调用实例方法,详见组件实例方法。
Vant
中的许多组件提供了实例方法,调用实例方法时,我们需要通过ref
来注册组件引用信息,引用信息将会注册在父组件的$refs
对象上。注册完成后,我们可以通过this.$refs.xxx
或者
1 | js复制代码const xxxRef = ref(); |
访问到对应的组件实例,并调用上面的实例方法。
- useCountDown 组合式 API
7.1 parseTime 解析时间
1 | js复制代码// vant/packages/vant-use/src/useCountDown/index.ts |
以上这大段代码,parseTime
是主要函数,解析时间,生成天数、小时、分钟、秒、毫秒的对象。
7.2 useCountDown 真实逻辑
真实逻辑这一段可以不用细看。可以调试时再细看。
主要就是利用 Date.now()
会自己走的原理。
1 | bash复制代码初始化开始:结束时间 = 当前时间戳 + 剩余时间 |
设计的十分巧妙,看到这里,我们可能感慨:不得不佩服。
1 | js复制代码// 简化版 一 |
码上掘金倒计时简化版二
1 | js复制代码// vant/packages/vant-use/src/useCountDown/index.ts |
我们继续来看 raf
和 cancelRaf
,是如何实现的。
- raf、cancelRaf、inBrowser 实现
1 | js复制代码// 判断是不是浏览器环境,你可能会问,为啥要判断?因为 SSR (服务端渲染)不是浏览器环境。 |
上文代码,主要一个 API
,requestAnimationFrame、cancelAnimationFrame
。
我们这里简单理解为 window.requestAnimationFrame()
中的回调函数,每 16.67ms
执行一次回调函数即可。
也就是类似 setTimeout、clearTimeout
1 | js复制代码const timeId = setTimeout( () => { |
也可以自行搜索这个 API
查阅更多资料。比如 MDN
上的解释。
mdn window.requestAnimationFrame
window.requestAnimationFrame()
告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行
回调函数执行次数通常是每秒 60 次,但在大多数遵循 W3C 建议的浏览器中,回调函数执行次数通常与浏览器屏幕刷新次数相匹配。
备注: 若你想在浏览器下次重绘之前继续更新下一帧动画,那么回调函数自身必须再次调用
window.requestAnimationFrame()
。
- 支持格式化时间,默认 HH:mm:ss
9.1 parseFormat 处理格式化
再来看看,组件中,是如何格式化时间的。这个值得我们参考。我们很多时候可能都是写死天数、小时等文案。不支持自定义格式化。
1 | js复制代码// vant/packages/vant/src/count-down/utils.ts |
9.2 padZero 补零
1 | js复制代码// vant/packages/vant-compat/node_modules/vant/src/utils/format.ts |
行文自此,我们就分析完了毫秒级渲染的倒计时组件的实现。
- 总结
我们来简单总结下。通过 demo
文件调试,入口文件,主文件,useCountDown
组合式 API,插槽等。
分析了自定义格式、毫秒级渲染、自定义样式(利用插槽)等功能的实现。
其中毫秒级渲染,主要就是利用 Date.now()
和 (window.requestAnimationFrame
)每 16.67ms
执行一次回调函数。
大致流程如下:
1 | bash复制代码初始化开始:结束时间 = 当前时间戳 + 剩余时间 |
看完这篇源码文章,再去看 CountDown 组件文档,可能就会有豁然开朗的感觉。再看其他组件,可能就可以猜测出大概实现的代码了。
如果是使用 react
、Taro
技术栈,感兴趣也可以看看 taroify
CountDown
组件的实现 文档,源码。
如果看完有收获,欢迎点赞、评论、分享支持。你的支持和肯定,是我写作的动力。
- 加源码共读群交流
最后可以持续关注我@若川。我会写一个组件库源码系列专栏,欢迎大家关注。
我倾力持续组织了一年每周大家一起学习200行左右的源码共读活动,感兴趣的可以点此扫码加我微信 ruochuan02
参与。
另外,想学源码,极力推荐关注我写的专栏《学习源码整体架构系列》,目前是掘金关注人数(4.2k+人)第一的专栏,写有20余篇源码文章。包含jQuery
、underscore
、lodash
、vuex
、sentry
、axios
、redux
、koa
、vue-devtools
、vuex4
、koa-compose
、vue 3.2 发布
、vue-this
、create-vue
、玩具vite
、create-vite
等20余篇源码文章。
本文转载自: 掘金