🎬 面试开场怎么引出这个话题
「密评系统有个测评指标树 — 商密测评标准是分层的树形结构,展开后有 8000 多个节点,要支持拖拽排序、增删改、节点懒加载。一开始用 el-tree,全展开直接卡死浏览器。换成 el-tree-v2 虚拟树后丝滑。印象深的坑:一是树形拖拽跨层级 — 拖到不同父节点下要校验层级关系、还要更新所有子节点的 path;二是懒加载 + 搜索 — 没加载的节点搜不到,要先递归展开路径。」
✅ 抛出 4 个可深挖的点:① 虚拟树原理 ② 懒加载 ③ 拖拽跨层级 ④ 搜索定位。
树形组件是中后台高频,万级节点 + 拖拽是高阶场景。
1

8000 节点为什么崩?

⚠️ 现象

el-tree 全展开 8000 节点,DOM 渲染几万个节点(tree-item + 文本 + 图标),浏览器卡死 5 秒+,操作延迟严重。

根因: 和表格虚拟滚动同理 — DOM 节点太多,浏览器 Layout/Paint 扛不住。el-tree 是全量渲染,el-tree-v2 是虚拟滚动(只渲染可视区)。方案: 换 el-tree-v2。
什么时候用 v2: 节点数 > 1000 就考虑 el-tree-v2。小数据量(几百)el-tree 够用,API 更熟。v2 的限制是不支持一些高级特性(自定义节点内容受限、拖拽要自己实现),要看场景。
2

虚拟树原理(扁平化 + 虚拟滚动)

🧠 关键:树 → 扁平数组
树是嵌套结构,虚拟滚动要"一维数组"(才能算 startIndex)。所以虚拟树内部把树拍平成一维数组,每个节点记录 level(层级)和 expanded(是否展开)。
渲染时:遍历扁平数组,跳过"父节点未展开"的(它们不可见),对可见节点用 padding-left × level 表示缩进。视觉上是树,数据是一维。
📋 扁平化 + 可见性过滤
// 树 → 扁平数组(带 level 和可见性)
function flattenTree(tree, level = 0, visibleParent = true) {
  const result = []
  for (const node of tree) {
    const visible = visibleParent  // 父可见我才可见
    result.push({ ...node, level, visible })
    if (node.children?.length && node.expanded) {
      result.push(...flattenTree(node.children, level + 1, visible))
    } else if (node.children?.length) {
      // 父折叠了,子节点不可见,但仍要递归处理(保留结构)
      result.push(...flattenTree(node.children, level + 1, false))
    }
  }
  return result
}

// 虚拟滚动:只渲染 visible && 在可视区的
const visibleNodes = flatTree.filter(n => n.visible)
const windowNodes = visibleNodes.slice(start, start + visibleCount)
3

懒加载子节点

懒加载场景: 8000 节点不可能一次性加载(接口慢 + 数据大)。懒加载:初始只加载根节点的第一层子节点,用户展开某节点时,才请求该节点的子节点。el-tree 的 load 属性支持。
⚠️ 懒加载 + 搜索的坑

懒加载下,没展开过的节点子节点没加载,搜索时搜不到深层未加载的节点。比如搜"物理安全",它在某未展开的深层节点下,前端数据里根本没有。

方案: 搜索走后端 — 后端返回匹配节点的完整路径(从根到目标的所有祖先 id),前端按路径逐层展开(触发懒加载),最后定位高亮。
4

树形拖拽跨层级

⚠️ 拖拽的难点
  • 层级校验: 不能把父节点拖到自己的子孙下(成环);某些节点类型有层级限制
  • path 更新: 节点移动后,它的 path(/根/父/当前)变了,所有子孙的 path 也要更新
  • 排序: 同层级内拖拽改 order,跨层级拖拽要插入目标位置
  • 性能: 大树拖拽时,更新 path 要递归所有子孙,用 Web Worker 或批量更新
校验重点:防环:DFS 检查目标是不是源的子孙;② 层级约束:业务规则(如"控制项"不能拖到"领域"下);③ 权限:用户有没有改这个节点位置的权限。校验不过要拒绝并提示原因。
5

大数据量优化总结

手段场景效果
虚拟树(el-tree-v2)节点 1000+DOM 从万级降到几十
懒加载数据量大,不必全展初始只加载首层
shallowRef 存树树进响应式避免深度劫持
批量更新拖拽/批量编辑合并多次操作为一次渲染
Web Workerpath 更新等重计算不阻塞主线程
6

高频追问 & 容易翻车点

1el-tree 和 el-tree-v2 区别?什么时候用 v2? 基础
el-tree: 全量渲染 DOM,节点多(1000+)卡。el-tree-v2: 虚拟滚动,只渲染可视区,大数据量流畅。原理是树扁平化 + 可见性过滤 + 虚拟滚动。节点 > 1000 用 v2,小数据量 el-tree API 更熟。v2 限制:自定义节点内容受限、拖拽要自己实现。
2虚拟树原理?怎么把树变虚拟? 中等
树扁平化成一维数组,每节点带 level(层级)和 expanded。渲染时过滤"可见的"(父展开的),对可见节点虚拟滚动(只渲染可视区 + 缓冲)。缩进用 padding-left × level。视觉是树,数据是一维,渲染是虚拟滚动。
3懒加载的树怎么搜索? 中等
懒加载下深层未展开节点没加载,前端搜不到。搜索走后端,后端返回匹配节点的完整路径(祖先 id 列表)。前端按路径逐层展开(触发懒加载),最后定位高亮目标节点。不能纯前端搜索懒加载树。
4树形拖拽要注意什么? 中等
防环:DFS 检查目标是不是源的子孙(否则死循环);② 层级约束:业务规则(某些类型不能放某层级);③ path 更新:移动后该节点及所有子孙的 path 要重算;④ 性能:大树 path 更新递归慢,Web Worker 或批量;⑤ 权限校验校验不过拒绝 + 提示原因。
7

30 秒电梯陈述

🎤 30 秒版
「密评系统有测评指标树,展开后 8000 多节点,要拖拽排序、增删改、懒加载。el-tree 全展开直接卡死,换 el-tree-v2 虚拟树后丝滑 — 原理是树扁平化成数组 + 虚拟滚动。两个印象深的坑:一是懒加载下搜索搜不到深层未加载节点,改成后端返回路径前端逐层展开定位;二是树形拖拽跨层级,要防环(DFS 检查)和批量更新所有子孙的 path。」
优势: 有规模(8000 节点)+ 原理(虚拟树)+ 坑(懒加载搜索/拖拽防环),体系完整。和表格虚拟滚动、文档 diff 串联,体现你"大数据量渲染"的完整经验。

面试亮点专题 11 — 测评指标树深度话题

虚拟树 · 树扁平化 · 懒加载搜索 · 拖拽跨层级防环 · path 更新

← 返回专题首页