news 2026/9/20 22:21:27

ant-design-vue Tree 组件完全指南:从基础用法到虚拟滚动与目录树

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ant-design-vue Tree 组件完全指南:从基础用法到虚拟滚动与目录树
  • 前端
  • UI组件
  • 设计系统

【免费下载链接】ant-design-vue

🌈 An enterprise-class UI components based on Ant Design and Vue. 🐜

项目地址:https://gitcode.com/gh_mirrors/an/ant-design-vue
点击查看免费下载

ant-design-vue 的Tree是一个以树形结构展示层级数据的核心组件,可用于文件目录、组织架构、分类体系等几乎所有能抽象为"父子关系"的业务场景。本文以官方英文文档(components/tree/index.en-US.md)为骨架,结合组件源码与仓库内 13 个官方示例(components/tree/demo),系统讲解 Tree 的 API 全量参数、事件回调、节点插槽定制、异步加载、拖拽排序、虚拟滚动与 DirectoryTree 目录树等实战能力,帮助你完整掌握在 Vue 3 项目中构建高性能树形交互的方案。

何时使用 Tree

任何可以用层级结构表达的信息都适合用树形组件呈现,例如:

  • 文件目录 / 资源管理器
  • 组织架构(上下级汇报关系)
  • 生物分类、地区行政划分、商品类目
  • 权限树、菜单树、多级评论

Tree 组件在展示上述层级关系的同时,天然支持节点的展开(expand)、折叠(collapse)与选中(select),并通过checkable提供勾选能力。从源码看,ant-design-vue 的 Tree 是对底层vc-tree的封装(components/tree/Tree.tsx),统一注入 antd 主题样式、插槽协议与事件命名,同时保留了TreeNodeDirectoryTree两个衍生组件,并通过 components/tree/index.tsx 一次性注册ATreeATreeNodeADirectoryTree三个全局组件。

Tree props 全量参数解析

官方文档给出了完整的 Tree 属性表,下面按功能分组逐项说明,并结合 components/tree/Tree.tsx 中treeProps()的真实定义进行印证。

数据与字段映射

属性说明类型默认值版本
treeData树的节点数据,类型为TreeNode[](v1.1.4 之前请使用treeNodesTreeNode[]--
fieldNames将 treeNode 中的titlekeychildren字段替换为 treeData 中对应的自定义字段object{ children: 'children', title: 'title', key: 'key' }3.0.0

treeData是最推荐的数据驱动写法。源码中组件还会对旧式插槽声明发出警告:当treeData未传而使用了default插槽时,会提示 "childrenof Tree is deprecated. Please usetreeDatainstead."(components/tree/Tree.tsx)。

fieldNames用于对接后端返回的非标准字段。例如后端数据使用name作为标题、child作为子节点数组时,可以这样配置(完整示例见 components/tree/demo/replaceFields.vue):

<a-tree v-model:expandedKeys="expandedKeys" v-model:selectedKeys="selectedKeys" v-model:checkedKeys="checkedKeys" checkable :tree-data="treeData" :field-names="fieldNames" > <template #title="{ name, key }"> <span v-if="key === '0-0-1'" style="color: #1890ff">{{ name }}</span> <template v-else>{{ name }}</template> </template> </a-tree>
const fieldNames: TreeProps['fieldNames'] = { children: 'child', title: 'name', }; const treeData: TreeProps['treeData'] = [ { name: 'parent 1', key: '0-0', child: [ { name: 'leaf', key: '0-0-0', disabled: true }, ], }, ];

需要说明的是,更早版本中的replaceFields属性已被标记为 deprecated,源码中通过watchEffect输出警告建议改用fieldNames(components/tree/Tree.tsx),并在渲染时做了兼容回退fieldNames = props.replaceFields(components/tree/Tree.tsx)。

受控状态与 v-model

Tree 的三组核心状态均支持v-model,这是 Vue 3 语法的受控用法,源码通过onUpdate:xxx事件实现双向绑定(components/tree/Tree.tsx):

属性说明类型默认值
expandedKeys(v-model)(受控)已展开节点的 keysstring[] | number[][]
checkedKeys(v-model)(受控)已勾选节点的 keysstring[] | number[] |{ checked, halfChecked }[]
selectedKeys(v-model)(受控)已选中节点的 keysstring[] | number[]-
  • expandedKeysselectedKeys均为简单的 key 数组。
  • checkedKeys需要注意父子联动规则:当指定的是父节点 key 时,其所有子节点会被一并勾选;当指定的是子节点 key 时,其父节点也会被勾选。只有在checkablecheckStrictly同时为true时,checkedKeys才接收{ checked: string[] | number[], halfChecked: string[] | number[] }对象,且父子勾选互不影响。
  • 对应的非受控默认值版本:defaultExpandedKeysdefaultCheckedKeysdefaultSelectedKeys均在源码的 props 中声明(components/tree/Tree.tsx)。

基础示例(components/tree/demo/basic.vue)同时展示了勾选、选中、禁用与默认展开的组合用法:

<a-tree v-model:expandedKeys="expandedKeys" v-model:selectedKeys="selectedKeys" v-model:checkedKeys="checkedKeys" checkable :tree-data="treeData" > <template #title="{ title, key }"> <span v-if="key === '0-0-1-0'" style="color: #1890ff">{{ title }}</span> <template v-else>{{ title }}</template> </template> </a-tree>
const treeData: TreeProps['treeData'] = [ { title: 'parent 1', key: '0-0', children: [ { title: 'parent 1-0', key: '0-0-0', disabled: true, // 整个节点禁用 children: [ { title: 'leaf', key: '0-0-0-0', disableCheckbox: true }, // 仅禁用勾选框 { title: 'leaf', key: '0-0-0-1' }, ], }, { title: 'parent 1-1', key: '0-0-1', children: [{ key: '0-0-1-0', title: 'sss' }] }, ], }, ];

交互行为开关

属性说明类型默认值
selectable节点是否可被选中booleantrue
multiple是否支持多选booleanfalse
checkable在节点前显示Checkboxbooleanfalse
checkStrictly精确勾选,父节点与子节点勾选状态不再关联booleanfalse
disabled是否禁用整棵树boolfalse
autoExpandParent展开父节点时是否自动联动展开其祖先booleanfalse
defaultExpandAll默认展开所有节点booleanfalse
blockNode节点是否撑满剩余横向空间booleanfalse
showLine是否显示层级连接线,3.0+ 可传{ showLeafIcon: boolean }boolean | objectfalse
showIcon是否在标题前展示图标,开启后无默认样式,需自行定制booleanfalse
draggable是否允许拖拽(IE > 8)booleanfalse
allowDrop是否允许节点被放置到目标位置({ dropNode, dropPosition }) => boolean-

源码中这些布尔属性的默认值通过initDefaultProps统一注入:checkable: falseselectable: trueshowIcon: falseblockNode: false(components/tree/Tree.tsx)。渲染时还会依据这些开关动态拼接类名,例如关闭图标时添加-icon-hide、blockNode 时添加-block-node、关闭选中时添加-unselectable(components/tree/Tree.tsx)。

插槽与自定义渲染

属性说明类型版本
title自定义节点标题slot2.0.0
switcherIcon自定义展开/折叠图标v-slot,作用域参数见下表-
height配置虚拟滚动高度,开启后将不支持横向滚动number-
virtual设置为 false 可关闭虚拟滚动boolean默认 true(3.0)
loadData异步加载数据function(node)-
loadedKeys(受控)已加载完成的节点,需与loadData配合string[] | number[][]
filterTreeNode过滤(高亮)节点,返回true的节点将被高亮function(node)-

switcherIcon插槽的作用域参数非常丰富,官方文档列出的完整字段为:

{ active, checked, expanded, loading, selected, halfChecked, title, key, children, dataRef, data, defaultIcon, switcherCls }

其中defaultIcon是内置默认图标组件、switcherCls是切换图标的样式类。源码 components/tree/utils/iconUtil.tsx 展示了默认图标的完整决策逻辑:加载中显示LoadingOutlined;叶子节点在showLine开启时显示FileOutlined或叶子连接线;非叶子节点默认显示CaretDownFilled,开启showLine后折叠态显示PlusSquareOutlined、展开态显示MinusSquareOutlined。若传入函数形式的switcherIcon,会以{ ...props, defaultIcon, switcherCls }为参数调用(components/tree/utils/iconUtil.tsx)。

Events 事件回调

Tree 的全部事件与参数签名如下(均可在官方文档 Events 表中查阅):

事件名说明回调参数
select用户点击节点时触发function(selectedKeys, e: { selected: bool, selectedNodes, node, event })
check勾选状态变化时触发function(checkedKeys, e: { checked: bool, checkedNodes, node, event })
expand节点展开/折叠时触发function(expandedKeys, { expanded: bool, node })
load节点异步加载完成时触发function(loadedKeys, { event, node })
rightClick用户右键点击节点时触发function({ event, node })
dragstart开始拖拽时触发function({ event, node })
dragenter拖入节点范围时触发function({ event, node, expandedKeys })
dragover在节点上方拖动时触发function({ event, node })
dragleave拖离节点范围时触发function({ event, node })
dragend拖拽结束时触发function({ event, node })
drop放置到节点时触发function({ event, node, dragNode, dragNodesKeys })

事件回调的类型定义集中在 components/tree/Tree.tsx,包括AntTreeNodeCheckedEventAntTreeNodeSelectedEventAntTreeNodeExpandedEventAntTreeNodeDragEnterEventAntTreeNodeDropEvent等,可从ant-design-vue/es/tree直接导入获得类型提示。select/check/expand三个事件在源码中会额外同步触发update:selectedKeys/update:checkedKeys/update:expandedKeys以支撑v-model(components/tree/Tree.tsx)。

Tree 实例方法

方法名说明
scrollTo({ key, align?, offset? })在虚拟滚动模式下滚动到指定 key 的节点,align可选'top' \| 'bottom' \| 'auto'offset为偏移量

该方法在源码中通过expose暴露(components/tree/Tree.tsx),同时组件还向外暴露了treeRef以及selectedKeyscheckedKeyshalfCheckedKeysloadedKeysloadingKeysexpandedKeys等计算属性,便于父组件以命令式方式获取当前状态。使用时先给<a-tree>ref,再调用ref.value.scrollTo({ key: '0-0-1', align: 'top' })即可。

TreeNode 节点配置

Tree 同时支持以子节点嵌套方式声明树(通过a-tree-node),每个节点的可配置属性如下:

属性说明类型默认值
key节点唯一标识,用于与 (default)ExpandedKeys / (default)CheckedKeys / (default)SelectedKeys 对应;必须保证整棵树内唯一string | number内部计算的位置值
title节点标题string'---'
disabled禁用该节点booleanfalse
selectable该节点是否可选中booleantrue
checkable当 Tree 开启 checkable 时,控制该节点是否显示 Checkboxboolean-
disableCheckbox禁用该节点的勾选框booleanfalse
icon自定义节点图标,传入组件时其渲染会收到完整 TreeNode propsslot | slot-scope-
isLeaf是否为叶子节点(在指定loadData时生效)boolean-
class节点类名string-
style节点样式string | object-

不过在treeData时代,更推荐用数据驱动 +#title#icon插槽代替嵌套节点。源码中TreeNode直接复用vc-treeVcTreeNode实现(components/tree/index.tsx),仅作再导出。

DirectoryTree 目录树

DirectoryTree是内置的目录风格树(a-directory-tree),自带文件/文件夹图标,专为资源管理器式场景设计,其实现位于 components/tree/DirectoryTree.tsx。

属性

属性说明类型默认值
expandAction目录展开触发方式,可选false'click''dblclick'stringclick

源码中对expandAction的解析(components/tree/DirectoryTree.tsx):

  • 'click':单击文件夹节点即展开/折叠,内部对展开操作做了 200ms 的 debounce(onDebounceExpand,components/tree/DirectoryTree.tsx),且按住Shift/Ctrl/Command时不触发展开;
  • 'dblclick''doubleclick':双击展开/折叠;
  • false:不自动展开,仅靠点击切换箭头。

文件/文件夹图标与多选

DirectoryTree 默认showIcon: trueblockNode: true,并根据节点状态自动切换图标:叶子节点显示FileOutlined,非叶子节点展开时显示FolderOpenOutlined、折叠时显示FolderOutlined(components/tree/DirectoryTree.tsx)。

与普通 Tree 相比,DirectoryTree 额外实现了类文件管理器的多选逻辑(源码见 components/tree/DirectoryTree.tsx):

  • Ctrl/Command + 单击:切换式多选(multiple模式下);
  • Shift + 单击:以最近一次单选点为起点的连续范围多选,通过calcRangeKeys计算范围 key 并合并去重;
  • 普通单击:单选,始终以最新点击节点作为lastSelectedKey

完整示例见 components/tree/demo/directory.vue:

<a-directory-tree v-model:expandedKeys="expandedKeys" v-model:selectedKeys="selectedKeys" multiple :tree-data="treeData" ></a-directory-tree>

实战场景详解

1. 异步加载数据(loadData)

点击展开节点时动态请求子级数据,是组织架构、区域级联等场景的标配。核心是利用loadData返回 Promise,并在回调中直接修改节点的dataRef.children后重建 treeData 引用(components/tree/demo/dynamic.vue):

<a-tree v-model:expandedKeys="expandedKeys" v-model:selectedKeys="selectedKeys" :load-data="onLoadData" :tree-data="treeData" />
const treeData = ref<TreeProps['treeData']>([ { title: 'Expand to load', key: '0' }, { title: 'Expand to load', key: '1' }, { title: 'Tree Node', key: '2', isLeaf: true }, // isLeaf 阻止再次加载 ]); const onLoadData: TreeProps['loadData'] = treeNode => { return new Promise<void>(resolve => { if (treeNode.dataRef.children) { resolve(); return; } setTimeout(() => { treeNode.dataRef.children = [ { title: 'Child Node', key: `${treeNode.eventKey}-0` }, { title: 'Child Node', key: `${treeNode.eventKey}-1` }, ]; treeData.value = [...treeData.value]; // 触发响应式更新 resolve(); }, 1000); }); };

注意两个关键点:叶子节点需显式声明isLeaf: true避免无效请求;修改 children 后必须重建treeData的引用(如展开新数组)才能触发重新渲染。

2. 可搜索树(filterTreeNode + title 插槽)

搜索场景需要三件事:用#title插槽高亮命中文本、用expandedKeys展开命中节点的祖先链、用autoExpandParent保证展开时祖先联动。完整逻辑见 components/tree/demo/search.vue:

<a-input-search v-model:value="searchValue" style="margin-bottom: 8px" placeholder="Search" /> <a-tree :expanded-keys="expandedKeys" :auto-expand-parent="autoExpandParent" :tree-data="gData" @expand="onExpand" > <template #title="{ title }"> <span v-if="title.indexOf(searchValue) > -1"> {{ title.substring(0, title.indexOf(searchValue)) }} <span style="color: #f50">{{ searchValue }}</span> {{ title.substring(title.indexOf(searchValue) + searchValue.length) }} </span> <span v-else>{{ title }}</span> </template> </a-tree>

核心思路:监听searchValue,遍历全量dataList找到命中节点,通过getParentKey递归反查祖先 key 集合,作为新的expandedKeys并重置autoExpandParent = true;用户在树内手动展开时则关闭autoExpandParent,避免展开状态被反复纠正。

3. 拖拽排序(draggable + drop)

启用draggable后,配合@drop事件即可实现节点重排。官方示例(components/tree/demo/draggable.vue)展示了完整的"拖入内部 / 拖到前后"三种落点处理:

<a-tree class="draggable-tree" draggable block-node :tree-data="gData" @dragenter="onDragEnter" @drop="onDrop" />
const onDrop = (info: AntTreeNodeDropEvent) => { const dropKey = info.node.key; const dragKey = info.dragNode.key; const dropPos = info.node.pos.split('-'); const dropPosition = info.dropPosition - Number(dropPos[dropPos.length - 1]); // 1) 从原位置移除拖拽节点 // 2) !info.dropToGap:放置到节点内部 → item.children.unshift(dragObj) // 3) 展开节点底部空隙 → 同样插入 children 头部 // 4) 否则按 dropPosition 为 -1 / 1 插入到目标节点的前/后 };

dropPosition的语义:-1表示落在节点上方间隙、1表示落在下方间隙,与dropToGap配合即可区分"成为子节点"还是"成为兄弟节点"。allowDrop属性可在此基础上进一步限制可放置位置。底层对拖拽落点指示线的渲染由 components/tree/utils/dropIndicator.tsx 提供。

4. 连接线与自定义图标(showLine / switcherIcon / icon)

文件目录类展示常开启showLine显示层级连接线,并用switcherIcon替换默认的加减号图标(components/tree/demo/line.vue):

<a-tree :show-line="showLine" :show-icon="showIcon" :default-expanded-keys="['0-0-0']" :tree-data="treeData" @select="onSelect" > <template #icon><carry-out-outlined /></template> <template #title="{ dataRef }"> <template v-if="dataRef.key === '0-0-0-1'"> <div>multiple line title</div> </template> <template v-else>{{ dataRef.title }}</template> </template> <template #switcherIcon="{ dataRef, defaultIcon }"> <SmileTwoTone v-if="dataRef.key === '0-0-2'" /> <component :is="defaultIcon" v-else /> </template> </a-tree>

#switcherIcon插槽可以拿到dataRef(当前节点数据)与defaultIcon(内置默认图标),从而做到"大部分节点用默认图标、个别节点换图标"。若只想针对不同节点定制标题前的图标,使用#icon插槽并读取keyselected等参数即可(components/tree/demo/customized-icon.vue)。3.0+ 还可以用对象形式:show-line="{ showLeafIcon: true }"控制叶子节点是否显示文件图标。

5. 虚拟滚动(height / virtual / scrollTo)

当节点数量庞大(如数千个)时,传入height属性即可启用虚拟滚动,配合default-expand-all一次性渲染海量节点(components/tree/demo/virtual-scroll.vue):

<a-tree v-model:selectedKeys="selectedKeys" v-model:checkedKeys="checkedKeys" default-expand-all checkable :height="233" :tree-data="dig()" />

启用虚拟滚动后virtual默认为true,可显式传:virtual="false"关闭;通过实例方法scrollTo({ key, align })可以编程式定位到任意节点。需要注意文档中的限制:开启height后将不支持横向滚动。

6. 右键菜单(rightClick 或 title 插槽 + Dropdown)

官方推荐用#title插槽包裹a-dropdown实现节点级右键菜单,比rightClick事件更灵活(components/tree/demo/context-menu.vue):

<a-tree v-model:expandedKeys="expandedKeys" :tree-data="treeData"> <template #title="{ key: treeKey, title }"> <a-dropdown :trigger="['contextmenu']"> <span>{{ title }}</span> <template #overlay> <a-menu @click="({ key: menuKey }) => onContextMenuClick(treeKey, menuKey)"> <a-menu-item key="1">1st menu item</a-menu-item> <a-menu-item key="2">2nd menu item</a-menu-item> <a-menu-item key="3">3rd menu item</a-menu-item> </a-menu> </template> </a-dropdown> </template> </a-tree>

若需要在树级统一处理右键(拿到节点信息),也可以直接用@right-click="({ event, node }) => ..."事件。

FAQ:如何隐藏 showLine 模式下的文件图标

当开启showLine后,叶子节点会通过switcherIcon渲染出文件图标(FileOutlined,见 components/tree/utils/iconUtil.tsx)。官方 FAQ 给出的方案是覆盖样式将其隐藏:

/* 隐藏叶子节点的文件图标 */ .ant-tree-switcher-leaf-line { visibility: hidden; }

或者从根上处理:3.0+ 使用:show-line="{ showLeafIcon: false }",此时iconUtil会为叶子节点渲染纯连接线-switcher-leaf-line而非文件图标,无需再写覆盖样式。

小结

Tree 组件在 ant-design-vue 中承担着全部层级数据展示职责,其能力可以总结为一句话:*数据驱动(treeData + fieldNames)+ 三组受控状态(v-model:expandedKeys / selectedKeys / checkedKeys)+ 完备事件体系(select / check / expand / drag/ drop / rightClick)+ 插槽定制(title / icon / switcherIcon)+ 进阶能力(异步加载、虚拟滚动、目录树)**。本文所有结论均可在 components/tree/index.en-US.md 官方文档、components/tree/Tree.tsx 与 components/tree/DirectoryTree.tsx 源码、以及 components/tree/demo 目录下 13 个可运行示例中逐条验证,适合作为接入 Tree 组件时的速查与排障参考。

  • 前端
  • UI组件
  • 设计系统

【免费下载链接】ant-design-vue

🌈 An enterprise-class UI components based on Ant Design and Vue. 🐜

项目地址:https://gitcode.com/gh_mirrors/an/ant-design-vue
点击查看免费下载

相关推荐

上一篇:otter.nvim常见问题解决:诊断与修复嵌入式代码LSP连接问题
下一篇:React Hooks完全指南:TOP课程中的最新特性

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/20 22:18:40

WABT wasm2wat 使用指南:3 分钟把 Wasm 二进制读成文本

WABT wasm2wat 使用指南&#xff1a;3 分钟把 Wasm 二进制读成文本 【免费下载链接】wabt The WebAssembly Binary Toolkit 项目地址: https://gitcode.com/GitHub_Trending/wa/wabt 你拿到一个编译好的 .wasm 文件&#xff0c;想读懂里面的逻辑&#xff0c;打开却只有一…

作者头像 李华
网站建设 2026/9/20 22:16:34

一条命令找回全部历史说说:GetQzonehistory批量导出QQ空间数据

一条命令找回全部历史说说&#xff1a;GetQzonehistory批量导出QQ空间数据 【免费下载链接】GetQzonehistory 获取QQ空间发布的历史说说 项目地址: https://gitcode.com/GitHub_Trending/ge/GetQzonehistory GetQzonehistory 是一款 QQ空间数据恢复 工具。扫码登录后&am…

作者头像 李华
网站建设 2026/9/20 22:15:11

用WorkBuddy搭建AI智能体工作流:跨境电商每日订单自动化实战

早上打开电脑&#xff0c;先查邮件、再同步数据、然后整理昨日的销售报表&#xff0c;最后还要把结果挨个发给相关同事。这一套流程我重复了快两年&#xff0c;直到把大部分环节丢给 WorkBuddy 处理之后&#xff0c;才真正意识到一个事实&#xff1a;这些每天雷打不动的事情&am…

作者头像 李华
网站建设 2026/9/20 22:13:20

别找临时中转:用 TaoToken 给 Dify 当兼容通道

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华