图片延迟加载和无限数据加载

一、图片懒加载意义

虽然当代浏览器在渲染DOM树的时候,遇到img并不会阻碍DOM树的渲染(浏览器会开辟HTTP线程请求图片资源文件),但是在生成RENDER TREE后,浏览器进行渲染的时候,会把渲染树和图片一起进行绘制

会遇到一些影响性能(影响页面第一次加载的速度)问题:

  1. 如果请求的图片资源过多,我们同时可以开辟的HTTP线程只有6个,这样图片资源预先的加载,会影响其他资源的请求速度
  2. 第一次绘制页面的时候,如果就开始绘制图片,也需要消耗很多的时间,也影响页面第一次打开的速度

解决方案:

  1. 我们一般都采用图片懒加载(开始需要展示图片的位置,我们基于默认图或者一个空白的盒子占位,真实图片不加载,只有当页面第一次渲染完以及滚动到当前所在的区域时候,再去加载真实的图片)
  2. 我们可以把图片base64(虽然也会慢一点,但是总比不做强)

二、实现方案

  1. 旧:基于盒子模型( 或者getBoundingClientRect() ),我们判断出是否加载图片,在滚动条滚动中随时进行判断处理(需要做节流);
  2. 新:IntersectionObserver基于元素的监听,一体化解决懒加载问题
/*
IntersectionObserver监听,当DOM元素出现和离开视口的时候触发回调函数
    observer = new IntersectionObserver([callback], [options])
    threshold 默认值是[0],我们可以基于数组的方式管控,元素相对于视口的交叉位置,在什么程度下触发
    回调函数
*/
observer.observe([DOM元素])    监听
observer.unobserve([DOM元素])  移除监听

//例:
let observer = new IntersectionObserver( changes => {
    //changes包含所有监听的DOM信息
    changes.forEach( item => {
        let { isIntersecting, target } = item;
        if(isIntersecting) {
            lazyImg(target);
            observer.unobserve(target);
        }
    });
}, {
    //控制什么时候触发回调
    threshold: [1]
} )
复制代码

三、无限下拉加载

/*
在整个卡片下面加一个盒子,如起名为bottomBox,设置高度为100px,监听盒子即可,如果监听到这个盒子已经
露头,说明已经到达底部,就可以再加载一次真实数据
*/
let observer2 = new IntersectionObserver(changes => {
    let isLoadMore = false; //是否正在加载更多数据
    let isIntersecting = changes[0].isIntersecting;
    if(isIntersecting) {
        //到达底部了
        if(isLoadMore) return;
        isLoadMore = true;
        //重新发送请求即可 
    }
}, {
    threshold: [0]
})
复制代码

四、图片懒加载插件封装

/*
图片懒加载插件:只处理图片的延迟加载
    规定:
        1. 结构:图片放在一个盒子中,盒子是图片没有加载之前的占位;需要延迟加载的图片,src是空的,
        data-image存放真实的地址,我们默认认为所有拥有data-image属性的图片都要做延迟加载
        
        <div>
            <img src="" data-img="image/1.png" />
        </div>
        
        2.导入JS后,我们可以暴露出一个API(例如init),只要执行init方法就会开始进行图片延迟加载,而且还可以支持一些配置参数:
        {
            threshold: [1],    //什么时候延迟加载
            animate: true,     //是否有动画:渐隐渐现(需要给图片设置opacity/transtion样式)
            attr: 'data-img',  //具备哪些属性的进行延迟加载(要求属性值一定是真实存在地址)
            onload: function(img){}  //每一张图片加载完要触发的函数
        }
*/
(function () {
    constructor(options) {
        //把配置项挂载到实例上
        this.options = options;
        
        //创建一个监听的实例
        let config = {
            threshold: options.threshold
        };
        this.observe = new IntersectionObserver(this.callback.bind(this), config)}
    
    //原型方法
    watch() {
        let { attr, animate } = this.options;
        //监听元素
        let allImgs = Array.from(document.querySelectorAll(`img[${attr}]`));
        allImgs.forEach(item => {
            //如果需要实现渐隐渐现动画,需要设置样式
            if(animate) {
                item.style.opacity = 0;
                item.style.transtion = "opacity .3s ease";
            }
            //监听图片所在的div
            this.observe.observe(item.parentNode);
        });
    }
    
    callback(changes){
        //回调触发
        changes.forEach( item => {
            let { isIntersecting, target } = item;
            if(isIntersecting) {
                this.lazyImg(target);
                observer.unobserve(target);
            }
        });
    }
    
    lazyImg(target){
        //单张图片延迟加载
        let { attr, animate, onload } = this.options;
        let img = target.querySelector('img'),
            trueImg = img.getAttribute(`${attr}`);
        img.src = trueImg;
        img.onload = () => {
            //图片加载成功
            img.style.display = 'block';
            if(animate) {
                img.offsetWidth; //目的:刷新浏览器渲染队列
                img.style.opacity = 1; 
            };
            //单张图片加载完都触发这个函数
            onload.call(this, img)
            img.removeAttribute(`${attr}`);
        }
        
    }
    
    class LazyImg {
        //静态对象
        static init( options={} ){
            //参数初始化
            options = Object.assign({
                threshold: [1],
                animate: true,
                attr: 'data-image',
                onload: function(){}
            }, options)
            //创造类的实例
            return new LazyImg(options);
        }
    }
    if(typeof window !== 'undefined') {
        window.LazyImg = LazyImg;
    }
    if(typeof module !=='undefined' && typeof module.exports !== 'undefined'){
        module.exports = LazyImg;
    }
})();
LazyImg.init();
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享