Element Plus Tree 组件跨树操作获取不到node的问题
前言
在使用 Element Plus Tree 组件时,我遇到了一个看似简单但极其困惑的问题:当我通过 append 方法将数据添加到目标树后,立即调用 getNode(data) 却返回null。这个问题困扰了我很久,经过深入的调试和源码分析,最终发现了问题的根本原因。
问题描述
业务场景
我的应用中有两个 Tree 组件:
- sourceTree:显示可选的工作列表
- targetTree:显示已选的工作列表
用户可以从 sourceTree 中选择工作,然后将其添加到 targetTree 中。同时,用户也可以从 targetTree 中删除已选的工作。
问题现象
// 添加数据到目标树
targetTree.value?.append(data, parent);
// 立即尝试获取节点 - 返回 null!
const node = targetTree.value?.getNode(data);
console.log(node); // null
奇怪的是:
- 数据确实被添加到了树中(nodesMap 中确实有新节点,nodesMap是el-tree实例内部的一个状态)
- 同样的 getNode(data) 在 sourceTree 中工作正常
- 官方文档明确说明 getNode 可以传递 nodeData 或 nodeKey
初步分析与错误尝试
尝试1:怀疑是异步更新问题
// 使用 nextTick 等待更新
await nextTick();
const node = targetTree.value?.getNode(data);
console.log(node); // 仍然是 null!
尝试2:怀疑是 node-key 问题
根据网上的一些资料,我尝试设置 node-key:
<el-tree
:data="targetData"
node-key="workContent"
ref="targetTree"
>
但是不行,因为我的targetData是混合数据结构,每一阶的数据结构都不一样,而element-plus官方提供的node-key属性只能传递一个string
尝试3:添加唯一 ID
// 为数据添加唯一标识
if (!data.id) {
data.id = `work_${data.typeID}_${data.apInterventionID}_${Date.now()}`;
}
破坏了原有的数据结构,导致树不显示,而且复杂度明显提高,不方便维护
深入源码分析
getNode 方法的实现
通过分析 Element Plus 源码,我发现了 getNode 方法的关键实现:
// tree-store.js
getNode(data) {
if (data instanceof Node) return data;
const key = isObject(data) ? getNodeKey(this.key, data) : data;
return this.nodesMap[key] || null;
}
getNodeKey 的关键逻辑
// util.js
const getNodeKey = function(key, data) {
if (!key) return data[NODE_KEY]; // NODE_KEY = "$treeNodeId"
return data[key];
};
关键发现:
- 当没有设置 node-key 时,getNode 依赖于 data.$treeNodeId 属性
- 这个属性是通过 markNodeData 函数添加的隐藏属性
markNodeData 的实现
// util.js
const markNodeData = function(node, data) {
if (!data || data[NODE_KEY]) return;
Object.defineProperty(data, NODE_KEY, {
value: node.id,
enumerable: false,
configurable: false,
writable: false
});
};
问题根源的发现
调试验证
为了验证我的推测,我添加了调试代码:
targetTree.value?.append(data, parent);
// 验证我的分析
console.log("data对象的$treeNodeId:", data.$treeNodeId);
console.log("targetTree的nodesMap keys:", Object.keys(targetTree.value?.store.nodesMap || {}));
console.log("是否匹配:", Object.keys(targetTree.value?.store.nodesMap || {}).includes(String(data.$treeNodeId)));
调试结果
data对象的$treeNodeId: 8
targetTree的nodesMap keys: ['358', '359', '360', '361', '362', '363', '364', '365', '366']
是否匹配: false
答案很明显了,问题的根本原因,就是当我从 sourceTree 移动数据到 targetTree 时:
- data 对象保留了 sourceTree 的 $treeNodeId = 8
- 但 targetTree 中新建的节点 ID 是 366
- 所以 getNode(data) 查找 $treeNodeId = 8,在 targetTree 中找不到!
偷懒复用的后果
最终解决方案
方案一
直接操作data,不走el-tree实例
方案二
如果可以的话,在模板中直接拿到node进行操作
方案三(不推荐)
删除旧的内部标记$treeNodeId。但这么做可能会影响原tree,且此状态本身是组件库内部管理的,不合适在外部操作
总结
Element Plus Tree 的内部机制
- 没有 node-key 时:依赖内部的 $treeNodeId 标记机制
- 跨树操作时:深拷贝或使用其它方式获取node
- 推荐做法:始终设置合适的 node-key

浙公网安备 33010602011771号