懒加载优化:JavaScript IntersectionObserver API监听元素是否可见
Author:zhoulujun Date:
前端性能优化很重要一项就是 按需加载,比如 懒加载,图片或者新数据请求,滚动到可见区域(浏览器视窗viewport内)再去加载。
这个关键点,就是如何判断目标元素是否在视窗内。
从实践中来看,关键点在于,首屏加载,他的等待可见时候(视窗内)才加载。
传统的滚动加载,通过滚动,去判断元素是否可见,在去根据需求去加载元素。
但是,元素本身是一个复杂的组件,这样做就比较麻烦。况且,滚动事件,只要滚动就会不断触发,需要做一些列的性能优化处理
其次元素本身是否折叠等因素,再次增加系统设计的复杂性。所以,我们需要用的新设计方式去解决这个问题。
首先,我们回顾传统的方法是如何处理。这里我们首先需要考虑的是否如何监控元素是否在进入到 视窗内。
判断元素是否在视窗之内
先安利下《图解js中DOM/event位置:clientX,offsetX,screenX,pageX,offsetLeft,scrollLeft》
传统方法:偏移量计算
公式: el.offsetTop - parent.scrollTop <= parent.clientHeight
function isInViewPortOfOne(el, parent = document.documentElement, offset = 100) { const viewPortHeight = parent.clientHeight; const scrollTop = parent.scrollTop; const offsetTop = el.offsetTop; const top = offsetTop - scrollTop; // 这里有个+100是为了提前加载+ 100 return top <= viewPortHeight + offset; }
这种是最早的算法,在ie时代,只有靠这个
虽然我们知道 offsetTop、offsetLeft 属性会不断触发浏览器重绘 (可参看 《浏览器层面优化前端性能(2):Reader引擎线程与模块分析优化点》、《chrome对页面重绘和回流以及优化进行优化》)
getBoundingClientRect计算
公式: el.offsetTop - parent.scrollTop <= parent.clientHeight
function isInViewPortOfOne(el, parent = document.documentElement, offset = 100) { const viewPortHeight = parent.clientHeight; const top = el.getBoundingClientRect() && el.getBoundingClientRect().top; // 这里有个+100是为了提前加载+ 100 return top - viewPortHeight <= offset; }
这种在ie8以上,绝大部分浏览器,都可以正常跑。ie8一下,应该是10年前的事情了
调用目标元素的getBoundingClientRect()方法,得到它对应于视口左上角的坐标,再判断是否在视口之内。就可以满足我们的开发需求了。
不管怎么样,以上两种方法都存在缺点:由于scroll事件密集发生,计算量很大,容易造成性能问题。虽然有 可以节流或者抖动(《Debounce和Throttle 的原理及实现》)来替身性能。
而且如果组件本身复杂,比如图标组件,需要控制组件里面的内部逻辑开关。组件组成的群组问题。加上组件的折叠。就要写一系列的代码
IntersectionObserver计算
IntersectionObserver 是一个比较新的api,直接看文档即可:https://developers.google.com/web/updates/2016/04/intersectionobserver
// 定义监听参数 const options = { // 表示重叠面积占被观察者的比例,从 0 - 1 取值, // 1 表示完全被包含 threshold: .1, }; // 定义监听函数 const callback = function (entries, observer) { /* entries.forEach((entry) => { entry.time; // 触发的时间 entry.rootBounds; // 根元素的位置矩形,这种情况下为视窗位置 entry.boundingClientRect; // 被观察者的位置举行 entry.intersectionRect; // 重叠区域的位置矩形 entry.intersectionRatio; // 重叠区域占被观察者面积的比例(被观察者不是矩形时也按照矩形计算) entry.target; // 被观察者 });*/ if (entries[0].intersectionRatio > 0) { console.log('进入可视区域'); // TODO } else { console.log('移出可视区域'); } }; // 创建一个监听者 const observer = new IntersectionObserver(callback, options); // 指定监听元素 const target = document.querySelector('.target'); observer.observe(target); // 移除绑定 // observer.unobserve(el)
通过这个方法,可以让自己自己判断自己是否在视窗内。从而通过组件容器去渲染里面的组件。
对于chrome浏览器,特别是toB的业务,不需要考虑ie问题的。这个方法,应该很实用
https://caniuse.com/?search=IntersectionObserver
目前兼容性方面,还是非常可观的。如果需要兼容不支持,就实现原来滚动监听的代码的了。
参考文章:
判断元素是否在视窗之内 https://imweb.io/topic/5c7bc84ebaf81d7952094978
如何判断元素是否进入可视区域viewport? https://juejin.cn/post/6844903725249609741
IntersectionObserver API 使用教程 https://www.ruanyifeng.com/blog/2016/11/intersectionobserver_api.html
JavaScript 监听元素是否进入/移出可视区域 https://blog.csdn.net/latency_cheng/article/details/84963435
转载本站文章《懒加载优化:JavaScript IntersectionObserver API监听元素是否可见》,
请注明出处:https://www.zhoulujun.cn/html/webfront/SGML/html5/2021_0110_8601.html