场景
在前端开发中,我们经常碰到分页加载的需求,在PC端通常用分页组件就可以解决这种类型的场景。而当我们在移动端中,分页组件就显得有点不符合逻辑和正常的交互体验,所以滚动分页常常成为我们的一种选择,即页面滚动到底部时才能加载更多数据,以免一次加载太多数据造成性能问题。
关于滚动分页,其实还是存在一些不好处理的问题需要我们一一解决,往往想要实现符合需求的交互和良好的用户体验并不是那么轻易。因此需要将滚动分页做成可复用易维护的模块,以提高效率。
功能分析
首先,滚动分页根据不同的操作,需要渲染不同的状态的页面视图:
滚动分页视图对应了多个状态,有状态自然会有状态机。状态机的变化请看如下流程图:
上图的其中一个关键点是,需要根据分页的后台接口请求的请求前、请求中和请求后三个阶段的状态渲染不同的视图。 另外一个关键点是,涉及到的用户操作有两个:
初始化时,触发第一次远程数据加载
滚动到底部时,触发远程数据加载
实现
1、常规的实现方案,监听滚动事件,判断页面滚动距离和窗口大小,距离顶部位置的高度,根据判断生成下一组数据并插入,直至数据量为空。
2.可以在页面的最底部加一条横线,当视图滚过横线之时,则需要添加数据,重复这样的操作,直至数据为空即可。
大致的逻辑类似以下代码,根据判断横线dom是否在区域内判断是否请求数据。
if (entries[0].intersectionRatio > 0) {
console.log('进入可视区域');
// do something
} else {
console.log('移出可视区域');
// do something
}
});
性能优化
使用了窗口的滚动事件会对浏览器或者程序运行载体有一定的性能影响,如果导致页面卡顿的情况,我们可以通过使用防抖和节流,减少对页面滚动的监听频率。
防抖:
实现原理:定义一个计时器,规定delay时间内触发则重置计时器,延迟时间根据需求制定。
应用场景:(就举一个跟它名字比较切合的例子吧)点击按钮,手抖了,误点了两下,事件重复触发!!为了防止出现这个局面,我们的防抖就可以派上用场了。
/**
* 防抖函数
* @param {*} event 触发的事件
* @param {*} delay 延迟时间
* @return {*} function 闭包
*/
function deBounce (event, delay) {
let timeOut = null
// 闭包缓存timeout
return () => {
// 若timeout存在则重置
if (timeOut) {
clearTimeout(timeOut)
}
timeOut = setTimeout(event, delay)
}
}
节流:
实现原理:定义一个计时器,控制阀门的开关,必须经过一定的时间后才可以重新触发事件。
应用场景:滚动事件,窗口resize事件等各种高频率触发的事件,大都可以用节流来降低其频率。
/**
* 节流函数
* @param {*} event 触发的事件
* @param {*} delay 水阀打开的时间
* @return {*}
*/
function throttle (event, delay) {
let flag = true
// 闭包缓存数据
return () => {
// 水阀关闭,不触发任何事件
if (!flag) {
return false
} else {
// 触发事件一次后,将水阀关闭
event()
flag = false
// 控制水阀在延迟多久后会重新打开
setTimeout(() => {
flag = true
}, delay)
}
}
/* 请注意,节流函数并不止上面这种实现方案,
例如可以完全不借助setTimeout,可以把状态位换成时间戳,然后利用时间戳差值是否大于指定间隔时间来做判定。
也可以直接将setTimeout的返回的标记当做判断条件-判断当前定时器是否存在,如果存在表示还在冷却,并且在执行fn之后消除定时器表示激活,原理都一样
*
}