指南

虚拟滚动 virtual

virtual 虚拟滚动的行为约定:窗口化口径、几何等价、尺寸要求与定位漫游

数据量大时(如单节点几千直属子节点的平铺组织架构),开启 virtual 后,同层可见兄弟数达阈值(50)的行只渲染视口内的窗口,DOM 数量与滚动流畅度不再随总数增长。

虚拟滚动

virtual(Vue 3 版 1.16.0 新增):同层可见兄弟数达阈值(50)的行只渲染视口内窗口,用等尺寸占位块保持布局与连接线 逐像素等价。要求数字型 label-width(本用例 120);关闭开关可对比全量渲染 3000 个节点的卡顿。

可见节点 0 / DOM 节点 0 / 总数 3000
Root
N1
N2
N3
N4
N5
N6
N7
N8
N9
N10
N11
N12
滚动横向滚动条:窗口随滚动移动;「滚动到随机节点」走 scrollToNode (窗口外目标先揭示再滚动)。DOM 节点数始终有界,与总数 3000 无关。
<script setup lang="ts">
import { VueOkrTree } from 'vue3-okr-tree'

const data = [
  {
    id: 0,
    label: 'Root',
    children: Array.from({ length: 3000 }, (_, i) => ({ id: i + 1, label: `N${i + 1}` })),
  },
]
</script>

<template>
  <div style="width: 800px; overflow: auto">
    <VueOkrTree :data="data" node-key="id" :label-width="120" default-expand-all virtual />
  </div>
</template>
显示代码

行为约定

  • 窗口化口径:按「未被 filter 隐藏的可见兄弟」窗口化;aria(aria-setsize / aria-posinset)、show-node-num 计数、getVisibleNodes() 等全部按全量可见列表输出,窗口化不影响语义。
  • 几何等价:未渲染兄弟的位置由等尺寸占位块顶住,行总宽、每个渲染节点的坐标与连接线走向和全量渲染逐像素一致;占位块自带连线段续接横线,行首 / 行末的边界帽(去线与圆角)语义由占位块自然继承,无需特殊处理。
  • 尺寸要求:要求数字型 label-width(占位块尺寸来自宽度模型:叶 / 折叠 = label-width + 2×sibling 间距,展开 = max(label-width, Σ 子盒宽) + 2×sibling 间距,与 float 布局的 shrink-wrap 结果一致);horizontal 布局还要求 label-height。auto 尺寸下模型不可知,达标行退回全量渲染并输出开发期警告。卡片宽度若被 CSS 覆盖(非 label-width 途径),模型不再成立。
  • 窄容器行为:全量渲染在窄容器里 float 会折行;virtual 行保持单行横向滚动(占位块把最小内容宽度顶到全行宽)——这是期望的虚拟滚动行为,与全量渲染的差异仅出现在「本会折行」的窄容器场景。
  • 定位与漫游:scrollToNode 与键盘漫游(方向键 / Home / End)遇到窗口外目标时先揭示(强制渲染其邻近区间)再定位,逐条推进不跳焦。
  • 创建期生效:与 node-key / direction 同属创建期快照,运行时变更输出警告,需换 :key 重建实例。
  • 深层小行不受益:窗口化只优化「同层大量兄弟」;每层只有几个节点的深树(如 1000 层 × 每层 10 个)不达阈值,全部渲染。
  • 已知边界:万级数据的首帧成本主要在 store 构建(1 万节点约 3s,与 virtual 无关、全量渲染同样存在);virtual 消除的是 DOM 数量与滚动 / 展开时的渲染卡顿。

与 OkrTreeViewport 的关系

两者互补:OkrTreeViewport 负责缩放与平移,virtual 负责窗口化渲染,可同时开启——画布平移 / 缩放会触发窗口重算。

Copyright © 2026