虚拟滚动与可见性
这一页讲什么
组件只把可视区域内的行渲染进 DOM。可见集合由两件事共同决定:
- 结构上可见 —— 节点的所有祖先都没有折叠。
assembly/tree-visibility.ts用单遍遍历 + 一个栈跟踪 「折叠祖先的rightNode」来重建整棵树的shown标志,不需要逐节点向上回溯; 清空搜索后也是用同一条路径恢复可见性,避免搜索期间被污染的shown残留。 - 几何上在视口里 —— 用容器的
scrollTop和高度切出[start, end)这一段行。
MPTT 让这两步都很廉价:
| 操作 | 复杂度 |
|---|---|
| 子树判定(某节点是否在某子树内) | O(1) —— 比较 leftNode / rightNode 区间 |
| 视口切片(取当前要渲染的行) | O(k),k = 视口内行数 |
| 展开 / 折叠一个节点 | O(subtree + log N) |
参数
| prop | 类型 | 默认值 | 说明 |
|---|---|---|---|
height | string | '100%' | 容器高度。父容器需要能给出确定高度,否则虚拟滚动没有可切分的窗口 |
width | string | '100%' | 容器宽度 |
lineHeight | number | 26 | 行高(像素)。虚拟滚动按它换算可见区间 |
fontSize | string | '14px' | 行内字号 |
展开 / 收起与动画
expandAll()/collapseAll()作用于当前视图:普通树操作完整树,搜索视图只操作当前搜索结果, 且不会改动主树的展开状态。- 展开或收起时,组件只为当前视口内已有的分支行创建一层短暂过渡(约 220ms), 不会为了动画把整棵子树加进 DOM。未变化的行不会被重新挂载。
- 过渡期间滚动会短暂冻结,避免动画进行到一半被换行打断。
- 系统「减少动态效果」(
prefers-reduced-motion)开启时,动画自动降级为瞬时。
用法
vue
<script setup lang="ts">
import { ref } from 'vue'
import { VueGiantTree } from 'vue3-giant-tree-wasm'
const treeRef = ref<InstanceType<typeof VueGiantTree>>()
const selected = ref([])
</script>
<template>
<div style="height: 600px">
<button @click="treeRef?.expandAll()">全部展开</button>
<button @click="treeRef?.collapseAll()">全部收起</button>
<VueGiantTree ref="treeRef" :tree="rows" root="root" v-model="selected" />
</div>
</template>动手试试
试一下:把「数据规模」切到 10k,点「全部展开」,再滚动列表 —— 行数是几千行,但 DOM 里的行数始终只和视口高度有关。