跳转到内容

分批渲染

什么是分批渲染?

分批渲染是一种优化大量数据渲染的方法,它将一次性渲染大量数据的操作分解为多次小批次的渲染。通过分批渲染,可以有效减少浏览器的计算压力,避免主线程长时间被占用,从而减少页面卡顿现象。

分批渲染的主要思路是:

  1. 每次只渲染一部分数据。
  2. 通过 setTimeoutrequestAnimationFrame 等方法,将剩余数据的渲染分散到后续的任务队列中。

这种方法的优点是页面的响应性更好,因为渲染任务被拆分成了多个小任务,页面不会因为一次性渲染大量数据而卡顿,用户可以在任务间隙进行交互,同时渲染任务被分散到多个帧中,用户可以感知到页面的逐步加载。

TIP

分批渲染不适合超大数据量,因为即使分批渲染完,大量的 DOM 也会导致浏览器卡顿。对于超大数据量,可以考虑使用虚拟滚动(virtual scrolling)等技术。

推荐数据量在几百到一两千之间使用分批渲染比较好。

一些虚拟滚动库


Demo(Vue3 setup 语法)

以下是一个使用 Vue3 setup 语法实现的分批渲染的 Demo:

分批渲染 Demo

@ep_src_vue_practice_batch_render_demo(./batch-render-demo.vue)
vue
<template>
  <div class="box">
    <button @click="startRendering">开始渲染</button>
    <button @click="clear">清空</button>
    <span class="render-status" v-if="visibleItems.length > 0">{{
      rendering ? '正在渲染...' : '渲染完成'
    }}</span>
  </div>
  <ul>
    <li v-for="item in visibleItems" :key="item">{{ item }}</li>
  </ul>
</template>

<script setup>
import { ref, reactive } from 'vue'

// 模拟数据
const totalItems = 300
const items = Array.from({ length: totalItems }, (_, i) => `Item ${i + 1}`)

const visibleItems = reactive([]) // 当前显示的数据
const rendering = ref(false) // 是否正在渲染

// 开始渲染
const startRendering = () => {
  /*
    清空当前显示的数据
    真实的项目中,可能并不需要这一步,看需求而定
  */
  visibleItems.splice(0, visibleItems.length)

  const batchSize = 3 // 每批渲染的数据量
  rendering.value = true
  let index = 0 // 重置索引

  // 分批渲染函数
  const renderBatch = () => {
    if (index >= items.length) {
      rendering.value = false
      return
    }

    // 假如本次需要渲染的数据
    const nextBatch = items.slice(index, index + batchSize)
    visibleItems.push(...nextBatch)

    index += batchSize

    // 使用 requestAnimationFrame 或 setTimeout 将任务分散到后续任务队列
    // requestAnimationFrame(renderBatch)
    setTimeout(renderBatch, 50)
  }

  renderBatch()
}

const clear = () => {
  visibleItems.splice(0, visibleItems.length)
}
</script>

<style scoped>
ul {
  height: 200px;
  overflow-y: scroll;
  margin-bottom: 0;
}
.box {
  display: flex;
  gap: 1rem;
}
.render-status {
  font-size: 14px;
}
</style>

使用 setTimeout 和 requestAnimationFrame 的区别

  • setTimeout 可以调节每次渲染的时间,一个恰当 batchSize渲染的间隔时间 能然用户很明显的感觉到一个顺畅分批渲染的过程。相比 requestAnimationFrame batchSize 的值可以稍微大一点
  • requestAnimationFrame 是浏览器下一次重绘之前的回调函数,不建议将 batchSize 的值设置的过大,否则会导致浏览器卡顿。相比 setTimeout 一个恰当 batchSize 值,用户几乎感觉不到这个是分批渲染的。

一些虚拟滚动库

比较与选择

库名称Vue 版本支持动态高度网格支持复杂度适用场景
Vue Virtual ScrollerVue 2/3中等列表、网格、动态布局
Vueuc (Vue Utilities)Vue 3简单动态高度简单列表
Vue Virtual Scroll ListVue 2/3简单高性能长列表
Vite-Vue-Virtual-ScrollerVue 3中等高性能长列表
Tiny-Virtual-ScrollVue 3非常简单简单固定高度列表