滚动加载--优化滚动性能
亲爱的儿子,我还没有追到你娘,提前祝你新年快乐!爸爸明年会努力的~
背景
思路
- 最容易想到的就是做一个底部检测,当用户滚动到底部时再请求下一部分内容,并将结果拼接到上一次结果之后。
- 但很多情况下返回内容不只有字符,还有媒体文件,这个时候浏览器的下载通道很容易被占满。这个时候 我们实际需要的其实是用户看得到的内容去做加载就可以,用户看不到的可以先用墓碑(占位符)。这么做可以大大减少流量浪费~
- 理清思路后大致实现不难,不过滚动是个高频发事件,这在一些极端 case 下容易引起卡顿,需要进行优化,这也是本文重点。
优化滚动
- 常用两种优化方式
-
Throttle
: 允许我们限制**响应的数量。通过限制每秒回调的数量的方式来达到优化目的;
// const fn = throttle(e => console.log(e, "123"), 300)
// const scroll = listen(document.body, "mousewheel", fn)
function throttle(...props) {
const [fn, threshhold = 250, scope] = props
let last
let deferTimer
return event => {
const context = scope || this
const now = +new Date()
if (last && now < last + threshhold) {
clearTimeout(deferTimer)
deferTimer = setTimeout(() => {
// 停止滚动延迟后触发
last = now
fn.apply(context, [event, ...props])
}, threshhold)
} else {
last = now
fn.apply(context, [event, ...props])
}
}
}
-
Debounce
: 事件发生时,不会立即**回调。而是等待一定的时间并检查相同的事件是否再次触发。如果是,我们重置定时器,并再次等待。如果在等待期间没有发生相同的事件,我们就立即**回调。 => 使用于滚动结束后执行操作
// 基本防抖
function debounce(...props) {
const [action, wait = 200] = props
let last
return function(event) {
const ctx = this
clearTimeout(last)
last = setTimeout(function() {
action.apply(ctx, [event, ...props])
}, wait)
}
}
判断到达底部
- scrollHeight <=> scrollTop + offsetHeight
对于预加载可以适当加个预加载高度 这样提前加载会更加流畅
dom 回收
由于每一个节点都会增加一些额外的内存、布局、样式和绘制。进行 DOM 回收可以使 dom 保持在一个比较低的数量上,进而加快上面提到的这些处理过程。在 dom 移除页面一定高度后对节点进行 move()
操作。为了保证滚动条的正确比例和防止高度塌陷,需要显示的声明高度。