Nuxt2/Vue2中$slots判断重渲染时偶发失效问题求助
Vue2/Nuxt2中$slots判断重渲染失效的原因解析
问题场景
在Nuxt2/Vue2项目中,nav-pane-sub-header组件包含一个名为image的插槽,原本通过$slots.image条件渲染该插槽,首次渲染功能正常,但组件重渲染时该判断偶尔失效。将判断条件改为$scopedSlots.image || $slots.image后问题解决,以下是具体代码及原理分析:
父组件代码
<template> <div> <nav-pane> <template #subheader> <nav-pane-sub-header> <template #image> <avatar /> </template> </nav-pane-sub-header> </template> </nav-pane> </div> </template>
存在问题的组件代码
<template> <div> <div> <i v-if="icon" /> <slot v-else-if="$slots.image"/> </div> <slot /> </div> </template>
修复后的判断代码
<slot v-else-if="$scopedSlots.image || $slots.image" />
原理解析
在Vue2的插槽机制中,$slots和$scopedSlots的核心差异导致了这个问题:
- $slots:存储的是已渲染完成的VNode节点集合,只有当插槽内容被实际渲染后才会被填充。首次渲染时,Vue会同步解析插槽内容并写入
$slots,因此判断有效;但重渲染时,由于更新机制的异步性或嵌套插槽的层级影响,$slots的更新可能滞后于组件的条件判断,导致$slots.image暂时为undefined,判断失效。 - $scopedSlots:存储的是插槽的渲染函数,只要父组件通过
<template #xxx>语法传入了插槽定义(即使没有传递props),这个渲染函数就会存在,不受渲染时机或更新顺序的影响。
结合你的代码来看:父组件中使用<template #image>传入插槽,在Vue2中这种语法会被默认处理为作用域插槽,对应的插槽定义会被注册到$scopedSlots而非$slots。首次渲染时Vue做了兼容处理,让$slots也能读取到该插槽,但重渲染时这个兼容逻辑未触发,导致$slots.image为空,而$scopedSlots.image始终存在,因此添加$scopedSlots.image的判断就能覆盖这种场景,同时保留$slots.image是为了兼容非作用域插槽的直接传入场景。
内容的提问来源于stack exchange,提问作者Benjamin De Mot
相关产品推荐
相关产品推荐

