背景描述:
在前后端分离架构下,需求文档功能常面临两大核心痛点:
- 大数据展示难题:传统分页加载会打断用户浏览连续性,而一次性渲染海量数据(如10万+条目)又会导致DOM爆炸、页面卡顿
- 交互体验割裂:现有方案(如懒加载)仍需等待数据分块请求,无法实现类似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 更新后立即执行滚动操作的问题,确保滚动效果的正确应用。