Vue组件递归调用需显式命名(如defineOptions({name:'TreeNode'}))、设置终止条件(如v-if="node.children?.length")、正确传递数据与唯一key(如:key="child.id"),并独立维护交互状态(如ref(false)控制展开)。

Vue 组件递归调用,本质是让一个组件在自己的模板中渲染自身实例,适用于树形结构(如菜单、目录、评论嵌套等)。关键不在“写个函数反复执行”,而在于“数据驱动 + 条件控制”的声明式渲染。
必须显式命名组件
Vue 无法识别未命名的组件自引用。在 <script setup> 中需通过 name 选项声明组件名;在 Options API 中直接写 name: 'TreeNode':
-
组合式 API 示例:
defineOptions({ name: 'TreeNode' }) -
Options API 示例:
export default { name: 'TreeNode', props: ['node'] } - 命名后,模板中才能合法使用
<TreeNode>标签调用自己
必须设置递归终止条件
没有终止条件会无限渲染,触发栈溢出或页面卡死。最常用判断依据是子节点是否存在且非空:
- 用
v-if="node.children && node.children.length"或v-if="Array.isArray(node.children) && node.children.length" - 避免只写
v-if="node.children"——若后端返回children: null或children: undefined,该条件仍为 false,但若误传为children: []却没加.length判断,可能意外跳过终止逻辑 - 不推荐仅靠层级数(如
level < 5)作为唯一终止条件,它可能提前截断真实数据
正确传递数据与 key
每次递归都是一次独立渲染,需确保子组件接收到准确的子节点数据,并拥有稳定唯一的 key:
立即学习“前端免费学习笔记(深入)”;
- 用
v-for遍历node.children时,:key必须基于子节点自身标识(如:key="child.id") - 若 ID 不全局唯一(例如多层同 ID),建议拼接路径:
:key="`${node.id}-${child.id}` - props 逐级透传:父节点传
:node="child",子组件再用相同方式处理其children
可选:添加交互状态(展开/收起)
每个节点可独立控制是否显示子节点,状态不应共享,需在组件内部维护:
- 在
setup()中用ref(false)或data()返回isOpen: false - 点击标题切换
isOpen,再用v-if="isOpen && node.children?.length"控制子树渲染 - 注意:不要把
isOpen放在原始数据里(如node.expanded),否则多个组件实例会互相干扰


















