You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue.js修改含插槽的子组件数组时出现插槽内容显示异常的问题

Vue.js修改含插槽的子组件数组时出现插槽内容显示异常的问题

兄弟,我太懂你这种槽点了——明明组件标题都对得上,结果插槽内容却乱跳,肯定是Vue的虚拟DOM复用机制在搞鬼!

问题根源

你直接操作slots.default()返回的VNode数组时踩了个坑:这些VNode是和它们最初的组件实例绑定死的。Vue的diff算法会根据VNode的标识复用组件实例,当你把第一个VNode移到数组末尾时,Vue以为这还是原来的那个组件,所以不会更新它的插槽内容,导致插槽还停留在原来的上下文里,就出现了标题对、内容错的诡异情况。

解决方案:把插槽内容结构化,让Vue能正确追踪

我们不能直接拿VNode来挪位置,得先把每个子项的组件类型、props、插槽内容拆出来存成结构化对象,再给每个项加个唯一key,让Vue知道哪个是哪个,别乱复用。

1. 修改脚本部分,重新处理插槽内容

把原来直接赋值childComponents.value = slots.default()改成结构化处理:

<script setup>
import { useSlots, ref, onMounted } from 'vue'

const slots = useSlots()
const childComponents = ref([])
const multiItemCarouselContainer = ref(null)
let translateAmount = 0

onMounted(() => {
  // 把插槽元素转换成结构化对象,保存每个项的关键信息
  childComponents.value = slots.default().map((child, idx) => ({
    // 加个唯一ID,别用index当key!
    id: `carousel-item-${Date.now()}-${idx}`,
    type: child.type,
    props: child.props || {},
    // 保存插槽内容
    slotContent: child.children
  }))
  // 这里要在onMounted里计算,确保childComponents已经初始化
  translateAmount = 100 / (childComponents.value.length + 1)
})

function transitionNextFinished() {
  childComponents.value.shift()
  multiItemCarouselContainer.value.id = null
  multiItemCarouselContainer.value.style.transition = 'none'
  multiItemCarouselContainer.value.style.transform = 'none'
  multiItemCarouselContainer.value.removeEventListener('transitionend', transitionNextFinished)
}

function transitionNext() {
  const firstChild = childComponents.value[0]
  childComponents.value.push(firstChild)
  multiItemCarouselContainer.value.id = 'transitionNextElement'
  multiItemCarouselContainer.value.style.transition = 'transform .6s ease-in-out'
  multiItemCarouselContainer.value.style.transform = `translateX(-${translateAmount}%)`
  multiItemCarouselContainer.value.addEventListener('transitionend', transitionNextFinished)
}
</script>

2. 修改模板,用结构化对象渲染组件和插槽

把原来直接渲染VNode改成渲染我们的结构化对象,同时传递插槽内容:

<div class="container-fluid px-0 multi-item-carousel">
  <div class="d-inline-flex multi-item-carousel-inner" ref="multiItemCarouselContainer">
    <div class="me-3" v-for="item in childComponents" :key="item.id">
      <component :is="item.type" v-bind="item.props">
        <!-- 这里渲染保存的插槽内容 -->
        {{ item.slotContent }}
      </component>
    </div>
  </div>
</div>

为什么这样能解决问题?

  • 我们把每个子项的信息拆成了独立对象,Vue能通过唯一的id来识别每个项,不会再随便复用组件实例。
  • 插槽内容被我们单独保存并传递给组件,每次渲染都会用正确的内容,不会再被旧的组件实例上下文影响。

这样改完之后,你再点击按钮切换,标题和插槽内容就会一起正确移动啦!

备注:内容来源于stack exchange,提问作者Roland Deschain

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 17:02:59