Gahing's blog Gahing's blog
首页
知识体系
  • 前端基础
  • 应用框架
  • 工程能力
  • 应用基础
  • 专业领域
  • 业务场景
  • 前端晋升 (opens new window)
  • Git
  • 网络基础
  • 算法
  • 数据结构
  • 编程范式
  • 编解码
  • Linux
  • AIGC
  • 其他领域

    • 客户端
    • 服务端
    • 产品设计
软素质
  • 面试经验
  • 人生总结
  • 个人简历
  • 知识卡片
  • 灵感记录
  • 实用技巧
  • 知识科普
  • 友情链接
  • 美食推荐 (opens new window)
  • 收藏夹

    • 优质前端信息源 (opens new window)
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Gahing / francecil

To be best
首页
知识体系
  • 前端基础
  • 应用框架
  • 工程能力
  • 应用基础
  • 专业领域
  • 业务场景
  • 前端晋升 (opens new window)
  • Git
  • 网络基础
  • 算法
  • 数据结构
  • 编程范式
  • 编解码
  • Linux
  • AIGC
  • 其他领域

    • 客户端
    • 服务端
    • 产品设计
软素质
  • 面试经验
  • 人生总结
  • 个人简历
  • 知识卡片
  • 灵感记录
  • 实用技巧
  • 知识科普
  • 友情链接
  • 美食推荐 (opens new window)
  • 收藏夹

    • 优质前端信息源 (opens new window)
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 前端基础

  • 应用框架

  • 工程能力

  • 应用基础

    • 兼容性

    • 前端安全

    • 国际化

    • 性能优化

      • 如何实现 script 并行异步加载顺序执行
      • yahoo前端优化35军规
      • 前端首屏优化 | 借助客户端能力提升 H5 首屏的 8 个手段
      • 前端优化性能指标
      • 4 种常用的前端性能分析工具
      • 前端性能预算经验
      • 前端图片分层加载详解
      • 基于CDN的前端优化可行性分析
      • 浅谈图片分层加载与懒加载
      • 浅谈 preload 预加载
      • 用 CAP 理论指导 Hybrid App 离线策略优化
      • 长列表

        • rc-virtual-list 源码解析
        • 为什么不用rAF进行滚动节流
        • 「前端长列表」开源库解析及最佳实践
        • 如何解决长列表的滚动白屏问题
          • 背景
          • 白屏成因
            • 输入事件滚动
            • 拖动滚动条
            • 代码控制
          • 解决方案
            • 算法优化
            • 预填充+防抖
          • 结语
          • 拓展阅读
      • 面试官问:如何实现 H5 秒开?
    • 换肤

    • 无障碍

  • 专业领域

  • 业务场景

  • 大前端
  • 应用基础
  • 性能优化
  • 长列表
gahing
2020/01/09
目录

如何解决长列表的滚动白屏问题

# 背景

之前提到过实现长列表的方式:监听 scroll 事件,在回调中计算渲染起止项并插入 dom 树,即只渲染可视区域

实现中发现,有些情况下可能会出现短暂的白屏现象

本文就来谈谈白屏成因和解决方案

# 白屏成因

造成滚屏有3种方式

  • 输入事件,如鼠标滚轮,键盘方向键
  • 拖动滚动条
  • 代码控制 scrollTop

每种方式,浏览器的内部处理都是不一样的

# 输入事件滚动

先说交互事件,以鼠标滚轮(mousewheel)为例

大部分浏览器采用的是异步滚动模型。在该模型中,视觉滚动位置在合成器线程中更新,并在 scroll 回调执行前可见

Alt text

内部执行顺序如下:

  1. 相关线程捕获到滚轮操作
  2. 通知合成器线程去滚动文档,包括更新滚动条位置
  3. 【事件循环1】执行滚轮事件回调
  4. 【事件循环1】执行 UI Render,触发 scroll 事件
  5. 【事件循环1】执行 scroll 事件回调

在执行「滚轮事件回调」的时候,文档位置可能已经更新过了。

因此,当 scroll 事件回调执行太久,就会出现文档已经滚动了,但是新的可视区域列表还未计算出来并更新到页面上,白屏就此产生。

我们可以禁用浏览器的异步滚动优化,即将「滚动文档操作」放到【事件循环1】的 UI Render 阶段去做

通过 passive=false 来实现 也就是说滚动文档需要与主线程交互

如果将原 scroll 事件的处理放到滚轮事件中处理的话, scrollTop 拿到的是之前的值(passive=false,浏览器并不知道是否要滚动,会不会被 prventDefault )。所以事件回调保持不变

这样下来,下一次的滚动文档必须等待本次 scroll 事件回调执行完毕,减缓了白屏现象,相应的,页面也显得没那么流畅

于是内部执行顺便变成如下:

  1. 相关线程捕获到滚轮操作
  2. 【事件循环1】执行滚轮事件回调
  3. 【事件循环1】执行 UI Render,触发 scroll 事件,通知合成器线程去滚动文档,包括更新滚动条位置
  4. 【事件循环1】执行 scroll 事件回调
document.addEventListener("scroll",function(e){
  console.log(window.scrollY)
  let start = performance.now()
  // 模拟耗时任务
  while( performance.now() - start <100){}
})
document.addEventListener("mousewheel",function(e){
},{ passive: false })
1
2
3
4
5
6
7
8

不过火狐中设置 passive: false 没有效果,浏览器的异步滚动优化无法禁用

详情看 Scroll-linked_effects (opens new window)

# 拖动滚动条

与滚轮事件有些许不同,chrome 的拖动滚动条没有异步滚动优化

其执行顺序如下:

  1. 相关线程捕获到滚动条被拖动
  2. 【事件循环1】执行 scroll 事件回调
  3. 【事件循环1】执行 UI Render,通知合成器线程去滚动文档,包括更新滚动条位置

效果就是卡一下,滚一下,滚过去的时候已经新的列表已经绘制完毕了,那么是不会有白屏问题的

document.addEventListener("scroll",function(e){
  console.log(window.scrollY)
  let start = performance.now()
  // 模拟耗时任务
  while( performance.now() - start <100){}
})
1
2
3
4
5
6

但是 Firefox 做了滚动优化,且不能禁用

导致 scroll 回调会在滚屏后执行,和上面输入事件效果一致,于是就出现了白屏

# 代码控制

通过 dom.scrollTop=xxx 进行自动滚动

一般是用来回滚列表显示某一项的

document.addEventListener("scroll",function(e){
  console.log(window.scrollY)
  let start = performance.now()
  // 模拟耗时任务
  while( performance.now() - start <1000){}
})

document.scrollingElement.scrollTop=100
1
2
3
4
5
6
7
8

发现 chrome 和 Firefox 此时的执行效果都一样,都是先执行 scroll 回调再滚动文档,没有什么异步滚动优化了。

这种情况下都不会出现白屏

# 解决方案

可以看到,chrome 的话只有输入事件可能导致白屏,且可以通过禁用滚动优化来减缓

而 Firefox 在输入事件和拖动滚动条的情况都会出现白屏,且基本不能解决

因此,我们只能提高 scroll 回调事件的执行效率,来减缓白屏的时长

目前有两个方向

  1. 算法优化
  2. 占位填充,通过防抖等滚动结束再计算

202309 补充:取消输入事件(鼠标滚轮、鼠标操作)默认行为,采用 JS 模拟滚动的方式,可以彻底解决。参考 精读《高性能表格》 (opens new window)

# 算法优化

具体可以参考我写的 前端长列表原理及优化 (opens new window) 一文

有两种策略:一种是可变滚动条总高度采用树状数组优化;一种是固定滚动条高度,通过定位项等几何关系算出

# 预填充+防抖

在 scroll 回调中先做填充,并对计算具体列表操作做防抖

效果上可能不太好,比较适合列表项带网络请求的情况,可以减少无效的网络请求

# 结语

因为很多都是浏览器自身的优化,不在规范范围内,本文较多结论是通过拓展阅读和实验结果得出,不保证正确。

欢迎指正~

# 拓展阅读

  1. https://hacks.mozilla.org/2016/02/smoother-scrolling-in-firefox-46-with-apz/
  2. https://developers.google.com/web/updates/2018/09/inside-browser-part4
编辑 (opens new window)
#HTML
上次更新: 2024/09/01, 23:56:56
「前端长列表」开源库解析及最佳实践
面试官问:如何实现 H5 秒开?

← 「前端长列表」开源库解析及最佳实践 面试官问:如何实现 H5 秒开?→

最近更新
01
浅谈代码质量与量化指标
08-27
02
快速理解 JS 装饰器
08-26
03
Vue 项目中的 data-v-xxx 是怎么生成的
09-19
更多文章>
Theme by Vdoing | Copyright © 2016-2024 Gahing | 闽ICP备19024221号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式