- 前端
- UI组件
- 设计系统
【免费下载链接】ant-design-vue
🌈 An enterprise-class UI components based on Ant Design and 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 主题样式、插槽协议与事件命名,同时保留了TreeNode与DirectoryTree两个衍生组件,并通过 components/tree/index.tsx 一次性注册ATree、ATreeNode、ADirectoryTree三个全局组件。
Tree props 全量参数解析
官方文档给出了完整的 Tree 属性表,下面按功能分组逐项说明,并结合 components/tree/Tree.tsx 中treeProps()的真实定义进行印证。
数据与字段映射
| 属性 | 说明 | 类型 | 默认值 | 版本 |
|---|---|---|---|---|
| treeData | 树的节点数据,类型为TreeNode[](v1.1.4 之前请使用treeNodes) | TreeNode[] | - | - |
| fieldNames | 将 treeNode 中的title、key、children字段替换为 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) | (受控)已展开节点的 keys | string[] | number[] | [] |
| checkedKeys(v-model) | (受控)已勾选节点的 keys | string[] | number[] |{ checked, halfChecked } | [] |
| selectedKeys(v-model) | (受控)已选中节点的 keys | string[] | number[] | - |
expandedKeys与selectedKeys均为简单的 key 数组。checkedKeys需要注意父子联动规则:当指定的是父节点 key 时,其所有子节点会被一并勾选;当指定的是子节点 key 时,其父节点也会被勾选。只有在checkable与checkStrictly同时为true时,checkedKeys才接收{ checked: string[] | number[], halfChecked: string[] | number[] }对象,且父子勾选互不影响。- 对应的非受控默认值版本:
defaultExpandedKeys、defaultCheckedKeys、defaultSelectedKeys均在源码的 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 | 节点是否可被选中 | boolean | true |
| multiple | 是否支持多选 | boolean | false |
| checkable | 在节点前显示Checkbox | boolean | false |
| checkStrictly | 精确勾选,父节点与子节点勾选状态不再关联 | boolean | false |
| disabled | 是否禁用整棵树 | bool | false |
| autoExpandParent | 展开父节点时是否自动联动展开其祖先 | boolean | false |
| defaultExpandAll | 默认展开所有节点 | boolean | false |
| blockNode | 节点是否撑满剩余横向空间 | boolean | false |
| showLine | 是否显示层级连接线,3.0+ 可传{ showLeafIcon: boolean } | boolean | object | false |
| showIcon | 是否在标题前展示图标,开启后无默认样式,需自行定制 | boolean | false |
| draggable | 是否允许拖拽(IE > 8) | boolean | false |
| allowDrop | 是否允许节点被放置到目标位置 | ({ dropNode, dropPosition }) => boolean | - |
源码中这些布尔属性的默认值通过initDefaultProps统一注入:checkable: false、selectable: true、showIcon: false、blockNode: false(components/tree/Tree.tsx)。渲染时还会依据这些开关动态拼接类名,例如关闭图标时添加-icon-hide、blockNode 时添加-block-node、关闭选中时添加-unselectable(components/tree/Tree.tsx)。
插槽与自定义渲染
| 属性 | 说明 | 类型 | 版本 |
|---|---|---|---|
| title | 自定义节点标题 | slot | 2.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,包括AntTreeNodeCheckedEvent、AntTreeNodeSelectedEvent、AntTreeNodeExpandedEvent、AntTreeNodeDragEnterEvent、AntTreeNodeDropEvent等,可从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以及selectedKeys、checkedKeys、halfCheckedKeys、loadedKeys、loadingKeys、expandedKeys等计算属性,便于父组件以命令式方式获取当前状态。使用时先给<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 | 禁用该节点 | boolean | false |
| selectable | 该节点是否可选中 | boolean | true |
| checkable | 当 Tree 开启 checkable 时,控制该节点是否显示 Checkbox | boolean | - |
| disableCheckbox | 禁用该节点的勾选框 | boolean | false |
| icon | 自定义节点图标,传入组件时其渲染会收到完整 TreeNode props | slot | slot-scope | - |
| isLeaf | 是否为叶子节点(在指定loadData时生效) | boolean | - |
| class | 节点类名 | string | - |
| style | 节点样式 | string | object | - |
不过在treeData时代,更推荐用数据驱动 +#title、#icon插槽代替嵌套节点。源码中TreeNode直接复用vc-tree的VcTreeNode实现(components/tree/index.tsx),仅作再导出。
DirectoryTree 目录树
DirectoryTree是内置的目录风格树(a-directory-tree),自带文件/文件夹图标,专为资源管理器式场景设计,其实现位于 components/tree/DirectoryTree.tsx。
属性
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| expandAction | 目录展开触发方式,可选false、'click'、'dblclick' | string | click |
源码中对expandAction的解析(components/tree/DirectoryTree.tsx):
'click':单击文件夹节点即展开/折叠,内部对展开操作做了 200ms 的 debounce(onDebounceExpand,components/tree/DirectoryTree.tsx),且按住Shift/Ctrl/Command时不触发展开;'dblclick'或'doubleclick':双击展开/折叠;false:不自动展开,仅靠点击切换箭头。
文件/文件夹图标与多选
DirectoryTree 默认showIcon: true、blockNode: 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插槽并读取key、selected等参数即可(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. 🐜
相关推荐
Radix Vue(reka-ui)Tree 树形控件完全指南:从基础用法到虚拟滚动与拖拽实现
Radix Vue(reka ui)Tree 树形控件完全指南:从基础用法到虚拟滚动与拖拽实现 <Badge Alpha</Badge Tree(树形视图)是
前端UI组件设计系统Ant Design List 组件完全指南:从基础列表到虚拟滚动与网格布局
Ant Design List 组件完全指南:从基础列表到虚拟滚动与网格布局 本指南围绕 antd 仓库 List 组件文档 https://link.gitc
前端UI组件设计系统Ant Design List 组件完全指南:从基础列表到虚拟滚动的高性能数据展示方案
Ant Design List 组件完全指南:从基础列表到虚拟滚动的高性能数据展示方案 List(列表)是 Ant Design 中最基础、应用最广泛的数据展示
前端UI组件设计系统
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考