2022年11月29日首发于掘金,现在同步到公众号。
11. 前言
大家好,我是若川。我倾力持续组织了一年多源码共读,感兴趣的可以加我微信 lxchuan12
参与。另外,想学源码,极力推荐关注我写的专栏《学习源码整体架构系列》,目前是掘金关注人数(4.6k+人)第一的专栏,写有20余篇源码文章。
我们开发业务时经常会使用到组件库,一般来说,很多时候我们不需要关心内部实现。但是如果希望学习和深究里面的原理,这时我们可以分析自己使用的组件库实现。有哪些优雅实现、最佳实践、前沿技术等都可以值得我们借鉴。
相比于原生 JS
等源码。我们或许更应该学习,正在使用的组件库的源码,因为有助于帮助我们写业务和写自己的组件。
如果是 Vue
技术栈,开发移动端的项目,大多会选用 vant
组件库,目前(2022-11-29) star
多达 20.5k
,已经正式发布 4.0 了[1]。我们可以挑选 vant
组件库学习,我会写一个组件库源码系列专栏[2],欢迎大家关注。
分析 vant4 源码,学会用 vue3 + ts 开发毫秒级渲染的倒计时组件,真是妙啊[3]
这次我们来学习 Lazyload
懒加载组件,可以点此查看 `lazyload` 文档体验[4]。
学完本文,你将学到:
1. 学会如何用 vue3 + ts 开发一个 lazyload 组件
2. 学会 `lazyload` 图片懒加载组件其原理
3. 学会使用事件和 IntersectionObserver API 实现懒加载
4. 等等
22. 准备工作
看一个开源项目,第一步应该是先看 README.md[5] 再看贡献文档 github/CONTRIBUTING.md[6]。
2.1 克隆源码 && 跑起来
You will need Node.js >= 14[7] and pnpm[8].
# 推荐克隆我的项目
git clone https://github.com/lxchuan12/vant-analysis
cd vant-analysis/vant
# 或者克隆官方仓库
git clone git@github.com:vant-ui/vant.git
cd vant
# 安装依赖,如果没安装 pnpm,可以用 npm i pnpm -g 安装,或者查看官网通过其他方式安装
pnpm i
# 启动服务
pnpm dev
执行 pnpm dev
后,这时我们打开懒加载组件 http://localhost:5173/#/zh-CN/lazyload
。
33. 图片懒加载原理
众所周知,图片懒加载的原理其实相对简单。就是进入可视区再加载图片。涉及到的知识点主要有:节流、新API、IntersectionObserver[9]。
大致流程:
事件模式
1. 初始化在元素(比如是 window,但不一定是 window)添加监听滚动和其他相关事件
2. 使用 Element.getBoundingClientRect API 获取元素的大小及其相对于视口的位置,判断是否进入可视化区
3. 图片设置 src 真实的图片路径
4. 离开销毁监听的事件、和移除绑定事件的元素
observer 模式
主要是第二步用 IntersectionObserver[10] API。
那么 vant4 中的 lazyload 怎么做的呢。
带着问题我们直接找到 lazyload demo
文件:vant/packages/vant/src/lazyload/demo/index.vue
。为什么是这个文件,我在之前文章跟着 vant4 源码学习如何用 vue3+ts 开发一个 loading 组件,仅88行代码分析了其原理,感兴趣的小伙伴点击查看。这里就不赘述了。
44. 利用 demo 调试源码
// vant/packages/vant/src/lazyload/demo/index.vue
// 代码有省略
<script lang="ts">
import Lazyload from '..';
if (window.app) {
// 手动修改 demo 为如下,添加 lazyImage 为 true
window.app.use(Lazyload, { lazyComponent: true, lazyImage: true });
}
<script setup lang="ts">
// eslint-disable-next-line import/first
import { cdnURL, useTranslate } from '../../../docs/site';
const t = useTranslate({
'zh-CN': {
title2: '背景图懒加载',
title3: '懒加载模块',
},
});
const imageList = [
cdnURL('apple-1.jpeg'),
cdnURL('apple-2.jpeg'),
cdnURL('apple-3.jpeg'),
cdnURL('apple-4.jpeg'),
];
</script>
<template>
<demo-block :title="t('basicUsage')">
<!-- 手动修改 demo 为 lazy-image -->
<lazy-image v-for="img in imageList" :key="img" :src="img">
</lazy-image>
</demo-block>
</template>
我们可以看出 lazy-load 为入口文件。
这里先附上两张调试截图。动手调试时可以参考学习。
install
函数调试
![682c8263318750c4b1f5164bcd90d67a.png](https://i-blog.csdnimg.cn/blog_migrate/693baf2df5d47b6e1475e6f0c8cd0e5e.png)
LazyClass
调试
![3dda5a1ba73db7768e66b6eef2084267.png](https://i-blog.csdnimg.cn/blog_migrate/8798e3235cde5acf2eaf6bf5d178acfd.png)
55. lazy-load 入口文件
从 vue-lazyload
文件引入导出和默认导出 Lazyload
。 主要包含:
把 lazy 实例对象添加到全局上
注册懒加载组件
注册图片组件
注册指令 lazy
注册指令 lazy-container
// vant/packages/vant/src/lazyload/index.ts
import { Lazyload } from './vue-lazyload';
export default Lazyload;
export { Lazyload };
我们接着来看 vue-lazyload/index.js
主文件。
66. vue-lazyload/index.js 主文件
主要导出一个包含 install
方法的对象 Lazyload
。
// vant/packages/vant/src/lazyload/vue-lazyload/index.j