突破性能瓶颈:基于虚拟滚动的大数据需求文档方案——告别卡顿与分页,实现Word级流畅体验

背景描述:

在前后端分离架构下,需求文档功能常面临两大核心痛点:

  1. 大数据展示难题:传统分页加载会打断用户浏览连续性,而一次性渲染海量数据(如10万+条目)又会导致DOM爆炸、页面卡顿
  2. 交互体验割裂:现有方案(如懒加载)仍需等待数据分块请求,无法实现类似Word的"无感知"无限滚动,影响用户沉浸感

当前市面主流方案(如分页、简单无限滚动)均存在性能或体验缺陷:

  • 分页:需手动切换页面,破坏长文档阅读流畅性[7]。
  • 基础无限滚动:滚动时DOM节点持续堆积,最终导致内存溢出。
  • 图表集成场景:若每行含动态图表(如ECharts),传统渲染会因重排/重绘引发严重卡顿。

本方案的创新价值

  • 虚拟滚动技术:仅渲染可视区域DOM(如屏幕内20条),动态计算滚动位置替换内容,实现10万+数据下的60FPS流畅滚动。

  • 前后端协同优化:

    • 前端:虚拟列表+动态高度计算(支持图文混排),结合Intersection Observer精准触发加载。

    • 后端:GraphQL分片查询(按需返回偏移量数据),减少网络传输压力。

      • 前端直接做数据切割也可以
  • "Word式"体验:无分页打断、无滚动白屏,支持快速定位与实时筛选。

二:开发过程中的各个原因分析

1、初次加载缓慢

1.1 、vxetable问题

导致此问题的原因是为解决不定高滚动条定位问题,初次加载时将所有数据都手动滚动了一遍导致首次加载很慢,

  • 手动触发滚动所有数据的原因:

    • 由于未加载的行是无法知道行高的。虚拟算法原理是估算虚拟行的行高 ,通过已渲染的行来纠正行高值。只有当所有数据被查看过一次之后才能达到和同等行高一样的效果。否则快速拉动滚动时会出现鼠标偏移,效果上会比同等行高略差。

好处:

对滚动条定位较为精准

坏处:导致首次加载缓慢,数据量越大,速度越慢,

1.2 树形结构问题

树形结构未采用虚拟树形,也会引发卡顿问题

2、定位滚动条较慢

定位较慢是因为每次定位的时候需要加载本次虚拟表格所需的所有dom ,大概8条左右,每个dom存在的内容不一,数据较多时就会存在定位卡顿问题。

3、页面操作较为卡顿

此问题是由以上俩问题综合导致

三:解决方案

优化本质思路:多次优化经验来看以及结合浏览器的当前状况以及chrome提出的优化模型来看,个人感觉前端优化本质从某种意义上来说可以总结为*"规避"*二字。

  • 规避性能瓶颈,避免让浏览器因为一些低效操作而卡顿或崩溃。
  • 规避不必要的资源消耗,减少加载时间、内存占用和带宽消耗。
  • 规避用户体验上的问题,确保页面交互流畅、响应迅速。
  • 规避架构和设计上的问题,使得代码更加可维护、可扩展,减少潜在的 Bug 和性能隐患。

我们现在卡顿基本上总结为短时间内渲染了大量dom

首先将树形改为虚拟树形

改为虚拟树形后可以大幅度减少dom的渲染量

优点:

  • 一定程度解决卡顿问题

不足:

  • 无法解决定位卡顿问题

  • 没有拖动功能

    • 见虚拟树形支持拖动改造文档
  • 没有新增时的占位符功能

    • 见虚拟树形支持拖动改造文档
  • 本身存在的问题

    • 滚动条拖动时整体都被拖动了
    • 右击新增时定位不准

定位卡顿存在不定高 问题以及显示内容可能较多引起的卡顿问题。

  • 若想解决显示内容较多引起的卡顿问题,我们可以采用懒加载
  • 若想解决不定高就需要手动触发所有数据的滚动 ,但是此操作会导致初次加载缓慢 ;会陷入死循环,所以我们要做*"规避"*

所以衍生出以下几种方案:

方案一:不解决首次加载缓慢,将内容改为懒加载

效果:见演示

优点:

  • 定位速度快,基本上秒定

缺点:

  • 因为加了懒加载,及时手动滚动了一遍,定位仍然不准确
  • 首次加载缓慢

结论:

不能采用

方案二:内容定高

效果:见演示

优点:

  • 定位速度快,基本上秒定
  • 定位最为精准
  • 首次加载迅速,接口响应时页面立马加载

缺点:

  • 定高没有自适应好看,而且可能会出现内部滚动条

建议:

  • 1、直接去掉滚动条,每次只展示一章节内容

  • 2、提供两两种模式

    • 不能跳转,但是能像当前这样查看所有章节
    • 能跳转,但是每次只能查看一章

结论:

不能采用,此方案达不到预期效果,且已有其他平台实现了此功能,例如cb

查看cb需求文档功能,进行思路解析:

  • 一:cb点击左侧菜单栏时有两种现象

    • 1、出现加载中的提示
    • 2、直接定位到内容区
  • 二:cb滚动条有两种现象

    • 1、正常上下滚动
    • 2、到了边界线时出现加载内容的loading提示
  • 三:从控制台观察cb的渲染量

    • 存在最大数量,比如30,超过此数量时会将内容进行替换,不超过时会对内容进行补充
  • 四:network观察点击现象

    • 1、点击菜单栏会进行http请求,且返回的内容为html,此操作可以看着是ssr渲染了,前端压力很小。

结论:

cb平台是采用了 分页概念以及 ssr手段*,从而达到了流畅 的效果,在交互时若跨度较大或超出上限,会存在loading反馈,提示用户正在加载中。*

我们项目本身情况:

  • vue框架、单页面应用。
  • 当前数据是一口气返回来的(后端也可配合修改,当前并无必要,但是当前后端请求时间较久,需优化)。
  • 未采用ssr渲染,若采用ssr渲染,我们的框架体系要多一个node服务端。

总结:

  • 也采用分页概念
  • 内容区 无ssr,采用懒加载
  • 此时理论情况上只需解决定位精准问题即可

根据总结得到以下方案三

方案三:树形懒加载+内容区分页+内容区懒加载

效果:见演示

优点:

  • 定位速度快,基本上秒定
  • 定位是所有方案包含改动前的效果中最精准的
  • 首次加载迅速,接口响应时页面立马加载
  • 内容高度自适应,符合需求

缺点:

  • 滚动加载后定位不是很准确

    • 经过算法优化已经较为精准

在实现过程中使用了一些 nextTick、setTimeout 的API,来确保当前内容加载完成 可以进行滚动了。

浏览器渲染机制 :浏览器会在 JavaScript 执行时进行页面的渲染更新,但有时渲染过程不会立即进行,特别是在多次 DOM 更新后,浏览器会合并多个布局计算和渲染任务。nextTick 只确保 DOM 更新完成,但并不保证浏览器已经重新渲染和计算了布局。

nextTick 执行时机 :即使 nextTick 回调在 DOM 更新后执行,浏览器的渲染进程可能还没有完成。这会导致滚动条操作没有生效,因为滚动位置是基于页面的当前渲染状态的。

setTimeout 的行为setTimeout 会让浏览器先完成所有当前的渲染和计算工作(即 DOM 更新和布局过程),然后在下一轮事件循环中执行你的回调。这样,滚动操作就可以在渲染完成后进行,确保滚动效果生效。

总结

当在 nextTick 中进行滚动时,滚动操作可能在浏览器完成布局计算之前执行,导致没有效果。为了确保滚动条位置能够生效,可以使用:

  • requestAnimationFrame:确保滚动操作在浏览器渲染完成后执行。
  • setTimeout:通过延迟执行滚动操作,确保在浏览器完成当前的渲染和布局后执行。

两者都能够有效解决在 DOM 更新后立即执行滚动操作的问题,确保滚动效果的正确应用。

相关推荐
前端Hardy16 分钟前
HTML&CSS:3D图片切换效果
前端·javascript
spionbo37 分钟前
Vue 表情包输入组件实现代码及完整开发流程解析
前端·javascript·面试
全宝38 分钟前
✏️Canvas实现环形文字
前端·javascript·canvas
金金金__40 分钟前
Element-Plus:popconfirm与tooltip一起使用不生效?
前端·vue.js·element
前端梭哈攻城狮1 小时前
uniapp图片上传添加水印/压缩/剪裁
前端·javascript·vue.js
天涯学馆1 小时前
前后端分离的 API 设计:技术深度剖析
前端·javascript·面试
爱编程的喵1 小时前
深入理解JavaScript原型机制:从Java到JS的面向对象编程之路
java·前端·javascript
独立开阀者_FwtCoder1 小时前
Cursor 1.0 重磅发来袭(毛骨悚然,开始学习你如何编码)
前端·javascript·github
五点六六六1 小时前
一些关于TreeShaking的AST的理解
前端·javascript·前端工程化
海盐泡泡龟2 小时前
“组件、路由懒加载”,在 Vue3 和 React 中分别如何实现? (copy)
前端·javascript·react.js