递归组件中slots不会自动穿透,必须显式透传:在每一层用包裹并重传,确保子节点能访问各自作用域数据。

Slots 是 Vue 中内容分发的核心机制
在递归组件中,slots 不会自动穿透到子级递归调用处。默认情况下,父组件传入的插槽内容只作用于当前组件实例,不会随着 <component :is="..."></component> 或递归调用(如 <treenode :nodes="childNodes"></treenode>)向下传递。要实现嵌套结构中的内容分发,必须显式地在每一层递归组件中 接收并重新渲染 slot。
递归组件中正确使用 slots 的关键写法
以树形节点组件 TreeNode.vue 为例,需同时满足:支持递归渲染 + 支持自定义节点内容(如图标、操作按钮等):
- 在模板中用
<slot></slot>渲染当前节点的自定义内容(即“节点本体”) - 对子节点列表,用
v-for渲染子TreeNode组件,并确保 把当前作用域的 slot 显式透传下去 - 使用
v-bind="$attrs"和v-on="$listeners"(Vue 2)或v-bind="$attrs"(Vue 3 默认继承)保持属性/事件透传,但 slot 必须手动处理
Vue 3 中推荐的递归 slot 写法(简洁可靠)
利用 <slot></slot> 默认行为 + defineSlots 类型提示(可选),并在子组件调用时用 <template></template> 显式转发:
<!-- TreeNode.vue -->
<template>
<div class="tree-node">
<!-- 当前节点内容由父级传入的 default slot 渲染 -->
<slot :node="node" />
<pre class="brush:php;toolbar:false;"><!-- 递归渲染子节点,且把当前 slot 向下透传 -->
<TreeNode
v-for="child in node.children"
:key="child.id"
:node="child"
>
<!-- 关键:把上层 slot 再次作为 default 插槽传给子组件 -->
<template #default="childSlotProps">
<slot name="default" v-bind="childSlotProps" />
</template>
</TreeNode>
这样,无论嵌套多深,每个节点都渲染相同结构的 slot 内容,且能访问各自节点数据(通过 v-bind="childSlotProps")。
立即学习“前端免费学习笔记(深入)”;
避免常见错误:不要依赖 $slots 在 setup 中手动渲染
虽然可以通过 useSlots() 获取插槽函数,但在递归场景中:手动调用 slots.default?.() 会丢失响应性上下文,且无法自然绑定子节点作用域。更稳妥的方式是坚持模板内声明式透传 —— 即上面示例中用 <template></template> 包裹并重传 <slot></slot>。这样既保持 Vue 的响应式更新机制,又让作用域插槽逻辑清晰可维护。


















