前言
开发过程中,发现了一个更方便来做图片懒加载的方法,简单记录一下
正文
之前比较常见的对图片进行懒加载的方法,一般都是手动获取到 img dom,然后通过监听 scroll 事件等,来判断 img dom 是否出现在用户视窗(或目标范围),然后在设置真实 url 来加载图片;拆分这个实现过程,中间做了两件事:
1. 监听img dom的位置
2. 设置img的真实url 开始加载图片
对于第一步需要来写大量的判断代码,如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| var targetElement = document.querySelector("#target");
function isElementInViewport(el) { var rect = el.getBoundingClientRect(); return ( rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth) ); }
function handleScroll() { if (isElementInViewport(targetElement)) { console.log("目标元素进入视窗"); } else { console.log("目标元素离开视窗"); } }
window.addEventListener("scroll", handleScroll);
handleScroll();
|
其次,另一点就是可能项目里对全局挂载事件可能不只有这一处,当挂载到全局的事件很多且没有手动移除,可能带来性能损耗(一直会对其传入的回调分配内存等)
新的实现方式 ,就是通过 Intersection Observer API ,来实现我们的第一步的操作,除过兼容性可能需要关注,基本可以完美替代上述的方案
他的原理也很简单,通过指定一个我们需要监听的 dom,然后显示传入一个回调函数,当这个 dom 出现在视窗(或指定的某个 dom)内时,会调用传入的回调
简单写个例子,这个例子是当我们点击 show 按钮的时候,id 为 panel 的 div 会展示出来,接着就会触发我们的回调
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> <style> div { width: 500px; height: 500px; background-color: rebeccapurple; display: none; } </style> </head> <body> <div id="panel"></div> <button id="show">show</button> <script> const show = document.querySelector("#show"); const panel = document.querySelector("#panel"); const func = () => { }; const panelObserber = new IntersectionObserver( (arg) => { console.log("debug callback", arg); if (arg[0].isIntersecting) { func(); } }, { root: document.querySelector("body"), threshold: 1, } ); panelObserber.observe(panel); show.addEventListener("click", () => { panel.style.display = "block"; }); </script> </body> </html>
|
封装一个 react Hook
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| import { useState, useRef, useEffect } from "react";
const useLazyLoad = (imageSrc: string, placeholderSrc: string) => { const [imageSrcState, setImageSrc] = useState(placeholderSrc); const imageRef = useRef(null);
useEffect(() => { const observer = new IntersectionObserver((entries) => { if (entries[0].isIntersecting) { setImageSrc(imageSrc); observer.unobserve(imageRef.current!); } });
if (imageRef.current) { observer.observe(imageRef.current); }
return () => observer.disconnect(); }, [imageSrc, placeholderSrc]);
return [imageSrcState, imageRef]; };
export default useLazyLoad;
|