前端渲染优化 - 大量数据展示

二叶草 2020年2月7日04:27:00优化评论阅读模式

在很多数据信息3D渲染时,一次性将全部连接点都3D渲染到网页页面处时,会发觉网页页面载入和翻转的那时候出現不能接纳的卡屏。根据 performance 开发者专用工具能够看见,这时的特性花销,绝大多数都被 paint,render 消耗掉了。

如果数据节点比较简单,你可以选择将节点分片渲染到页面上:

const ul = document.getElementById('container');const total = 100000;const once = 20;let index = 0;
function loop(curTotal, curIndex) {  if (curTotal <= 0) return false;
  let pageCount = Math.min(curTotal, once);  requestAnimationFrame(() => {    let fragment = document.createDocumentFragment();    for (let i = 0; i < pageCount; i++) {      let li = document.createElement('li');      li.innerText = curIndex + i + ' : ' + ~~(Math.random() * total)      fragment.appendChild(li);    }    ul.appendChild(fragment);    loop(curTotal - pageCount, curIndex + pageCount);  })}
loop(total, index);

当节点较复杂时,如果将节点都渲染在页面上时,依然会造成页面卡顿。

所以在这种场景下,我们可以选择渲染可视区域的方式,先来看一个 demo 效果:

前端渲染优化 - 大量数据展示

在图中渲染了一个 10k 行,100列的多层级列表。页面滚动时还是比较流畅的。

虚拟滚动

要实现只渲染可视区域的关键,就是实现虚拟滚动,实现方式也比较简单:

  • 最外层容器设置为一个合理的可视范围;
  • 内层容器为所有数据高度之和;
  • 垂直方向渲染一个占位元素,高度为滚动高度之和;
  • 水平方向渲染一个占位元素,宽度为水平滚动之和;

HTML 结构

<div ref={scrollRef} style={{ height: '500px', width: '500px',overflow: 'scroll' }}>  <div style={{ height: `${nodeHeight * nodeYLength}px`, width: `${nodeWidth * nodeXLength}px` }}>    <div style={{ height: `${nodeHeight * srcollYLength}px` }} />    {/* render node element */}  </div></div>

滚动事件监听

当页面滚动时需要重新计算显示内容,通过暴露 x, y 给主程序使用:

import { useState, useEffect } from 'react';/** * scroll hook * @param {*} scrollRef * @returns {Object} */export default function useScroll(scrollRef) {  const [position, setPosition] = useState({x: 0, y: 0});
  useEffect(() => {    const handler = () => {      if (scrollRef.current) {        setPosition({          x: scrollRef.current.scrollLeft,          y: scrollRef.current.scrollTop,        });      }    };
    if (scrollRef.current) {      scrollRef.current.addEventListener('scroll', handler, {        capture: false,        passive: true,      });    }
    return () => {      if (scrollRef.current) {        scrollRef.current.removeEventListener('scroll', handler);      }    };  }, [scrollRef]);
  return position;}

有了 HTML 结构和滚动位置,只需要将源数据截取出需要渲染的数据就可以了。

全量计算

只有当用户变更行列配置时(如展开 / 收起行树)等时候需要对全量的行或者列进行枚举。但是由于树的操作不属于频繁调用的操作,而列数通常相对较少,因此也能达到不错的效果。

其他细节

  • 层级提升:因为渲染区域会频繁操作 DOM , 我们可以使用 CSS 中的 will-change 属性,从原来的渲染层中独立出来,减少一部分浏览器渲染开销;
  • 留出一定的缓冲区: 为了避免频繁更新组件的状态,减少 VDOM 比较的次数,一般需要预留四周 0.5 到 1 屏的缓冲区,当视图移出缓冲区时,再渲染下一个缓冲区的节点;
  • 可视区变化:当视图大小发生变化时(特别是变大时),为了避免出现空白,我们需要重新计算可视范围;
  • 数据占位:在实际项目中,数据一般都来自网络请求,而这些数据很可能是一个数据库中的记录。既然有如此大的数据量,那么数据请求上的优化也是一个重要的因素。一次性返回所有的数据可能在服务层和传输层造成较大的性能问题,由于数据库读取主键的速度比读取整个条目快得多,因此可以首先请求所有条目的主键,如 id,然后渲染一些条目的占位符,之后当用户滚动到某一个视图时,再通过主键集合请求该视图附近的数据。

如果虚拟滚动还是无法满足对性能的渴求,还有一些别的方案,例如,完全抛弃 DOM 节点,采用 canvas 自行进行元素的布局和绘制。

小结

前端渲染大量数据的时遇到性能瓶颈的原因可能有:

  • 计算耗时:js 计算大量数据(10k+) 在时间复杂为 O(n²) 空间复杂度为O(1)耗时需要一秒左右,每增加一个空间复杂度系数,整体耗时就会翻倍;
  • 在渲染大量节点时 paint 耗时,往往比 js 计算更耗时;

优化思路有以下几种:

  • 避免时间复杂度 O(n²) 以及以上的遍历;
  • 只渲染需要显示的节点;
  • 优化显示方式:折叠显示内容、合并一定范围内节点显示(常用于网络图);
  • 将需要重绘的节点提升层级,交互区域提升层级(如:拖拽中的节点);
  • 使用 workers 开启多线程,把计算任务放在不同线程里。

以上就是所有内容,希望给你在渲染大量数据时提供一点思路。

本文来源于:前端渲染优化 - 大量数据展示-变化吧门户
特别声明:以上文章内容仅代表作者本人观点,不代表变化吧门户观点或立场。如有关于作品内容、版权或其它问题请于作品发表后的30日内与变化吧联系。

  • 赞助本站
  • 微信扫一扫
  • weinxin
  • 加入Q群
  • QQ扫一扫
  • weinxin
二叶草
nginx解析漏洞 优化

nginx解析漏洞

phpstudy(小皮模板存在nginx解析漏洞) 影响版本 phptsuy8.1.07的Nginx1.5.11版本影响版本 phptsuy8.1.07的Nginx1.5.11版本 phpstudy介...
网站SEO优化基础流程(新手必看) 优化

网站SEO优化基础流程(新手必看)

宝塔面板搭建一个获取网站的Favicon图标的APIgetFavicon是一个可以获取网站的Favicon图标并显示在你的网页上的项目。安装方法很简单,属于开箱即食。这篇文章还是基于宝塔面板来搭建。 ...

发表评论