Diff 算法解析插槽内容动态变化机制-父子组件 Patch 顺序详解指南

作者:袖梨 2026-05-23

Vue的Diff算法处理插槽更新时,关键在于父组件控制子组件的slots引用变更,触发子组件重新渲染并执行自身差异对比。

插槽内容何时被重新生成?

Diff 算法解析插槽内容动态变化机制-父子组件 Patch 顺序详解指南

作为函数式内容,插槽(如slots.default)在父组件render阶段被调用生成VNode。当父组件的响应式依赖发生变化时,包括v-if条件切换、v-for数据更新或作用域插槽props变动,都会触发父组件重新render并生成新的插槽VNode数组。

  1. 插槽VNode采用惰性构造机制:仅在父组件执行render时生成,不会提前缓存或跨patch复用
  2. 只要父组件传入的slots引用发生改变(即使内容相同),Vue即判定插槽需要更新
  3. 子组件无法直接判断插槽内容变化,仅通过浅比较识别slots对象引用变更

父子 Patch 顺序如何影响插槽更新?

插槽更新严格遵循父组件优先于子组件的处理顺序:

  1. 父组件patch到子组件VNode时,首先调用updateComponentPreRender更新子实例的propsslots
  2. 若slots引用变化,将标记子组件需要重新render(即使props未改变)
  3. 触发子组件render()生成新VNode后,才开始执行子组件内部的children差异对比
  4. 子组件的diff过程仅比较本次render输出的新旧VNode树,不会回溯比对历史插槽VNode

示例场景:父组件包含<Child><p>{{ msg }}</p></Child>模板时:

  1. 父组件render重新执行→调用新的slots.default函数→生成新的<p>...</p>VNode数组
  2. 父组件patch到ChildVNode→检测slots引用变更→触发子组件pre-render更新
  3. 子组件再次render→模板中的<slot></slot>展开为新VNode→进入子组件自身的patchChildren流程

带 key 的作用域插槽怎么 diff?

作用域插槽(如v-slot:item="{ data }")本身不含key,但当其位于v-for循环中时,父级会为每个循环项生成带key的slotVNode。

  1. 将插槽内容作为子组件slots的组成部分传入,key值保留在生成的VNode上
  2. 子组件render后,插槽展开形成一组带key的子节点
  3. 子组件内部执行patchKeyedChildren,利用key实现精准节点复用和位置调整
  4. 当列表中某项插槽内容变化时,父组件生成带相同key的新VNode→子组件diff识别为相同节点→仅更新props或文本内容

为什么不能在子组件 created 中读取插槽 DOM?

插槽内容的DOM生成完全由父组件render流程控制,与子组件生命周期无关:

  1. created阶段:父组件render尚未执行,slots可能为空或仅含默认内容,且无真实DOM
  2. mounted阶段:父组件已完成patch,子组件完成首次render,插槽内容已挂载至子组件DOM
  3. 如需访问插槽DOM,必须结合onMountednextTick确保渲染完成

Vue的插槽更新机制通过父子组件协同工作,确保动态内容的高效渲染和精确更新。

相关文章

精彩推荐