vueTable大数据展现优化

背景

clipboard.png
大数据项目根据用户输入代码查询数据,用户的代码不可控(好比select from db limit 5000),有可能一页需求要求展现100行5000列数据。因为是用户代码实时查询的数据,后端不可能将全部查询结果都存储。所以,查询的结果是实时的、全量的,分页和排序都须要前端去实现前端

现状

刚开始的接手项目的时候彻底不能展现十万级的数量,chrome标签页直接崩溃。这个在分析了需求,展现数据不须要响应式后,用了Object.freeze()后就能够勉强展现十万级数据。虽然仍是卡顿,可是需求已经实现。(值得注意的是Object.freeze()并非深度冻结,实际应用中对象要进行递归操做。)vue

下面图展现的是100行乘以一千列,在左右拖拽0-150列。目前也对超过两百列的数据进行横向的懒加载操做,实现原理时监听scroll事件滚动到末尾时截取对应下一组数据,而后将滚动条恢复到头部。能够从gif中明显感受到这个过程是滚动条恢复到原状之间耗时比较长。并且当用户想要看前一组数据的最后一项和后一组数据的前一项时,js就要不停地作截取数据的操做从新渲染,开销很是大。git

clipboard.png

利用chrome devtool performance进行性能分析。(进行性能分析时使用隐身模式避免chrome插件对结果分析形成误差)github

  • 观察FPS图表,有几段红帧证实这过程当中页面超负荷,会出现卡顿响应缓慢等。

    clipboard.png

  • 选中红帧区域,Main区域发生变化,变为当前选择时段的函数调用栈详情。点击会在下面的Summary里发现对应的信息以及警告提示回流可能为性能的瓶颈Forced reflow is a likely performance bottleneck.。对应的问题出在监听scroll事件后出现的js代码中,执行的次数很是多。不只须要去读取scrollLeft值,还由于从新渲染数据时使组件纵向高度发生了改变,进而屡次触发了element-ui组件的updateScrollY方法。从Screenshots能够看到这时恰好是数据移动到最后要对数据进行截取从新渲染。
    clipboard.png
  • 从上图summary能够看到js的运行压力很大,勾选memory项纪录js heap占用状况,查看到占用高达161mb-325mb
    clipboard.png

优化要点

  • table的数据并不会有修改的须要,仅仅是展现,并不须要响应式。Object.freeze()能够阻止vue追踪属性的变化,减小性能的开销
  • 因为数据展现的table不只大量并且常常变换数据集。为了减小回流和重绘,table作绝对定位脱离文档流,避免布局抖动。
  • 因为后端返回的数据一组表头和内容分开的数组,而开源element-ui的vue组件都是以key:value的形式,大量数据状况下仅仅是将数组转化为key:value的形式就花费掉几百毫秒的时间。开源组件能解决的是通用状况,这种状况下为了尽可能减小开销重写适用于业务的table组件仍是颇有必要的。chrome

    //后端返回的格式
    data = [
        columnName: ['col1', 'col2', ……],
        columns: [
            ['1', '2', ……],
            ['1', '2', ……],
            ……
        ]
    ]
    // 开源组件须要的格式
    data = [
        {
            col1: '1',
            col2: '2',
            ……
        },
        {
            col1: '1',
            col2: '2',
            ……
        }
    ]
  • 后端返回的数据量有可能高达百万级,尽管前端进行分页仍是有可能要展现到数量达十万。其中行最多每页只展现100条,可是列由ide用户执行的代码决定,这里主要影响性能的是列数。列数有可能为1000条,模拟横向懒加载,将拿回来的数组截取部分展现,减小页面上的dom节点。可是目前模拟懒加载的方式用户体验很差。element-ui

    • 为了解决横向滚动时相邻列的数据可以展现在同一屏上,而不需屡次来回切换,首先作的工做是在截取数据时保留前一屏的数据,拖动后滚动条回到中间位置,在必定范围内不须要屡次滚动才能查看。(以下图)

      clipboard.png

- 但这种方式也是很是不友好,每次滚动到最后要去检测用户是否按着鼠标有没有抬起,防止触发屡次数据从新渲染。由于这种状况下,用户拖一次只能加载一组新数据,滚动条便回到了中间位置,若是用户须要看到最后一组数据就要屡次操做。正常的懒加载应该是有一条适应高度的滚动条拖拽,无缝链接。

- 懒加载方式常见的有:
    1. 淘宝一屏用元素占据必定的高度,而后再去拉图片数据。滚动条便适应高度的拖动距离。但这种方式仍是须要元素占位,淘宝一页的数据量其实不算大,由于它结合了分页。
    2. 掘金沸点的无限加载:掘金的方式是监听到底部时,再去拉响应的数据追加,滚动条会自适应滚到相对应的地方。可是掘金这种懒加载一直加载数据没有截取掉旧数据,因此滚动条距离也是一直适应数据的。尝试将掘金沸点一直拖动到2000条,网页已经开始有点卡顿。而在ide项目中,两千条数据算是少许数据。
- 启发于[https://github.com/tangbc/vue-virtual-scroll-list](vue-virtual-scroll-list),利用了padding值模拟了淘宝固定高度,不须要元素占位,模拟出所有数据量的滚动条纵向滚动距离,拖动时彻底无感知数据的从新渲染。目前vue-virtual-scroll-list只支持纵向,但稍微改造下就能用在ide项目的横向懒加载。(改造后以下图,gif软件录制时稍微有点卡顿感)

clipboard.png

  • scroll长时间运行的从新计算样式事件,其时间若是超过 16.7 毫秒,而且刚好发生在滚动期间,致使用户体验到明显的抖动。为了在拖动过程当中数据变化以连贯、平滑进行过渡,函数节流改setTimeout为requestAnimationFrame(rAF),由系统来决定回调函数的执行时机;它能保证回调函数在屏幕每一次的绘制间隔中只被执行一次,这样就不会引发丢帧现象,也不会致使渲染数据出现卡顿的问题,而且rAF能兼容到ie9以上了。

优化后结果分析

拖动几百条数据截取的performance在FPS图表中已经没有最初的红标,没有Forced reflow,每帧的rendering也由rAF控制在16.7ms之内,js内存占用也从161mb-325mb,下降到157mb-196mb。后端

clipboard.png

组件接口设计原则

复用性:配置参数的方式去差别化体现,参数的可配置性提升了组件的复用率和灵活性。
可维护性:组件化后,组件内部的逻辑只对组件负责,外部的逻辑只经过配置参数适配,提升了代码的逻辑清晰度,能够快速定位代码出现问题的地方。数组

这个组件设计时对外提供toLefttoRightonScroll事件,分别是滑动过程当中到了头、尾,及滑动过程的回调。提供了offsetremainbench参数表示刚渲染时的误差,显示的列数,及保留多少列在实际dom中。dom

小结

之前没有想过js也会承受那么大的压力,一点点优化都能显著减轻内存。在写代码时要特别关注高频事件的触发,一切的优化方向就是在实现功能的前提下减小从新渲染的发生。ide

相关文章
相关标签/搜索