vue 3递归组件需显式命名(defineoptions或普通script)、模板中自引用子节点、v-if检查children存在且非空作为终止条件,并用本地ref/reactive状态实现独立展开收起。

Vue 3 单文件组件实现递归调用,核心在于让组件能在自己的模板里合法引用自身——这需要明确的组件名称 + 合理的终止条件 + 正确的数据传递。不靠插件也能做,但命名方式在 script setup 下有特殊要求。
必须显式声明组件 name
Vue 3 的 script setup 默认不暴露 name,而模板中自引用(如 <treeitem></treeitem>)依赖这个名称。解决方法有两种:
- 在
script setup同级加一个普通<script></script>标签,写export default { name: 'TreeItem' } - 使用
defineOptions({ name: 'TreeItem' })(推荐,更简洁,需确保已启用unplugin-vue-define-options或 Vue CLI/Vite 默认支持)
没有 name,模板里的自引用标签会被视为未知组件,控制台报错或静默失败。
模板中直接调用自身标签
在组件的 <template></template> 里,像使用普通子组件一样写自己的标签名:
- 假设组件名为
TreeItem,就写<treeitem v-for="child in node.children" :key="child.id" :node="child"></treeitem> - 每次渲染都传入一个子节点数据(不是整个树),保证层级隔离
-
:key必须稳定唯一,优先用子节点自身 ID;若 ID 可能重复,可拼接路径(如:key="`${node.id}-${child.id}`)
一定得有终止条件,否则崩溃
递归不会自动停,必须靠条件控制是否继续渲染子级:
- 最稳妥写法:
v-if="node.children && node.children.length",同时检查存在性和非空性 - 避免只写
v-if="node.children"—— 若后端返回children: null或undefined,它为 false,看似安全;但若返回children: []却没判长度,就会意外跳过渲染 - 不要仅靠层级限制(如
level )作为主终止逻辑,它可能截断真实数据
每个实例独立维护交互状态
如果要做展开/收起,状态不能存在原始数据里(比如 node.expanded = true),否则所有同名节点会联动:
- 在
setup中用ref(false)或reactive({ isOpen: false })定义本地状态 - 点击标题时只改当前实例的
isOpen,再结合v-if="isOpen && node.children?.length"控制子树显示 - 这样每一层节点都能独立开关,互不影响
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










