前端如何对超大量数据进行渲染

3,606 阅读2分钟

前端性能优化的方向

  • 分批渲染DOM:避免一次性渲染大量DOM,可以减少浏览器的性能压力。
  • 集中进行DOM操作:将多次DOM操作集中在一起进行,减少不必要的渲染。
  • 移除可视区域以外的DOM:对于不在可视区域的DOM元素,可以将其从DOM树中移除,以减少内存占用和渲染负担。

实现大量数据渲染的方法

1. 使用定时器

  • 方法概述:通过setInterval定时器分批插入DOM元素,每次处理一定数量的节点。
  • 代码示例
    export default (element, data) => {
      const fragment = document.createDocumentFragment();
      let timer = setInterval(() => {
        for (let i = 0; i < 20; i++) {
          const item = data.shift();
          if (!item) {
            clearInterval(timer);
            break;
          }
          const div = document.createElement('div');
          div.innerText = item.string;
          div.setAttribute('class', 'item');
          div.style.backgroundColor = item.color;
          fragment.appendChild(div);
        }
        element.appendChild(fragment);
      }, 100);
    };
    

2. 用requestAnimationFrame代替定时器

  • 方法概述:使用requestAnimationFrame来代替setInterval,它会在浏览器下一次重绘之前执行回调函数,从而更加精确地控制DOM操作的时机。
  • 代码示例
    export default (element, data) => {
      requestAnimationFrame(() => step(element, data));
    };
    const step = (element, data) => {
      const fragment = document.createDocumentFragment();
      for (let i = 0; i < 20; i++) {
        const item = data.shift();
        if (!item) {
          break;
        }
        const div = document.createElement('div');
        div.innerText = item.string;
        div.setAttribute('class', 'item');
        div.style.backgroundColor = item.color;
        fragment.appendChild(div);
      }
      element.appendChild(fragment);
      data.length && requestAnimationFrame(() => step(element, data));
    };
    

3. 使用虚拟滚动

  • 方法概述:虚拟滚动技术通过只渲染可视区域的数据来提高列表的渲染性能,并根据页面滚动动态添加或删除DOM节点。
  • 实现过程
    • 设置父节点的positionrelative,子节点的positionabsolute,并通过transform属性设置子节点的位置。
    • 使用boundary数组记录渲染区域的上下边界。
    • 使用startIndexendIndex来确定需要渲染的数据范围。
    • 定义step变量表示每次批量添加或删除的DOM数量,以及threshold阈值来触发DOM的添加或删除。
  • 代码示例
    export default (element, data) => {
      const boundary = [0, 0];
      let startIndex = 0;
      let endIndex = 0;
      const step = 10;
      const threshold = 200;
      const init = () => {
        element.style.position = 'relative';
        drop();
        initScroll(element);
      };
      const drop = () => {
        // ...代码逻辑同上...
      };
      const rise = () => {
        // ...代码逻辑同上...
      };
      const createElement = (item, mode) => {
        // ...代码逻辑同上...
      };
      const initScroll = (element) => {
        // ...代码逻辑同上...
      };
    };
    

虚拟滚动的scroll事件处理

  • 方法概述:通过监听滚动事件并结合阈值(threshold)来决定是否添加或删除DOM节点,以及是否触发数据的添加或删除。
  • 代码示例
    export const scroll = (element, func) => {
      let top = 0;
      return _.throttle(() => {
        const { scrollTop, scrollHeight } = element;
        if (scrollTop > top) {
          top = scrollTop;
          func({ mode: Mode.append, scrollTop, scrollBottom: scrollHeight - scrollTop - element.clientHeight });
        } else if (scrollTop < top) {
          top = scrollTop;
          func({ mode: Mode.before, scrollTop, scrollBottom: scrollHeight - scrollTop - element.clientHeight });
        }
      }, 100);
    };