Vue插槽更新由父组件slots引用变更触发,子组件仅浅比较引用并重新render;插槽VNode惰性生成、不复用,且patch严格遵循父先子后时序;编译阶段将slot视为动态边界纳入Block Tree分块管理。

Vue 在 Patch 阶段处理插槽内容,核心逻辑是不直接 diff 插槽 VNode,而是由父组件决定 slots 引用是否变更,从而触发子组件重新 render。插槽本身不是独立节点,它没有自己的生命周期或 diff 流程,更新完全依赖父组件的响应式变化和父子 patch 时序。
插槽 VNode 是惰性生成、不复用的
插槽内容(如 slots.default)本质是函数,在父组件 render 执行时才被调用并返回 VNode 数组。这意味着:
- 每次父组件 re-render,都会重新执行插槽函数,生成全新的 VNode,哪怕内容看起来一样
- 这些 VNode 不会跨 patch 缓存或复用,也不会被提前构造
- 即使子组件实例没销毁,只要父传入的
slots对象引用变了(比如函数重定义),Vue 就判定插槽已更新
子组件只认 slots 引用,不比对内容
子组件内部无法感知“插槽内容有没有变”,它只做浅比较(shallowEqual)来判断 slots 对象是否为新引用:
- 若引用不同,子组件标记为需 re-render,即使 props 完全没变
- 子组件 render 后产出的新 VNode 树,才会进入它自己的 diff 流程——此时才比对本次渲染出的插槽内容与上一次 render 的结果
- 它不会回溯去比对“上次插槽函数生成的 VNode”,因为那些早已被丢弃
父先子后:严格的 patch 时序保障更新正确性
插槽更新必须遵循“父组件 patch 先完成 slots 更新,再触发子组件 patch”这一顺序:
- 父组件 patch 到子组件 VNode 节点时,先调用
updateComponentPreRender,更新子实例的props和slots - 若
slots引用变化,子组件被标记为 dirty,准备 re-render - 随后子组件执行
render(),基于新的slots生成 VNode;此时它的 diff 才开始作用于自身 children(含插槽渲染结果)
编译阶段已为插槽划定动态边界
Vue 3 编译器将 <slot> 视为动态节点边界,天然纳入 Block Tree 的分块管理:
- 每个插槽出口(
<slot name="xxx"></slot>)默认成为一个 block root - 父组件中影响某具名插槽的响应式变量变化,只会触发该插槽所在 block 的 patch,其余结构(其他插槽、静态布局)不受影响
- 配合
v-memo可进一步收窄刷新范围,例如<template #header v-memo="[user.id]">{{ user.name }}</template>,仅当user.id变时才重渲染 header 插槽

















