this.options = {
ListenEvents: listenEvents || DEFAULT_EVENTS,
}
/*
* add or remove eventlistener
* @param {DOM} el DOM or Window
* @param {boolean} start flag
* @return
*/
initListen(el, start) {
this.options.ListenEvents.forEach((evt) =>
(start ? on : off)(el, evt, this.lazyLoadHandler)
);
}
复制代码
8.4.1 on、off 监听事件,移除事件
export function on(el, type, func) {
el.addEventListener(type, func, {
capture: false,
passive: true,
});
}
复制代码
// vant/packages/vant/src/lazyload/vue-lazyload/util.js
export function off(el, type, func) {
el.removeEventListener(type, func, false);
}
复制代码
8.5 initIntersectionObserver 初始化
/**
* init IntersectionObserver
* set mode to observer
* @return
*/
initIntersectionObserver() {
if (!hasIntersectionObserver) {
return;
}
this.observer = new IntersectionObserver(
this.observerHandler.bind(this),
this.options.observerOptions
);
if (this.listeners.length) {
this.listeners.forEach((listener) => {
this.observer.observe(listener.el);
});
}
}
复制代码
8.6 observerHandler 观测,触发 load 事件
mdn 文档:IntersectionObserverEntry
/**
* init IntersectionObserver
* @return
*/
observerHandler(entries) {
entries.forEach((entry) => {
if (entry.isIntersecting) {
this.listeners.forEach((listener) => {
// 如果加载完成了,就移除监听
if (listener.el === entry.target) {
if (listener.state.loaded)
return this.observer.unobserve(listener.el);
listener.load();
}
});
}
});
}
复制代码
8.7 实例方法 addLazyBox 添加要懒加载的组件到队列(数组)
/*
* add lazy component to queue
* @param {Vue} vm lazy component instance
* @return
*/
addLazyBox(vm) {
this.listeners.push(vm);
// 浏览器环境
if (inBrowser) {
// 把
this.addListenerTarget(window);
// 如果是监听 observer 模式,监听 new IntersectionObserver().observe(vm.el)
this.observer && this.observer.observe(vm.el);
if (vm.$el && vm.$el.parentNode) {
// 加入父级
this.addListenerTarget(vm.$el.parentNode);
}
}
}
复制代码
8.8 实例方法 removeComponent 移除组件
/*
* remove lazy components form list
* @param {Vue} vm Vue instance
* @return
*/
removeComponent(vm) {
if (!vm) return;
remove(this.listeners, vm);
this.observer && this.observer.unobserve(vm.el);
// 移除父级
if (vm.$parent && vm.$el.parentNode) {
this.removeListenerTarget(vm.$el.parentNode);
}
// 移除 window 元素
this.removeListenerTarget(window);
}
复制代码
8.9 addListenerTarget 添加事件的目标元素
比如 window 等。
/*
* add listener target
* @param {DOM} el listener target
* @return
*/
addListenerTarget(el) {
if (!el) return;
let target = this.targets.find((target) => target.el === el);
if (!target) {
target = {
el,
id: ++this.targetIndex,
childrenCount: 1,
listened: true,
};
this.mode === modeType.event && this.initListen(target.el, true);
this.targets.push(target);
} else {
target.childrenCount++;
}
return this.targetIndex;
}
复制代码
8.10 removeListenerTarget 移除事件的目标元素
/*
* remove listener target or reduce target childrenCount
* @param {DOM} el or window
* @return
*/
removeListenerTarget(el) {
this.targets.forEach((target, index) => {
if (target.el === el) {
target.childrenCount--;
if (!target.childrenCount) {
this.initListen(target.el, false);
this.targets.splice(index, 1);
target = null;
}
}
});
}
复制代码
9. 总结
大致流程:
- 事件模式
1. 初始化在元素(比如是 window,但不一定是 window)添加监听滚动和其他相关事件
2. 使用 Element.getBoundingClientRect API 获取元素的大小及其相对于视口的位置,判断是否进入可视化区
3. 进入可视区触发 load 事件,将图片设置 src 真实的图片路径,从而自动加载图片
4. 离开销毁监听的事件、和移除绑定事件的元素
复制代码
- observer 模式
主要是第二步用 IntersectionObserver API。
// 把 Vue 实例对象 this 添加到 lazy 实例中
lazyManager.addLazyBox(this);
// 执行 lazyLoaderHandler 函数(发现节点(元素)在视口 checkInView,触发 load 事件)
lazyManager.lazyLoadHandler();
// 移除组件
lazyManager.removeComponent(this);
复制代码
在 load 事件中,调用 loadImageAsync
函数。
const image = new Image();
image.src = xxx;
image.onload = () => {}
image.onerror = () => {}
复制代码
行文至此,我们就算分析完了 lazyload 组件。
其中,有很多细节处理值得我们学习。 比如:
- 监听事件,不仅仅是
scroll
事件,还有'scroll','wheel','mousewheel','resize','animationend','transitionend','touchmove'
- 监听本身数组存起来了
- 目标元素也用数组存起来了。
install
函数主要有以下实现:
- 把 lazy 实例对象添加到全局上
- 注册懒加载组件
- 注册图片组件
- 注册指令 lazy
- 注册指令 lazy-container 没有分析。
但限于篇幅原因,组件源码还有指令部分没有分析。 感兴趣的小伙伴可以自行分析学习。
如果看完有收获,欢迎点赞、评论、分享支持。你的支持和肯定,是我写作的动力。
10. 加源码共读群交流
最后可以持续关注我@若川。我会写一个组件库源码系列专栏,欢迎大家关注。
我倾力持续组织了一年每周大家一起学习200行左右的源码共读活动,感兴趣的可以点此扫码加我微信 ruochuan12 参与。
作者:若川
链接:https://juejin.cn/post/7171227417246171149
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。