6.5 el-tree 组件
Element UI 的 el-tree
组件功能强大且易于使用。
一、 核心作用与基本结构
<el-tree>
组件用于以树形结构展示具有父子层级关系的数据。用户可以展开/折叠节点,进行选择、拖拽等操作。
最基础的示例:
<template><el-tree:data="treeData":props="defaultProps"@node-click="handleNodeClick"></el-tree>
</template><script>
export default {data() {return {// 树形数据源treeData: [{label: '一级 1',children: [{label: '二级 1-1',children: [{label: '三级 1-1-1'}]}]}, {label: '一级 2',children: [{label: '二级 2-1'}, {label: '二级 2-2'}]}],// 定义数据中关键字段的名称defaultProps: {children: 'children', // 指定子节点数组的字段名label: 'label' // 指定节点显示文本的字段名}}},methods: {handleNodeClick(data, node, component) {console.log('点击了节点:', data);// data: 节点对应的原始数据对象// node: 节点的 Node 对象 (包含 key, level, parent 等信息)// component: 节点的 Vue 组件实例}}
}
</script>
二、 核心属性详解
<el-tree>
的行为和外观由众多属性控制。
data
:- 作用: 树形结构的数据源,一个包含层级关系的数组。
- 类型:
Array
- 格式: 数组中的每个对象代表一个节点。通常包含
label
(显示文本) 和children
(子节点数组) 字段。具体字段名由props
属性定义。
props
:- 作用: 非常关键!用于定义数据对象中哪些字段对应树节点的特定属性。
- 类型:
Object
- 可配置的键值:
label
: 节点显示的文本对应的字段名。Function(data, node) | String
children
: 子节点数组对应的字段名。String
disabled
: 是否禁用节点对应的字段名。Function(data, node) | String
isLeaf
: 是否为叶子节点对应的字段名。Function(data, node) | String
(用于懒加载)
- 示例:
props: {label: 'name', // 显示文本用 data.namechildren: 'subItems', // 子节点用 data.subItemsdisabled: 'locked', // 禁用状态用 data.locked (true/false)isLeaf: 'isFile' // 是否叶子节点用 data.isFile (true/false) }
node-key
:- 作用: 极其重要!每个节点的唯一标识字段名。在使用
check
、expand
、drag
等功能时,必须设置此属性,否则会报错或功能异常。 - 类型:
String
- 示例:
:node-key="'id'"
(假设数据中每个节点都有唯一的id
字段)
- 作用: 极其重要!每个节点的唯一标识字段名。在使用
default-expand-all
:- 作用: 是否默认展开所有节点。
- 类型:
Boolean
- 默认值:
false
- 注意: 如果数据量很大,不建议使用,会影响性能。
expand-on-click-node
:- 作用: 是否在点击节点本身(而非展开图标)时展开/折叠节点。
- 类型:
Boolean
- 默认值:
true
- 示例: 设为
false
时,只有点击+/-
图标才能展开/折叠。
show-checkbox
:- 作用: 节点是否显示复选框 (
Checkbox
)。 - 类型:
Boolean
- 默认值:
false
- 注意: 开启后,需要配合
check-strictly
和check-on-click-node
等属性使用。
- 作用: 节点是否显示复选框 (
check-on-click-node
:- 作用: 是否在点击节点时选中/取消选中节点(仅在
show-checkbox
为true
时有效)。 - 类型:
Boolean
- 默认值:
false
- 示例: 设为
true
时,点击节点文本即可勾选复选框。
- 作用: 是否在点击节点时选中/取消选中节点(仅在
check-strictly
:- 作用: 在显示复选框的情况下,是否严格的遵循父子不互相关联。
- 类型:
Boolean
- 默认值:
false
- 解释:
false
(默认): 父节点选中时,所有子节点自动选中;子节点全选中时,父节点自动选中(半选或全选)。true
: 父子节点选中状态完全独立,互不影响。
draggable
:- 作用: 是否开启拖拽节点功能。
- 类型:
Boolean
- 默认值:
false
- 注意: 开启后需要监听
@node-drag-start
,@node-drag-enter
,@node-drag-leave
,@node-drag-over
,@node-drag-end
,@node-drop
事件来控制拖拽行为。
allow-drag
:- 作用: 判断节点能否被拖拽。
Function(node)
- 类型:
Function
- 示例:
:allow-drag="node => node.data.type !== 'root'"
(根节点不能拖拽)
- 作用: 判断节点能否被拖拽。
allow-drop
:- 作用: 拖拽时判定目标节点能否被放置。
Function(draggingNode, dropNode, type)
。type
的值为'prev'
,'inner'
,'next'
。 - 类型:
Function
- 示例:
:allow-drop="allowDrop"
(见下方完整示例)
- 作用: 拖拽时判定目标节点能否被放置。
highlight-current
:- 作用: 是否高亮当前选中的节点。
- 类型:
Boolean
- 默认值:
false
default-expanded-keys
:- 作用: 默认展开的节点的
key
数组。需要配合node-key
使用。 - 类型:
Array
- 示例:
:default-expanded-keys="[2, 3]"
- 作用: 默认展开的节点的
default-checked-keys
:- 作用: 默认勾选的节点的
key
数组。需要配合node-key
和show-checkbox
使用。 - 类型:
Array
- 作用: 默认勾选的节点的
filter-node-method
:- 作用: 自定义节点过滤方法。配合
filter
方法使用。 - 类型:
Function(value, data, node)
- 说明: 返回
true
表示显示该节点,false
表示隐藏。
- 作用: 自定义节点过滤方法。配合
三、 核心事件详解
事件是与树形控件交互的主要方式。
@node-click
:- 触发: 点击某个节点时。
- 参数:
(data, node, component)
- 节点数据、节点对象、组件实例。
@node-contextmenu
:- 触发: 右键点击某个节点时。
- 参数:
(event, data, node, component)
- 鼠标事件、节点数据、节点对象、组件实例。常用于弹出右键菜单。
@check-change
:- 触发: 节点选中状态发生变化时(仅在
show-checkbox
为true
时有效)。 - 参数:
(data, checked, indeterminate)
- 节点数据、是否被选中、是否半选。
- 触发: 节点选中状态发生变化时(仅在
@check
:- 触发: 当复选框被点击,或者使用
setChecked/Keys
方法时。 - 参数:
(data, checkedStatus)
- 节点数据、选中状态对象{ checkedNodes, checkedKeys, halfCheckedNodes, halfCheckedKeys }
。
- 触发: 当复选框被点击,或者使用
@current-change
:- 触发: 当前选中节点变化时(需要
highlight-current
为true
)。 - 参数:
(data, node)
- 当前节点数据、节点对象。
- 触发: 当前选中节点变化时(需要
@node-expand
/@node-collapse
:- 触发: 节点被展开 / 折叠时。
- 参数:
(data, node, component)
- 节点数据、节点对象、组件实例。
拖拽相关事件 (
draggable="true"
时):@node-drag-start
: 开始拖拽时。@node-drag-enter
: 拖拽进入目标节点时。@node-drag-leave
: 拖拽离开目标节点时。@node-drag-over
: 拖拽在目标节点上时。@node-drag-end
: 拖拽结束时。@node-drop
: 拖拽成功放置时。这是处理数据更新的关键事件。- 参数: 通常包含
(draggingNode, dropNode, dropType, ev)
。dropType
是'prev'
,'inner'
,'next'
。
四、 常用功能详解
1. 多选 (带复选框)
<el-tree:data="treeData":props="defaultProps"node-key="id"show-checkbox:default-checked-keys="[5]" <!-- 默认选中 id 为 5 的节点 -->@check-change="handleCheckChange"
>
</el-tree><script>
export default {methods: {handleCheckChange(data, checked, indeterminate) {console.log(`节点 ${data.label} 选中状态: ${checked}, 半选状态: ${indeterminate}`);},// 获取当前选中的节点getCheckedNodes() {return this.$refs.tree.getCheckedNodes();},// 获取当前选中节点的 keygetCheckedKeys() {return this.$refs.tree.getCheckedKeys();}}
}
</script>
2. 懒加载 (Lazy Load)
适用于数据量巨大或需要按需加载的场景(如文件系统)。
<el-tree:data="treeData":props="props"node-key="id":load="loadNode"lazy@node-click="handleNodeClick"
>
</el-tree><script>
export default {data() {return {props: {label: 'name',children: 'zones',isLeaf: 'leaf' // 标记是否为叶子节点}}},methods: {loadNode(node, resolve) {// node: 当前节点的 Node 对象// resolve: 回调函数,用于返回子节点数据if (node.level === 0) {// 根节点return resolve([{ name: 'Root1', leaf: false }, { name: 'Root2', leaf: true }]);}if (node.level > 3) return resolve([]); // 最多展示到 4 级// 模拟异步加载setTimeout(() => {const data = [{name: 'Child1',leaf: node.level >= 3 // 4级以后是叶子节点}, {name: 'Child2',leaf: node.level >= 3}];resolve(data);}, 500);}}
}
</script>
3. 自定义节点内容 (使用插槽)
这是实现复杂节点(如带图标、按钮)的关键。
<el-tree:data="treeData":props="defaultProps"node-key="id"default-expand-all@node-click="handleNodeClick"
><!-- 使用 scoped slot 自定义节点内容 --><span class="custom-tree-node" slot-scope="{ node, data }"><span>{{ node.label }}</span><span><el-buttontype="text"size="mini"@click="() => append(data)">Append</el-button><el-buttontype="text"size="mini"@click="() => remove(node, data)">Delete</el-button></span></span>
</el-tree><style>
.custom-tree-node {flex: 1;display: flex;align-items: center;justify-content: space-between;font-size: 14px;padding-right: 8px;
}
</style><script>
export default {methods: {append(data) {const newChild = { id: id++, label: 'testtest', children: [] };if (!data.children) {this.$set(data, 'children', []);}data.children.push(newChild);},remove(node, data) {const parent = node.parent;const children = parent.data.children || parent.data;const index = children.findIndex(d => d.id === data.id);children.splice(index, 1);}}
}
</script>
4. 拖拽 (Drag & Drop)
<el-tree:data="treeData":props="defaultProps"node-key="id"draggable:allow-drop="allowDrop":allow-drag="allowDrag"@node-drop="handleDrop"
>
</el-tree><script>
export default {methods: {// 判断节点是否可以被拖拽allowDrag(node) {// 例如:根节点不能拖拽return node.data.id !== 1;},// 判断拖拽时目标节点是否可以被放置allowDrop(draggingNode, dropNode, type) {// 例如:禁止将节点拖拽到根节点下if (dropNode.data.id === 1 && type === 'inner') {return false;}// 例如:禁止将父节点拖拽到其子节点下if (type === 'inner' || type === 'prev') {let parent = dropNode.parent;while (parent && parent !== this.$refs.tree.root) {if (parent.data.id === draggingNode.data.id) {return false;}parent = parent.parent;}}return true;},// 处理拖拽放置后的逻辑handleDrop(draggingNode, dropNode, dropType, ev) {console.log('Drop:', draggingNode, dropNode, dropType);// 在这里更新你的数据源 treeData// 通常需要根据 dropType ('prev', 'inner', 'next') 将 draggingNode.data 插入到 dropNode.data 的相应位置this.$message.success('节点已移动');}}
}
</script>
5. 节点过滤 (Filter)
<el-inputplaceholder="输入关键字进行过滤"v-model="filterText">
</el-input><el-treeclass="filter-tree":data="treeData":props="defaultProps"node-key="id":filter-node-method="filterNode"ref="tree"
>
</el-tree><script>
export default {watch: {// 监听 filterText 的变化filterText(val) {// 调用树的 filter 方法this.$refs.tree.filter(val);}},methods: {// 自定义过滤逻辑filterNode(value, data) {if (!value) return true; // 空值时显示所有// 过滤 label 字段return data.label.indexOf(value) !== -1;// 也可以过滤其他字段// return data.label.indexOf(value) !== -1 || data.description.indexOf(value) !== -1;}}
}
</script>
五、 高级技巧与最佳实践
node-key
必须设置: 这是几乎所有高级功能(多选、展开、拖拽)正常工作的前提。- 性能考虑:
- 避免一次性加载过深或过宽的树。
- 大数据量时优先使用懒加载 (
lazy
)。 - 避免使用
default-expand-all
。
- 数据更新: 直接修改
data
数组可能不会触发视图更新。推荐使用 Vue 的响应式方法:this.$set(data, 'children', newChildrenArray)
this.$delete(childrenArray, index)
- 或者直接替换整个
treeData
数组。
- 方法调用:
<el-tree>
提供了丰富的实例方法,如getCheckedNodes()
,setCheckedKeys()
,updateKeyChildren()
,getNode()
,remove()
,append()
,filter()
等,通过ref
调用。 - 样式定制: 可以通过 CSS 覆盖
.el-tree-node
,.el-tree-node__content
,.el-tree-node__label
等类名来定制样式。
总结
Element UI 的 <el-tree>
组件是一个功能非常全面的树形控件。掌握其核心属性 data
, props
, node-key
和 show-checkbox
,以及关键事件 @node-click
和 @check-change
是基础。灵活运用 scoped slot
可以实现高度自定义的节点内容。对于复杂场景,懒加载 (lazy
) 是处理大数据量的利器,拖拽 (draggable
) 和 过滤 (filter
) 功能则极大地提升了交互性。node-key
是几乎所有高级功能的基石,务必正确设置。