[Lazy loading] js implements lazy loading, vue implements image lazy loading instructions

lazy loading

Lazy loading, for a very long page, the content of the visible area is loaded first, and other parts are loaded when they enter the visible area

Lazy loading effect

It is a way to optimize webpage performance, which can greatly improve user experience. For example, there are many pictures on a page, but only a few appear on the first screen. At this time, if all the pictures are loaded at once, performance will be affected. At this time, lazy loading can be used, and the page is scrolled to the visible area and then loaded. Optimize the first screen loading.

Image lazy loading

Listen to the scroll bar scrolling event. When the height of the viewport + scroll height is greater than the offset (that is, the distance) at the top of the image location, for example, load the image resource

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>

Use the getBoundingClientRect() provided by the browser to optimize the above code: modify lazyload as follows:

getBoundingClientRect()Used to get the position information of the element relative to the viewport

        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;
                }
            }
        }

Optimize the above code again using IntersectionObserver()

IntersectionObserverIt is a constructor provided natively by the browser, which is used to check whether an element has entered the viewport ( viewport), whether the user can see the element

Modify the above code:

    <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>

Content lazy loading:

There are n orders on a page, and 10 orders can be displayed each time. When the user drags the scroll bar to scroll to the bottom of the order list, another 10 orders will be displayed until the loading is complete.

<!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>

详情可参考:(Detailed IntersectionObserver API_

IntersectionObserver Intersection Observer - Short Book (jianshu.com)

IntersectionObserver

5 Viewers for Browsers

app.directive

Custom global directives, it has two ways of writing

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

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

 install method in vue

It is available for us to develop new custom instructions and global registration components. The first parameter is vuethe constructor, and the second parameter is an optional option object

Then start to write the image lazy loading instruction of the extension instruction

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)
        }
    })
}

Use the extended image lazy loading directive:

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

extension:

Vue also has many tool libraries, among which there are similar APIs that can realize this function, such as:

useIntersectionObserver | VueUse

Guess you like

Origin blog.csdn.net/weixin_52479803/article/details/132428794