滚动加载--优化滚动性能

亲爱的儿子,我还没有追到你娘,提前祝你新年快乐!爸爸明年会努力的~

滚动加载--优化滚动性能

背景

  • 最近有用阿里云 sdk 中的日志服务,在它的 api 里发现它并不支持分页查询,倒是有个 offset 指定日志起点,所以需要配合鼠标滚动做一个懒加载。

思路

  1. 最容易想到的就是做一个底部检测,当用户滚动到底部时再请求下一部分内容,并将结果拼接到上一次结果之后。
  2. 但很多情况下返回内容不只有字符,还有媒体文件,这个时候浏览器的下载通道很容易被占满。这个时候 我们实际需要的其实是用户看得到的内容去做加载就可以,用户看不到的可以先用墓碑(占位符)。这么做可以大大减少流量浪费~
  3. 理清思路后大致实现不难,不过滚动是个高频发事件,这在一些极端 case 下容易引起卡顿,需要进行优化,这也是本文重点。

优化滚动

  • 常用两种优化方式
  1. 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])
    }
  }
}
  1. 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() 操作。为了保证滚动条的正确比例和防止高度塌陷,需要显示的声明高度。