Vue 2.x嵌套使用同名具名插槽时响应式失效问题排查求助
问题根因
这是Vue 2.6.x版本插槽解析逻辑的已知bug,核心触发点为同名具名插槽嵌套场景下的作用域绑定错误:
Vue 2.x解析插槽时会按名称缓存插槽作用域实例,嵌套层级下如果出现同名的具名插槽,内部插槽的作用域会错误覆盖外层插槽的作用域引用,导致最外层传入的插槽内容的响应式依赖收集链路被打断,数据更新时无法触发插槽内容重渲染。
对应你的复现代码,两层content插槽嵌套时,Vue内部维护的$scopedSlots映射表出现作用域指向错误,最终Parent组件接收的默认插槽没有正确绑定根实例的counter响应式依赖,因此更新无响应。
可行规避方案
方案1:插槽名称差异化
给嵌套层级的同名插槽增加层级前缀避免重名,是改造成本最低的修复方案:
// 调整内层组件的插槽命名,避免重名 const WithContentSlotTwo = Vue.extend({ template: `<div><slot name="inner-content"/></div>`, }); const Parent = Vue.extend({ components: { WithContentSlotOne, WithContentSlotTwo }, template: ` <with-content-slot-one> <template #content> <with-content-slot-two> <template #inner-content> <slot /> </template> </with-content-slot-two> </template> </with-content-slot-one> `, });
方案2:优先使用默认插槽传递内容
如果是renderless组件场景,统一使用默认插槽传递内容即可规避该问题,默认插槽的作用域解析逻辑不会触发该bug:
// 所有renderless组件统一用默认插槽传递内容,支持透传slotProps const WithContentSlot = Vue.extend({ template: `<div><slot v-bind="slotProps"/></div>`, data() { return { slotProps: { /* 暴露的逻辑参数 */ } } } }); const Parent = Vue.extend({ components: { WithContentSlot }, template: ` <with-content-slot v-slot="outerProps"> <with-content-slot v-slot="innerProps"> <slot v-bind="{ ...outerProps, ...innerProps }"/> </with-content-slot> </with-content-slot> `, });
方案3:给插槽添加唯一key属性
如果无法修改组件的插槽命名,可以给嵌套的同名插槽手动添加唯一key,强制Vue为每个插槽创建独立的作用域实例:
const Parent = Vue.extend({ components: { WithContentSlotOne, WithContentSlotTwo }, template: ` <with-content-slot-one> <template #content key="outer-content"> <with-content-slot-two> <template #content key="inner-content"> <slot /> </template> </with-content-slot-two> </template> </with-content-slot-one> `, });
方案4:升级到Vue 2.7.x版本
该bug在Vue 2.7.0及以上版本已经被官方修复,升级版本后无需修改业务代码即可解决问题,2.7版本对Vue 2.6的代码兼容性极高,几乎没有破坏性变更。
内容的提问来源于stack exchange,提问作者Ábel Énekes
相关产品推荐
相关产品推荐

