公司动态

Element Tree 展开与折叠的进阶实践:从基础操作到状态持久化

📅 2026/7/15 2:13:34
Element Tree 展开与折叠的进阶实践:从基础操作到状态持久化
1. Element Tree 展开与折叠的基础操作Element UI 的 Tree 组件是前端开发中常用的树形结构展示工具。在实际项目中我们经常需要控制树节点的展开和折叠状态。先来看最基础的操作方式1.1 全部展开的实现方法实现全部展开的核心思路是将所有父节点的 ID 存入default-expanded-keys数组。这个数组会告诉 Tree 组件哪些节点应该默认展开。我常用的做法是递归遍历树数据收集所有非叶子节点的 ID// 展开全部节点 expandAll() { this.defaultExpandedKeys [] const collectIds (nodes) { nodes.forEach(node { if (node.children node.children.length) { this.defaultExpandedKeys.push(node.id) collectIds(node.children) } }) } collectIds(this.treeData) }这个方法的关键点在于清空原有的展开节点数组递归遍历树结构收集所有有子节点的节点 ID将收集到的 ID 数组赋值给default-expanded-keys1.2 全部折叠的变通方案Element Tree 没有直接提供全部折叠的 API但我们可以通过重新加载数据来实现。实测下来最简单的方案是// 折叠全部节点 collapseAll() { this.defaultExpandedKeys [] // 通过重新赋值触发视图更新 this.treeData [...this.treeData] }这里有个小技巧直接修改defaultExpandedKeys为空的数组然后强制触发 Tree 组件的重新渲染。这种方式虽然简单粗暴但在大多数场景下都能稳定工作。2. 动态维护展开节点状态在复杂业务场景中简单的全部展开/折叠往往不能满足需求。我们需要更精细地控制每个节点的展开状态。2.1 监听节点展开/折叠事件Element Tree 提供了两个关键的事件监听器node-expand节点展开时触发node-collapse节点折叠时触发我们可以利用这两个事件来动态维护一个展开节点 ID 数组el-tree node-expandhandleNodeExpand node-collapsehandleNodeCollapse :default-expanded-keysexpandedKeys // 其他配置... / data() { return { expandedKeys: [] // 存储当前展开的节点ID } }, methods: { // 节点展开处理 handleNodeExpand(data) { if (!this.expandedKeys.includes(data.id)) { this.expandedKeys.push(data.id) } }, // 节点折叠处理 handleNodeCollapse(data) { const index this.expandedKeys.indexOf(data.id) if (index -1) { this.expandedKeys.splice(index, 1) } } }2.2 递归处理子节点状态当折叠一个父节点时我们通常希望它的所有子节点也自动折叠。这需要递归处理handleNodeCollapse(data) { // 移除当前节点 const index this.expandedKeys.indexOf(data.id) if (index -1) { this.expandedKeys.splice(index, 1) } // 递归移除所有子节点 const removeChildren (node) { if (node.children) { node.children.forEach(child { const childIndex this.expandedKeys.indexOf(child.id) if (childIndex -1) { this.expandedKeys.splice(childIndex, 1) } removeChildren(child) }) } } removeChildren(data) }这个实现确保了当父节点折叠时所有子节点的展开状态都会被清除避免出现父节点折叠但子节点仍然展开的不一致情况。3. 状态持久化实践页面刷新或数据重新加载后保持树的状态是提升用户体验的关键。3.1 本地存储方案最简单的持久化方案是使用浏览器的 localStorage// 保存状态 saveTreeState() { localStorage.setItem(treeExpandedKeys, JSON.stringify(this.expandedKeys)) } // 恢复状态 restoreTreeState() { const saved localStorage.getItem(treeExpandedKeys) if (saved) { this.expandedKeys JSON.parse(saved) } } // 在mounted钩子中调用恢复 mounted() { this.restoreTreeState() }3.2 服务端持久化对于需要跨设备或长期保存的场景可以将状态保存到服务端// 保存到服务端 async saveToServer() { try { await api.saveUserPreference({ key: treeState, value: this.expandedKeys }) } catch (error) { console.error(保存状态失败, error) } } // 从服务端获取 async loadFromServer() { try { const res await api.getUserPreference(treeState) if (res.data) { this.expandedKeys res.data.value || [] } } catch (error) { console.error(加载状态失败, error) } }4. 性能优化技巧处理大型树结构时性能问题不容忽视。以下是几个实测有效的优化方案4.1 延迟加载与虚拟滚动对于超大型树可以考虑实现延迟加载和虚拟滚动el-tree :propsprops :loadloadNode lazy / data() { return { props: { label: name, children: children, isLeaf: leaf } } }, methods: { async loadNode(node, resolve) { if (node.level 0) { // 加载根节点 const roots await api.getRootNodes() return resolve(roots) } // 加载子节点 const children await api.getChildren(node.data.id) resolve(children) } }4.2 避免不必要的重新渲染在 Vue 中可以通过以下方式优化渲染性能// 使用Object.freeze防止不必要响应式处理 async loadTreeData() { const res await api.getTreeData() this.treeData Object.freeze(res.data) } // 使用key控制复用 el-tree :keytreeVersion /当需要强制更新时只需修改 treeVersion 即可触发整个树的重新渲染。