【懒加载】js实现懒加载、vue实现图片懒加载指令

懒加载

延迟加载,对于一个很长的页面,优先加载可视区域的内容,其他部分等进入可视区域时再加载

懒加载作用

是一种网页性能优化的方式,它能极大的提升用户体验。比如一个页面中有很多图片,但是首屏只出现几张,这时如果一次性把图片全部加载出来会影响性能。这时可以使用懒加载,页面滚动到可视区再加载。优化首屏加载。

图片懒加载

监听滚动条滚动事件,当视口的高度+滚动高度,大于图片所在位置举例顶部的偏移量时(也就是距离),加载图片资源

index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <div style="height: 1200px;"></div>
    <img src="https://ts2.cn.mm.bing.net/th?id=OIP-C.Fi0rA6s5NQ1VIlwp9IzhIgHaKe&w=210&h=297&c=8&rs=1&qlt=90&o=6&pid=3.1&rm=2"
        data-src="https://ts3.cn.mm.bing.net/th?id=OIP-C.D-43aYLc7We_sO_5ZSMIXgHaFj&w=288&h=216&c=8&rs=1&qlt=90&o=6&pid=3.1&rm=2" />
    <script>
        let img = document.getElementsByTagName("img");
        let num = img.length;
        let count = 0; 

        lazyload();

        window.addEventListener('scroll', lazyload); 

        function getTop(el) {
            var T = el.offsetTop;
            // 迭代地获取元素及其父元素相对于文档顶部的累积偏移量
            while (el = el.offsetParent) {
                T += el.offsetTop;
            }
            // 循环后 返回元素相对于文档顶部的总偏移量
            return T;
        }

        function lazyload() {
            //视口高度
            let viewHeight = document.documentElement.clientHeight || document.body.clientHeight;
            //滚动高度
            let scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
            for (let i = count; i < num; i++) {
                // 元素现在已经出现在视口中
                if (getTop(img[i]) < scrollTop + viewHeight) {
                    // 如何图片等于默认图 则加载新图
                    if (img[i].getAttribute("src") !== "https://ts2.cn.mm.bing.net/th?id=OIP-C.Fi0rA6s5NQ1VIlwp9IzhIgHaKe&w=210&h=297&c=8&rs=1&qlt=90&o=6&pid=3.1&rm=2") continue;
                    img[i].src = img[i].getAttribute("data-src");
                    count++;
                } else {
                    break;
                }
            }
        }
    </script>
</body>

</html>

使用浏览器提供的getBoundingClientRect(),优化上述代码: lazyload修改如下:

getBoundingClientRect()用于获取元素相对于视口的位置信息

        function lazyload() {
            let viewHeight = document.documentElement.clientHeight || document.body.clientHeight;
            for (let i = count; i < num; i++) {
                // getBoundingClientRect() 用于获取元素相对于视口的位置信息
                // 当相对于视口位置 小于等于视口时说明图片已经可见了
                if (img[i].getBoundingClientRect().top < viewHeight) {
                    if (img[i].getAttribute("src") !== "https://ts2.cn.mm.bing.net/th?id=OIP-C.Fi0rA6s5NQ1VIlwp9IzhIgHaKe&w=210&h=297&c=8&rs=1&qlt=90&o=6&pid=3.1&rm=2") continue;
                    img[i].src = img[i].getAttribute("data-src");
                    count++;
                } else {
                    break;
                }
            }
        }

使用IntersectionObserver()再次优化上述代码

IntersectionObserver是浏览器原生提供的构造函数,用于查看某个元素是否进入了视口(viewport),用户能否看到该元素

修改上述代码:

    <script>
        let img = document.getElementsByTagName("img");

        const observer = new IntersectionObserver(changes => {
            console.log(changes)
            //changes 是被观察的元素集合
            for (let i = 0, len = changes.length; i < len; i++) {
                let change = changes[i];
                // 通过这个属性判断是否在视口中
                if (change.isIntersecting) {
                    const imgElement = change.target;
                    imgElement.src = imgElement.getAttribute("data-src");
                    // IntersectionObserver 对象的一个方法,用于停止对指定元素的观察
                    observer.unobserve(imgElement);
                }
            }
        })
        Array.from(img).forEach(item => {
            observer.observe(item)
        });
    </script>

内容懒加载:

一个页面有n条订单,每次可以给他显示10条,等用户拖动滚动条滚动到订单列表底部一段距离时再显示10条,直到加载完

<!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>
        span {
            display: block;
        }
    </style>
</head>

<body>
    <div style="height: 1200px;">1</div>
    <div class="box">
        <span>开始</span>
    </div>

    <script>
        let div = document.getElementsByClassName("box")[0];
        let lastSpanElement = div.querySelector('span:last-child');

        const observer = new IntersectionObserver(changes => {
            console.log(changes)
            //changes 是被观察的元素集合
            for (let i = 0, len = changes.length; i < len; i++) {
                let change = changes[i];
                // 通过这个属性判断是否在视口中
                if (change.isIntersecting) {
                    for (let i = 0; i < 10; i++) {
                        const spanElement = document.createElement('span');
                        spanElement.textContent = `这是第 ${i + 1} 个<span>标签`;
                        div.appendChild(spanElement);
                    }
                    observer.unobserve(lastSpanElement);
                    lastSpanElement = div.querySelector('span:last-child');
                    observer.observe(lastSpanElement)
                }
            }
        })
        observer.observe(lastSpanElement)
    </script>
</body>

</html>

详情可参考:(IntersectionObserver API详解_

IntersectionObserver 交叉观察器 - 简书 (jianshu.com)

IntersectionObserver

浏览器的5种观察器

app.directive

自定义全局指令,它有两种写法

// 注册(对象形式的指令)
// 所有生命周期都可
app.directive('my-directive', {
  /* 自定义指令钩子 */
})

// 注册(函数形式的指令)
// 固定只在2个生命周期上触发, mounted 和 updated 
app.directive('my-directive', () => {
  /* ... */
})

 vue中install方法

可供我们开发新的自定义指令跟全局注册组件,第一个参数是vue的构造器,第二个参数是可选的选项对象

然后再开始编写扩展指令之图片懒加载指令

export default {
  install (app) {
    app.component(XtxSkeleton.name, XtxSkeleton)
    app.component(XtxCarousel.name, XtxCarousel)
+    defineDirective(app)
  }
}

import defaultImg from '@/assets/images/200.png'
// 扩展指令
const defineDirective = (app) => {
    // 图片懒加载
    app.directive('lazyload', {
        mounted(el, binding) {
            const observer = new IntersectionObserver(([{ isIntersecting }]) => {
                if (isIntersecting) {
                    // 停止观察
                    observer.unobserve(el)
                    el.onerror = () => {
                        // 图片加载失败 设置默认图
                        el.src = defaultImg
                    }
                    // binding 是对象,.value就是绑定指令的值
                    el.src = binding.value
                }
            }, {
                //目标元素和根元素相交部分的比例达到该值的时候,
                //callback 函数将会被执行
                threshold: 0.01
            })
            // 开始观察
            observer.observe(el)
        }
    })
}

使用扩展的图片懒加载指令:

<img alt="" v-lazyload="goods.picture" />

扩展:

vue还有许多工具库,其中也有类似的api可以实现这种功能,列如:

useIntersectionObserver | VueUse

猜你喜欢

转载自blog.csdn.net/weixin_52479803/article/details/132428794