Vue树形组件如何实现点击项切换颜色并重置上一项颜色
问题原因
你现在的实现里每个递归渲染的TreeView组件实例都是独立维护自身的col颜色状态,实例之间没有统一的状态同步机制,点击新节点时无法通知旧的选中节点重置颜色,自然实现不了互斥选中的效果。
修复方案
不用手动查找上一个选中节点改样式,利用Vue响应式特性,把选中状态提升为所有树节点共享的响应式状态,每个节点根据「当前节点ID是否等于全局选中ID」来判断自身背景色即可,步骤如下:
- 给组件加依赖注入配置,让所有递归生成的树节点共享同一个选中状态对象,不用逐层传参
- 把原来绑定内部
col变量的背景色样式,改成对比共享选中ID的动态判断 - 修正点击事件的传参问题,加事件冒泡阻止,避免点子节点误触发父节点逻辑
完整修正代码
组件脚本部分
export default { name: 'TreeView', // 递归组件必须设置name字段 props: { tree: { type: Object, required: true }, depth: { type: Number, default: 0 } }, // 注入父级传递的共享选中状态 inject: { treeSelection: { default: null } }, data() { return { isOpen: false, // 根节点初始化共享选中状态 innerSelection: { activeId: null } } }, // 向所有子节点传递共享选中状态 provide() { return { treeSelection: this.treeSelection || this.innerSelection } }, computed: { isFolder() { return this.tree.children && this.tree.children.length } }, methods: { toggle(e) { if (!this.isFolder) return this.isOpen = !this.isOpen }, getEl(e) { // 阻止事件冒泡,避免触发父节点点击逻辑 e.stopPropagation() // 更新共享的选中节点ID,所有节点自动同步样式 this.treeSelection.activeId = this.tree.id return this.tree.id } } }
模板部分
<template> <li class="main__li list" :style="{ 'margin-left': `${depth * 20}px`, 'background-color': treeSelection.activeId === tree.id ? 'blue' : 'initial' }" @click="toggle($event); getEl($event)" :title="tree.name" > {{ tree.name }} </li> <ul v-show="isOpen" v-if="isFolder" class="ul__ctg list"> <TreeView v-for="(chld, inx) in tree.children" :key="inx" :tree="chld" :depth="depth + 1" /> </ul> </template>
逻辑说明
- 所有递归渲染的树节点共享同一个响应式的
treeSelection对象,不管点击哪一层的节点,只要修改activeId为当前节点ID,所有节点都会自动重新计算背景色:ID匹配的显示蓝色,不匹配的回到默认初始色,完全不需要手动操作DOM查找旧节点重置样式。 - 加了
e.stopPropagation()是为了避免点击子节点时,事件冒泡到父级li触发父节点的选中逻辑,导致选中状态错乱。 - 修正了原来模板里
toggle(e)的写法错误,模板里要取事件对象必须显式写$event传参,否则会报e is not defined的错误。 - 递归组件必须显式设置
name字段,否则组件内部调用自身会找不到组件定义。
内容的提问来源于stack exchange,提问作者Netervey
相关产品推荐
相关产品推荐

