本文为稀土掘金技术社区首发签约文章,14天内禁止转载,14天后未获授权禁止转载,侵权必究!
- 前言
大家好,我是若川。我倾力持续组织了一年每周大家一起学习200行左右的源码共读活动,感兴趣的可以点此扫码加我微信 ruochuan02
参与。另外,想学源码,极力推荐关注我写的专栏《学习源码整体架构系列》,目前是掘金关注人数(4.3k+人)第一的专栏,写有20余篇源码文章。
我们开发业务时经常会使用到组件库,一般来说,很多时候我们不需要关心内部实现。但是如果希望学习和深究里面的原理,这时我们可以分析自己使用的组件库实现。有哪些优雅实现、最佳实践、前沿技术等都可以值得我们借鉴。
相比于原生 JS
等源码。我们或许更应该学习,正在使用的组件库的源码,因为有助于帮助我们写业务和写自己的组件。
如果是 Vue
技术栈,开发移动端的项目,大多会选用 vant
组件库,目前(2022-11-29) star
多达 20.5k
,已经正式发布 4.0 了。我们可以挑选 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 写一个图片懒加载组件!
这次我们来学习 Lazyload
懒加载组件,可以点此查看 lazyload
文档体验。
学完本文,你将学到:
1 | bash复制代码1. 学会如何用 vue3 + ts 开发一个 lazyload 组件 |
- 准备工作
看一个开源项目,第一步应该是先看 README.md 再看贡献文档 github/CONTRIBUTING.md。
2.1 克隆源码 && 跑起来
You will need Node.js >= 14 and pnpm.
1 | bash复制代码# 推荐克隆我的项目 |
执行 pnpm dev
后,这时我们打开懒加载组件 http://localhost:5173/#/zh-CN/lazyload
。
- 图片懒加载原理
众所周知,图片懒加载的原理其实相对简单。就是进入可视区再加载图片。涉及到的知识点主要有:节流、新API、IntersectionObserver。
大致流程:
- 事件模式
1 | bash复制代码1. 初始化在元素(比如是 window,但不一定是 window)添加监听滚动和其他相关事件 |
- observer 模式
主要是第二步用 IntersectionObserver API。
那么 vant4 中的 lazyload 怎么做的呢。
带着问题我们直接找到 lazyload demo
文件:vant/packages/vant/src/lazyload/demo/index.vue
。为什么是这个文件,我在之前文章跟着 vant4 源码学习如何用 vue3+ts 开发一个 loading 组件,仅88行代码分析了其原理,感兴趣的小伙伴点击查看。这里就不赘述了。
- 利用 demo 调试源码
1 | js复制代码// vant/packages/vant/src/lazyload/demo/index.vue |
我们可以看出 lazy-load 为入口文件。
这里先附上两张调试截图。动手调试时可以参考学习。
install
函数调试
LazyClass
调试
- lazy-load 入口文件
从 vue-lazyload
文件引入导出和默认导出 Lazyload
。
主要包含:
- 把 lazy 实例对象添加到全局上
- 注册懒加载组件
- 注册图片组件
- 注册指令 lazy
- 注册指令 lazy-container
1 | js复制代码// vant/packages/vant/src/lazyload/index.ts |
我们接着来看 vue-lazyload/index.js
主文件。
- vue-lazyload/index.js 主文件
主要导出一个包含 install
方法的对象 Lazyload
。
1 | js复制代码// vant/packages/vant/src/lazyload/vue-lazyload/index.js |
单从图片懒加载来看,简化上面的代码,则是这样。
1 | js复制代码// 简化 |
我们先来看 LazyImage
组件。
- lazy-image 组件
传入 lazy 实例对象作为参数,默认导出一个返回 vue 组件对象的函数。
我看这块源码时,调试发现由于 render
函数 vue2
和 vue3
写法不同,导致报错。于是提了一个PR,修复了这个问题。
fix(lazyload): lazy-image h is not a function (#11229)
1 | js复制代码// vant/packages/vant/src/lazyload/vue-lazyload/lazy-image.js |
7.1 watch、created、mounted、beforeUnmount
1 | js复制代码export default (lazyManager) => ({ |
我们可以看出,主要有以下三个实例方法,下文细述。
1 | js复制代码// 把 Vue 实例对象 this 添加到 lazy 实例中 |
7.2 methods init 初始化函数
1 | js复制代码export default (lazyManager) => ({ |
7.3 methods checkInView 检查元素是否在视图中的函数
1 | js复制代码export default (lazyManager) => ({ |
这里主要是用了 useRect 组合式 API。
vant 文档:useRect 获取元素的大小及其相对于视口的位置
获取元素的大小及其相对于视口的位置,等价于 Element.getBoundingClientRect。
更多介绍,我在文章 分析 vant4 源码,如何用 vue3 + ts 开发一个瀑布流滚动加载的列表组件?分析过,这里就不在赘述了。
7.4 methods load 函数
1 | js复制代码export default (lazyManager) => ({ |
我们来看 loadImageAsync
函数。
7.5 loadImageAsync 加载图片
1 | js复制代码export const loadImageAsync = (item, resolve, reject) => { |
我们接着来看 lazy.js
文件中的 Lazy
类。
- lazy 类
我们来看 lazy.js
主结构,拥有若干实例方法。
1 | js复制代码export default function () { |
8.1 构造函数 Lazy
1 | js复制代码export default function () { |
值得一提的是:throttle
节流函数,包裹 lazyLoadHandler
。为了防止多次快速加载,影响性能。
8.2 实例方法 lazyLoadHandler
英文注释:发现节点(元素)在视口,触发 load
事件
1 | js复制代码/** |
8.3 setMode 设置模式:事件模式还是 observer 模式
1 | js复制代码import { inBrowser } from '@vant/use'; |
1 | js复制代码setMode(mode) { |
8.4 initListen 初始化监听事件
添加和移除事件监听
1 | js复制代码const DEFAULT_EVENTS = [ |
8.4.1 on、off 监听事件,移除事件
1 | js复制代码export function on(el, type, func) { |
1 | js复制代码// vant/packages/vant/src/lazyload/vue-lazyload/util.js |
8.5 initIntersectionObserver 初始化
1 | js复制代码/** |
8.6 observerHandler 观测,触发 load 事件
mdn 文档:IntersectionObserverEntry
1 | js复制代码/** |
8.7 实例方法 addLazyBox 添加要懒加载的组件到队列(数组)
1 | js复制代码/* |
8.8 实例方法 removeComponent 移除组件
1 | js复制代码/* |
8.9 addListenerTarget 添加事件的目标元素
比如 window 等。
1 | js复制代码/* |
8.10 removeListenerTarget 移除事件的目标元素
1 | js复制代码/* |
- 总结
大致流程:
- 事件模式
1 | bash复制代码1. 初始化在元素(比如是 window,但不一定是 window)添加监听滚动和其他相关事件 |
- observer 模式
主要是第二步用 IntersectionObserver API。
1 | js复制代码// 把 Vue 实例对象 this 添加到 lazy 实例中 |
在 load 事件中,调用 loadImageAsync
函数。
1 | js复制代码const image = new Image(); |
行文至此,我们就算分析完了 lazyload 组件。
其中,有很多细节处理值得我们学习。
比如:
- 监听事件,不仅仅是
scroll
事件,还有'scroll','wheel','mousewheel','resize','animationend','transitionend','touchmove'
- 监听本身数组存起来了
- 目标元素也用数组存起来了。
install
函数主要有以下实现:
- 把 lazy 实例对象添加到全局上
- 注册懒加载组件
- 注册图片组件
- 注册指令 lazy
- 注册指令 lazy-container 没有分析。
但限于篇幅原因,组件源码还有指令部分没有分析。
感兴趣的小伙伴可以自行分析学习。
如果看完有收获,欢迎点赞、评论、分享支持。你的支持和肯定,是我写作的动力。
- 加源码共读群交流
最后可以持续关注我@若川。我会写一个组件库源码系列专栏,欢迎大家关注。
我倾力持续组织了一年每周大家一起学习200行左右的源码共读活动,感兴趣的可以点此扫码加我微信 ruochuan02
参与。
另外,想学源码,极力推荐关注我写的专栏《学习源码整体架构系列》,目前是掘金关注人数(4.1k+人)第一的专栏,写有20余篇源码文章。
本文转载自: 掘金