1、演示
2、方式一:手动监听滚动事件
原理:
手动监听滚动事件的原理是通过添加滚动事件监听器,当页面滚动时触发相应的回调函数,检测页面是否已经滚动到底部,从而触发加载更多数据的逻辑。
优点:
1、相对简单直接:不需要引入额外的插件或API,只需编写少量代码即可实现滚动加载更多功能。
2、可定制性强:可以根据项目需求灵活地调整滚动加载更多的逻辑,比如添加动画效果、加载提示等。
缺点:
1、性能问题:由于滚动事件触发频率较高,在数据量较大时可能会导致性能问题,需要额外的优化措施来提高性能。
2、兼容性问题:不同浏览器的滚动事件表现可能不尽相同,需要考虑浏览器兼容性。
3、实现复杂度:相比起使用现成的插件或API,手动监听滚动事件需要编写更多的代码。
实现代码:
<!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> * { margin: 0; padding: 0; box-sizing: border-box; } .loadingText { width: 100%; text-align: center; height: 50px; font-size: 50px; align-items: center; } .item { font-size: 50px; padding: 50px; width: 100%; background-color: rgb(71, 70, 70); text-align: center; margin-bottom: 10px; } </style> </head> <body> <div class="container"></div> <p class="loadingText">正在加载...</p> </body> <script> const container = document.querySelector('.container') const loadingText = document.querySelector('.loadingText') const body = { pageSize: 10, pageNum: 1, } let data = [] const total = 25 function getData() { let maxNum = body.pageNum * body.pageSize let startNum = (body.pageNum - 1) * body.pageSize + 1 let endNum = maxNum > total ? total : maxNum for (let i = startNum; i <= endNum; i++) { data.push(i) } renderPage() } function renderPage() { container.innerHTML = '' for (let i = 0; i < data.length; i++) { const div = document.createElement('div') div.innerText = data[i] div.classList = 'item' container.appendChild(div) } } let timer = null document.addEventListener('scroll', function (e) { if (timer) clearTimeout(timer) timer = setTimeout(() => { var contentHeight = document.documentElement.scrollHeight || document.body.scrollHeight var scrollTop = document.documentElement.scrollTop || document.body.scrollTop var windowHeight = document.documentElement.clientHeight || document.body.clientHeight if (contentHeight - scrollTop - windowHeight <= 0) { if (body.pageNum >= Math.ceil(total / body.pageSize)) { loadingText.innerHTML = '没有更多了' return } else { body.pageNum++ getData() } } }) }) window.onload = function () { getData() } </script> </html>
代码解析:
1、定义body模拟后端所需参数,pageSize表示一次加载多少条数据,pageNum表示第几次加载数据
2、定义data数据
3、定义total模拟通过调用接口后端返回的数据总条数
4、getData函数模拟从后端获取数据
5、renderPage函数用来将数据渲染到页面上
最后通过监听页面的滚动事件,当用户滚动页面时,会触发该事件。在事件处理函数中,首先会清除之前设置的定时器timer,然后再设置一个新的定时器。这样做是为了防止在用户不断滚动页面时频繁触发事件处理函数。
在定时器中,首先获取页面内容的高度、滚动距离和窗口高度。然后判断用户是否已经滚动到页面底部,即页面内容高度减去滚动距离和窗口高度的差值是否小于等于0。如果是,则判断当前页面页数是否已经达到总页数,如果是,则显示"没有更多了"的提示信息,否则增加页数并继续获取数据。
3、 方式二:使用原生的Intersection Observer API
原理:
通过监视指定元素与视口交叉的情况,当指定元素进入或离开视口时触发相应的回调函数。通过使用Intersection Observer API,可以实现监听滚动事件,从而触发加载更多数据的功能。
优点:
1、高性能:Intersection Observer API可以节省性能资源,因为它只在元素进入或离开视口时触发相关回调函数,相比手动监听滚动事件性能更好。
2、良好的浏览器支持:Intersection Observer API的浏览器支持较好,可以在大多数主流浏览器中使用。
3、灵活性:可以通过配置选项灵活地调整触发时机和回调函数,满足不同项目需求。
缺点:
1、相对复杂:相比手动监听滚动事件,使用Intersection Observer API需要理解一定的API和相关概念,相对复杂一些。
2、兼容性问题:虽然大多数主流浏览器支持Intersection Observer API,但仍有部分浏览器可能存在兼容性问题,需要进行兼容性处理。
实现代码:
<!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> * { margin: 0; padding: 0; } .loadingText { width: 100%; text-align: center; height: 50px; font-size: 50px; align-items: center; } </style> </head> <body> <div class="container"></div> <p class="loadingText">正在加载...</p> </body> <script> const container = document.querySelector('.container') const loadingText = document.querySelector('.loadingText') const body = { pageSize: 10, pageNum: 1, } let data = [] const total = 25 function getImgData() { let maxNum = body.pageNum * body.pageSize let startNum = (body.pageNum - 1) * body.pageSize + 1 let endNum = maxNum > total ? total : maxNum for (let i = startNum; i <= endNum; i++) { data.push(`https://img.beiqiai.com/img${i}.jpg`) } renderPage() } function renderPage() { container.innerHTML = '' for (let i = 0; i < data.length; i++) { const img = document.createElement('img') img.src = data[i] container.appendChild(img) } } const observer = new IntersectionObserver(entries => { // 遍历观察到的所有元素 entries.forEach(entry => { // 如果当前元素进入视口 if (entry.isIntersecting) { if (body.pageNum > Math.ceil(total / body.pageSize)) { loadingText.innerHTML = '没有更多了' observer.unobserve(loadingText) return } else { getImgData() body.pageNum++ } } }) }) observer.observe(loadingText) </script> </html>
代码解析:
总体思路不变,将方式一种的监听滚动事件换成使用IntersectionObserver来进行监听
在这里使用IntersectionObserver创建了一个观察器实例,并使用它来观察具有id "loadingText"的元素。当被观察的元素进入视口时,代码会检查当前页面数是否超过基于总项目数和页面大小计算得出的总页数。如果是这样,它会更新加载文本以指示没有更多项目可以加载,并停止观察该元素。否则,它调用getImgData()函数加载更多图像数据,并递增页面数。
4、方式三:使用插件
原理:
使用Element组件库中的Infinite Scroll 无限滚动插件。
优点:
1、快速实现:使用无限滚动插件可以快速实现滚动加载更多功能,节省开发时间和精力。
2、配置灵活:插件提供了丰富的配置选项,可以根据项目需求灵活定制加载更多数据的行为。
3、通用性:无限滚动插件通常具有良好的通用性,可以在多个项目中复用。
缺点:
1、依赖第三方插件:使用无限滚动插件需要依赖第三方库或插件,可能会增加项目的复杂度。
2、定制性受限:虽然插件提供了丰富的配置选项,但某些特定的定制需求可能无法通过插件满足,需要额外的工作。
3、性能问题:一些无限滚动插件可能存在性能问题,特别是在处理大量数据时,需要进行优化。
实现代码:
<template> <ul v-infinite-scroll="load" class="infinite-list" style="overflow: auto"> <li v-for="i in count" :key="i" class="infinite-list-item">{{ i }}</li> </ul> </template> <script lang="ts" setup> import { ref } from 'vue' const count = ref(0) const load = () => { count.value += 2 } </script> <style> .infinite-list { height: 300px; padding: 0; margin: 0; list-style: none; } .infinite-list .infinite-list-item { display: flex; align-items: center; justify-content: center; height: 50px; background: var(--el-color-primary-light-9); margin: 10px; color: var(--el-color-primary); } .infinite-list .infinite-list-item + .list-item { margin-top: 10px; } </style>
代码解析:
通过使用
v-infinite-scroll
指令,当滚动到列表底部时调用load
方法来加载更多数据。初始时列表为空,通过循环遍历count
来显示对应数量的列表项。每次调用load
方法时,count
增加2,从而增加2个列表项。在样式部分,
.infinite-list
设置了高度和样式,在.infinite-list .infinite-list-item
中设置了每个列表项的样式,包括高度、背景色、边距和文字颜色。.infinite-list .infinite-list-item + .list-item
用来给列表项之间添加间距。