ant-design-vue TreeSelect 树选择组件完全指南:API 详解、源码实现与实战示例
【免费下载链接】ant-design-vue🌈 An enterprise-class UI components based on Ant Design and Vue. 🐜项目地址: https://gitcode.com/gh_mirrors/an/ant-design-vue
TreeSelect 是 ant-design-vue 提供的树型选择控件,它复用 Select 的选择交互,但选项数据结构是树形的,天然适合公司层级、学科系统、分类目录等层级化数据的录入场景。本文以 components/tree-select/index.zh-CN.md 为骨架,结合组件源码 components/tree-select/index.tsx、官方 Demo(basic.vue 等 13 个示例)与单元测试,系统讲解 TreeSelect 的全部 API、常用场景组合与底层实现原理,帮助你从会用走向用得明白。
何时使用 TreeSelect
TreeSelect 与 Select 的交互方式类似(点击展开下拉、输入过滤、多选打 Tag),唯一区别在于选项的数据结构是树形的。当你的候选数据存在明显的层级关系时,例如:
- 公司组织架构(部门 → 小组 → 成员);
- 学科体系(学院 → 专业 → 课程);
- 商品/内容分类目录(一级分类 → 二级分类 → 叶子节点)。
就应当优先选用 TreeSelect,它内置了父节点展开/收起、子树级联勾选、异步加载子节点等树形交互能力,而普通 Select 只能平铺展示所有选项。
快速上手:基本用法
先看官方最简示例 basic.vue,它演示了v-model:value受控取值、show-search搜索、allow-clear清除、tree-default-expand-all默认展开以及treeData数据源:
<template> <a-tree-select v-model:value="value" show-search style="width: 100%" :dropdown-style="{ maxHeight: '400px', overflow: 'auto' }" placeholder="Please select" allow-clear tree-default-expand-all :tree-data="treeData" tree-node-filter-prop="label" > <template #title="{ value: val, label }"> <b v-if="val === 'parent 1-1'" style="color: #08c">sss</b> <template v-else>{{ label }}</template> </template> </a-tree-select> </template> <script lang="ts" setup> import { ref, watch } from 'vue'; import type { TreeSelectProps } from 'ant-design-vue'; const value = ref<string>(); const treeData = ref<TreeSelectProps['treeData']>([ { label: 'root 1', value: 'root 1', children: [ { label: 'parent 1', value: 'parent 1', children: [ { label: 'parent 1-0', value: 'parent 1-0', children: [ { label: 'my leaf', value: 'leaf1' }, { label: 'your leaf', value: 'leaf2' }, ]}, { label: 'parent 1-1', value: 'parent 1-1' }, ], }, { label: 'parent 2', value: 'parent 2' }, ], }, ]); watch(value, () => { console.log(value.value); }); </script>要点说明:
v-model:value:组件的受控值。在源码 index.tsx 中声明了'onUpdate:value'事件,因此支持 Vue 3 的v-model:value语法;单选时是string,多选/可勾选时是string[];#title插槽:自定义树节点渲染,插槽参数中包含value、label、title等字段,可用作高亮、图标或富文本展示;tree-node-filter-prop="label":指明搜索过滤时依据label字段匹配(默认是value)。
TreeSelect 完整 API 详解
TreeSelect 共提供 42 个核心属性,本文按数据、选择、搜索、展开加载、外观交互五个维度分组讲解,保证每个参数都给出类型、默认值与适用场景。
数据配置:treeData / treeDataSimpleMode / fieldNames
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|---|---|---|---|---|
| treeData | treeNodes 数据,如果设置则不需要手动构造 TreeNode 节点(value 在整个树范围内唯一) | array<{value, label, children, [disabled, disableCheckbox, selectable]}> | [] | |
| treeDataSimpleMode | 使用简单格式的 treeData(此时 treeData 应为[{id:1, pId:0, value:'1', label:"test1",...}],pId是父节点的 id),可传入对象自定义id/pId字段名 | false | Array<{ id: string, pId: string, rootPId: null }> | false | |
| fieldNames | 替换 treeNode 中 label、value、children 字段为 treeData 中对应的字段 | object | {children:'children', label:'title', value:'value'} | 3.0.0 |
| replaceFields | 替换 treeNode 中 label、value、key、children 字段(旧 API) | object | {children:'children', label:'title', key:'key', value:'value'} | 1.6.1(3.0.0 废弃) |
| treeNodeLabelProp | 作为显示内容的 prop 设置 | string | 'title' |
- treeData 是首选数据源。源码 index.tsx 在初始化时会发出警告:
children方式(手写a-tree-select-node)已废弃,请改用treeData; - treeDataSimpleMode 扁平化数据:当后端返回的是
id/pId扁平列表时,无需手动组装嵌套结构,组件会自动按pId建树。异步加载示例 async.vue 即采用此模式; - fieldNames 适配后端字段名:后端字段叫
name而非title时,通过:field-names="{ children: 'children', label: 'name', value: 'value' }"映射,完整示例见 replaceFields.vue。对应地,tree-node-filter-prop也要同步改为"name"; - replaceFields 已废弃:源码 index.tsx 会打印
replaceFields is deprecated, please use fieldNames instead的告警,且实现上直接取fieldNames = props.replaceFields(index.tsx)兼容旧写法。
选择与回填:value / multiple / treeCheckable / showCheckedStrategy
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|---|---|---|---|---|
| value(v-model) | 指定当前选中的条目 | string/string[] | - | |
| defaultValue | 指定默认选中的条目(非受控) | string/string[] | - | |
| multiple | 支持多选(设置 treeCheckable 时自动变为 true) | boolean | false | |
| treeCheckable | 显示 checkbox | boolean | false | |
| treeCheckStrictly | checkable 状态下节点选择完全受控(父子节点选中状态不再关联),会使labelInValue强制为 true | boolean | false | |
| showCheckedStrategy | 定义选中项回填方式:TreeSelect.SHOW_ALL显示所有选中节点(含父节点);TreeSelect.SHOW_PARENT只显示父节点(其下所有子节点均选中时);默认只显示子节点 | enum{SHOW_ALL, SHOW_PARENT, SHOW_CHILD} | TreeSelect.SHOW_CHILD | |
| labelInValue | 是否把每个选项的 label 包装进 value,value 类型从string变为{value: string, label: VNode, halfChecked: string[]} | boolean | false |
multiple与treeCheckable的关系:源码 index.tsx 计算isMultiple = !!(props.treeCheckable || props.multiple),即开启treeCheckable时多选自动生效,同时会输出告警提示你无需再手动传multiple;showCheckedStrategy的用法:在 checkable.vue 中通过import { TreeSelect } from 'ant-design-vue'取TreeSelect.SHOW_PARENT,用于"父节点全选中时只回填父节点"。这三个常量由源码 index.tsx 通过Object.assign(TreeSelect, { TreeNode, SHOW_ALL, SHOW_PARENT, SHOW_CHILD, install })静态挂载;treeCheckStrictly:适合"父子独立勾选"的场景(如权限分配),此时labelInValue被强制置为 true;- 多选 Tag 的辅助参数:
maxTagCount(最多显示 tag 数)、maxTagPlaceholder(隐藏 tag 时的占位内容,slot/function(omittedValues)),在 virtual-scroll.vue 中可以看到:max-tag-count="10"的实际用法。
搜索与过滤:showSearch / filterTreeNode / searchValue
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|---|---|---|---|---|
| showSearch | 在下拉中显示搜索框(仅在单选模式下生效) | boolean | false | |
| searchPlaceholder | 搜索框默认文字 | string|slot | - | |
| searchValue(v-model) | 搜索框的值,可通过search事件获取用户输入 | string | - | |
| filterTreeNode | 是否根据输入项进行筛选,默认用 treeNodeFilterProp 的值作为要筛选的属性;也可传Function(inputValue, treeNode)自定义(需返回 bool) | boolean | Function | Function | |
| treeNodeFilterProp | 输入项过滤对应的 treeNode 属性 | string | 'value' |
- 单选模式下开启
show-search后,下拉面板顶部会出现搜索框,输入内容即时过滤树节点;多选模式本身就支持输入过滤,无需额外开启; searchValue与search事件配合可实现搜索值的完全受控:源码 index.tsx 中handleSearch同时触发update:searchValue与search,即v-model:search-value可直接绑定;- 若树节点显示文本与 value 不一致(如 value 是编码、label 是名称),记得将
treeNodeFilterProp设为'label',否则按默认的value过滤可能搜不到用户想输入的中文名。
展开与异步加载:treeExpandedKeys / loadData / treeLoadedKeys
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|---|---|---|---|---|
| treeDefaultExpandAll | 默认展开所有树节点 | boolean | false | |
| treeDefaultExpandedKeys | 默认展开的树节点(非受控) | string[] | number[] | - | |
| treeExpandedKeys(v-model) | 设置展开的树节点(受控) | string[] | number[] | - | |
| loadData | 异步加载数据 | function(node) | - | |
| treeLoadedKeys | (受控)已经加载的节点,需要配合loadData使用 | string[] | [] | 3.3.0 |
异步加载的完整范式见 async.vue:配合tree-data-simple-mode使用扁平数据,onLoadData返回一个 Promise,在 Promise 中请求接口并追加子节点,resolve(true)通知组件展开完成:
const onLoadData = (treeNode: TreeSelectProps['treeData'][number]) => { return new Promise(resolve => { const { id } = treeNode.dataRef; setTimeout(() => { treeData.value = treeData.value.concat([ genTreeNode(id, false), genTreeNode(id, true), genTreeNode(id, true), ]); resolve(true); }, 300); }); };注意loadData回调的treeNode对象需要通过treeNode.dataRef拿到原始数据(包含id、isLeaf等字段),叶子节点应在数据中标记isLeaf: true,否则组件会一直尝试加载。
外观、尺寸与交互细节
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|---|---|---|---|---|
| allowClear | 显示清除按钮 | boolean | false | |
| disabled | 是否禁用 | boolean | false | |
| placeholder | 选择框默认文字 | string|slot | - | |
| size | 选择框大小,可选largesmall | string | 'default' | |
| status | 设置校验状态 | 'error' | 'warning' | - | 3.3.0 |
| placement | 选择框弹出的位置 | bottomLeftbottomRighttopLefttopRight | bottomLeft | 3.3.0 |
| suffixIcon | 自定义的选择框后缀图标 | VNode | slot | - | |
| treeIcon | 是否展示 TreeNode title 前的图标(无默认样式,需自行定义) | boolean | false | |
| treeLine | 是否展示线条样式,可传{ showLeafIcon }对象控制叶子图标 | boolean | object | false | 3.0 |
| virtual | 设置 false 时关闭虚拟滚动 | boolean | true | 3.0 |
| listHeight | 设置弹窗滚动高度 | number | 256 | |
| dropdownMatchSelectWidth | 下拉菜单与选择器同宽,默认设置min-width,值小于选择框宽度时忽略;false 会关闭虚拟滚动 | boolean | number | true | |
| dropdownStyle | 下拉菜单的样式 | object | - | |
| popupClassName | 下拉菜单的 className | string | - | 4.0 |
| getPopupContainer | 菜单渲染父节点,默认渲染到 body;遇到菜单滚动定位问题时可改为滚动区域并相对定位 | Function(triggerNode) | () => document.body | |
| notFoundContent | 下拉列表为空时显示的内容 | slot | Not Found | |
| tagRender | 自定义 tag 内容,多选时生效 | slot | - | 3.0 |
| title | 自定义标题 | slot | - | 3.0.0 |
分组场景示例:
- 校验状态:
status="error"/status="warning"可直接展示红/黄边框,见 status.vue。源码 index.tsx 通过getMergedStatus将 Form 表单上下文的校验状态与组件自身status合并,因此放进a-form-item时会自动继承表单校验结果,无需重复设置; - 弹出方向:
placement支持四个方向,默认bottomLeft;注意源码 index.tsx 中当 ConfigProvider 配置了 RTL 方向时,默认值会自动切换为bottomRight; - 虚拟滚动:
virtual默认开启,配合listHeight(默认 256,见 index.tsx 的initDefaultProps)与listItemHeight(默认 26)渲染。当树节点数量很大(数千级)时建议保持开启,参考 virtual-scroll.vue 中递归生成 10×10×10 级别数据量级的示例; - 线条样式:
treeLine传布尔值开启连接线,传对象可控制showLeafIcon,见 tree-line.vue;其样式语义与 Tree 组件的 showLine 一致; - 自定义 Tag:多选模式下用
#tagRender插槽接管已选项标签渲染,插槽参数为{ label, closable, onClose, option },custom-tag-render.vue 展示了利用option.color给不同节点 Tag 上色的做法;单选场景直接使用#title插槽即可; - 弹出容器:
getPopupContainer默认挂载到 body,若在滚动容器内出现下拉定位异常,应将其指向滚动区域并配合相对定位。
事件(Events)
| 事件名称 | 说明 | 回调参数 | 版本 |
|---|---|---|---|
| change | 选中树节点或输入值发生变化时调用 | function(value, label, extra) | |
| dropdownVisibleChange | 展开/收起下拉菜单的回调 | function(open) | 3.0 |
| search | 文本框值变化时回调 | function(value: string) | |
| select | 树节点被选中时调用 | function(value, node, extra) | |
| treeExpand | 展开树节点时调用 | function(expandedKeys) |
源码 index.tsx 展示了事件转发链:handleChange先触发update:value(支撑 v-model),再触发change,最后通知FormItemContext.onFieldChange()让 Form 感知字段变化;handleSearch同理同时触发update:searchValue与search;handleTreeExpand同时触发update:treeExpandedKeys与treeExpand。这解释了为什么searchValue、treeExpandedKeys都能直接使用v-model语法。
Tree 方法(Methods)
| 名称 | 描述 |
|---|---|
| blur() | 移除焦点 |
| focus() | 获取焦点 |
通过模板 ref 或useTemplateRef获取组件实例后调用。源码 index.tsx 用expose({ focus, blur })显式暴露这两个方法,内部委托给底层vc-tree-select的实例(treeSelectRef.value.focus?.())。
TreeNode props(组件式节点)
官方建议使用
treeData代替手写 TreeNode,免去手工构造的麻烦。
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|---|---|---|---|---|
| checkable | 当树为 checkable 时,设置独立节点是否展示 Checkbox | boolean | - | |
| disableCheckbox | 禁掉 checkbox | boolean | false | |
| disabled | 是否禁用 | boolean | false | |
| isLeaf | 是否是叶子节点 | boolean | false | |
| key | 此项必须设置(其值在整个树范围内唯一) | string | number | - | |
| selectable | 是否可选 | boolean | true | |
| title | 树节点显示的内容 | string|slot | '---' | |
| value | 默认根据此属性值进行筛选(其值在整个树范围内唯一) | string | - |
这些字段同时对应treeData数组中单个节点的可选扩展属性:disabled、disableCheckbox、selectable、isLeaf,在 checkable.vue 中可见{ label: 'Child Node3', value: '0-1-0', disabled: true }的用法——树数据中直接给节点打标即可,无需手动渲染组件节点。
源码实现:TreeSelect 是如何工作的
TreeSelect 在架构上是一层"薄封装",核心逻辑全部下沉到内部组件库 components/vc-tree-select(提供VcTreeSelect、TreeNode以及SHOW_ALL/SHOW_PARENT/SHOW_CHILD常量),ant-design-vue 层负责:
- Props 归一化:在 treeSelectProps() 中从
vcTreeSelectProps剔除showTreeIcon、treeMotion、inputIcon等内部细节,再补充suffixIcon、size、bordered、treeLine、status、popupClassName等对外属性,并通过initDefaultProps设置listHeight: 256、treeIcon: false、listItemHeight: 26、bordered: true等默认值; - 废弃 API 告警:对
children节点、treeCheckable时多余的multiple、replaceFields、dropdownClassName分别输出 dev 警告(index.tsx); - 主题与上下文集成:通过
useConfigInject('select', props)继承 ConfigProvider 的prefixCls、size、getPopupContainer、disabled、dropdownMatchSelectWidth、virtual等全局配置;结合FormItemInputContext实现表单校验联动;通过useCompactItemContext支持与a-space紧凑排列组合; - 样式体系:样式基于 CSS-in-JS 实现——
useSelectStyle(复用 Select 的样式)与useStyle(TreeSelect 专属样式)两个 wrapper 包裹渲染结果,wrapSelectSSR/wrapTreeSelectSSR同时支持 SSR 样式抽取; - 插槽映射:将 Vue 插槽统一转换为
v-slots与customSlots传给底层组件,并注入默认的treeCheckable勾选框样式插槽(index.tsx)。
组件的单元测试位于 components/tree-select/tests/index.test.js,通过项目共享的focusTest(验证 focus/blur 方法)与mountTest(验证组件可正常挂载、卸载且无副作用)保证基础契约稳定;快照测试见 components/tree-select/tests/snapshots/demo.test.js.snap。
实践建议汇总
- 数据量大(上千节点)时:保持
virtual开启,必要时调小listHeight或通过dropdownMatchSelectWidth={false}释放滚动条空间; - 后端扁平接口:优先用
tree-data-simple-mode+id/pId,避免前端递归组树; - 接口字段与组件默认字段不一致:用
fieldNames映射,并同步调整tree-node-filter-prop; - 表单校验:无需手动传
status,放入a-form-item后校验状态会自动联动;需要手动展示时再传status="error" | "warning"; - 多级勾选回填:按业务语义选择
SHOW_ALL(完整展示)或SHOW_PARENT(聚合展示),默认SHOW_CHILD只回填叶子; - 异步加载:叶子节点务必标记
isLeaf: true,并在loadData的 Promise resolve 后追加子节点数据。
如需查看更多场景,可直接阅读 components/tree-select/demo 目录下的全部 13 个示例(含 placement.vue、suffix.vue、highlight.vue),每个示例都是可直接复制运行的 Vue 3 SFC。
【免费下载链接】ant-design-vue🌈 An enterprise-class UI components based on Ant Design and Vue. 🐜项目地址: https://gitcode.com/gh_mirrors/an/ant-design-vue
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考