Vue2开发实现滚动加载

361 阅读2分钟

在前端项目开发过程中,很多时候会有滚动加载的需求。正常我们会手动写一个通过对滚动条触底的计算,对于多次函数调用会利用防抖去解决,带来极大的不便。

对此JS有一个交叉观察器API来实现另一种滚动加载——IntersectionObserver。

一、IntersectionObserverAPI是什么?

交叉观察器API是一个用以异步检测目标元素与祖先元素或顶级文档的视口相交情况变化的方法。

  • 页面滚动时“懒加载”图像或其他内容;
  • 通过“无限滚动”来加载并显示更多的内容,或可替代翻页功能;

以上的两种情况需要用到相交检测。

二、IntersectionObserverAPI在我们vue实际开发中如何使用?

  1. 首先在vue组件create或者mounted时,可以创建IntersectionObserverAPI,如下:
methods:{
    getObserver() {
        const box = document.querySelector(".box");
        const loadMore = document.querySelector(".loadMore");
        this.observer = new IntersectionObserver(
            (entries) => {
                // entries参数是IntersectionObserverAPI的特有参数,其中有7个值,通过参数的
                // isIntersecting值来判断视口是否可见来执行相应操作
                // isIntersecting返回true和false
                entries.forEach((entry) => {
                    if (entry.isIntersecting) {
                        this.loadData();
                        this.observer.unobserve(loadMore); // 每次观察之后停止观察
                        this.observer.disconnect();        // 停止观察之后关闭观察器
                        this.getObserver();                // 再次调用观察器操作,以便未观察完闭
                                                           // 的元素继续完成观察
                    } else {
                        this.cunt = 0;
                    }
                });
            },
            {
                root: box,
                threshold: 1
            }
        );
        this.observer.observe(loadMore);
    },
}
mounted() {
    this.getObserver();
}
  1. 在methods中添加数据调用方法
methods:{
    loadData() {
        this.data.push({
            id: this.id,
            url: imgUrl
        });
    }
}
  1. 在判断中执行数据调用方法
entries.forEach((entry) => {
    if (entry.isIntersecting) {
        // 执行视口显示操作
        this.loadData()
    } else {
        this.cunt = 0;
    }
}
  1. 完整代码为
<template>
    <div>
        <div>
            <div class="box">
                <div class="scrollContainer">
                    <div class="img" v-for="(item, index) in data" :key="item.id">
                        <img :src="item.url" alt="" />
                    </div>
                    <div class="loadMore">加载更多。。。</div>
                </div>
            </div>
        </div>
    </div>
</template>
<script>
import imgUrl from "@/assets/imag/下载.jpg";
export default {
    data() {
        return {
            observer: null,
            isInitListen: false,
            accountList: [], // 已加载的数据列表
            totalAcount: 0, // 数据总数(已加载+未加载)
            data: [
                {
                    id: 0,
                    url: imgUrl
                }
            ],
            id: 0,
            cunt: 0
        };
    },
methods: {
    getObserver() {
        const box = document.querySelector(".box");
        const loadMore = document.querySelector(".loadMore");
        this.observer = new IntersectionObserver(
            (entries) => {
                entries.forEach((entry) => {
                    if (entry.isIntersecting) {
                        this.loadData();
                        this.observer.unobserve(loadMore);
                        this.observer.disconnect();
                        this.getObserver();
                    } else {
                        this.cunt = 0;
                    }
                });
            },
            {
                root: box,
                threshold: 1
            }
        );
        this.observer.observe(loadMore);
    },
    loadData() {
        this.cunt++;
        console.log(`方法调用了${this.cunt}次`);
        this.id++;
        this.data.push({
            id: this.id,
            url: imgUrl
        });
        }
},
mounted() {
    this.getObserver();
    }
};
</script>
<style lang="scss">
.box {
    border: 1px solid red;
    height: 800px;
    width: 300px;
    margin: 40px auto;
    overflow: scroll;
    .scrollContainer {
        display: flex;
        align-items: center;
        justify-content: space-evenly;
        flex-wrap: wrap;
        .img {
            img {
                width: 100%;
            }
        }
        .loadMore {
            line-height: 60px;
            border: 1px blue solid;
        }
    }
}
</style>