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

Vue3中transition-group的tag设为v-timeline时无法渲染Vuetify组件

Vue3+Vuetify中transition-group绑定v-timeline失效的问题解析

问题描述

在Vue3项目中,尝试通过transition-group的tag属性指定Vuetify的v-timeline组件,为时间线内的卡片添加GSAP动画,但Vue无法正确识别并渲染v-timeline组件,导致时间线样式和功能异常。

主组件代码

<script setup lang="ts">
import gsap from 'gsap'
import { useTranslate } from '@/@core/composable/useTranslate'
import TimeLineItem from './TimeLineItem.vue'

interface ITimeLine {
  icon: string
  title: string
  description: string
  color: string
}
const timeLines = ref<ITimeLine[]>([
  {
    title: 'PadvishInstall',
    description: 'timeline-welcome',
    icon: 'material-symbols:looks-one-outline',
    color: 'warning',
  },
  {
    title: 'InsertingToken',
    description: 'timeline-step1',
    icon: 'ic:outline-looks-two',
    color: 'error',
  },
  {
    title: 'ContactInfo',
    description: 'timeline-step2',
    icon: 'ph:number-square-three-bold',
    color: 'info',
  },
])

const { translate } = useTranslate()

const beforeEnter = (el: Element) => {
  const he = el as HTMLElement
  he.style.opacity = '0'
  he.style.transform = 'translateX(100px)'
}

const enter = (el: Element, done: () => void) => {
  gsap.to(el, {
    opacity: 1,
    x: 0,
    duration: 0.8,
    onComplete: done,
    delay: Number((el as HTMLElement).dataset.index) * 0.5,
  })
}
</script>

<template>
  <VCard class="text-center" variant="text" title="card title">
    <VCardText>
      <transition-group
        align="start"
        justify="center"
        truncate-line="both"
        :density="$vuetify.display.smAndDown ? 'compact' : 'default'"
        appears
        @before-enter="beforeEnter"
        @enter="enter"
        tag="v-timeline"
      >
        <TimeLineItem
          v-for="(item, index) in timeLines"
          :key="index"
          :data-index="index"
          :title="translate(item.title)"
          :description="translate(item.description)"
          :icon="item.icon"
          :color="item.color"
        />
      </transition-group>
    </VCardText>
  </VCard>
</template>

TimeLineItem组件代码

<script setup lang="ts">
interface Props {
  icon: string
  title: string
  description: string
  color: string
}

const props = defineProps<Props>()
</script>

<template>
  <VTimelineItem size="x-small" fill-dot>
    <template #icon>
      <div class="v-timeline-avatar-wrapper rounded-circle">
        <VAvatar size="small">
          <VIcon size="100" :icon="icon" :color="color" />
        </VAvatar>
      </div>
    </template>
    <VCard>
      <VCardText>
        <div class="d-flex justify-space-between">
          <h6 class="text-base font-weight-semibold mb-1 me-3">
            {{ title }}
          </h6>
        </div>
        <p class="mb-1">
          {{ description }}
        </p>
      </VCardText>
    </VCard>
  </VTimelineItem>
</template>
<style lang="scss" scoped>
.v-timeline-avatar-wrapper {
  background-color: rgb(var(--v-theme-background));
}
</style>

原因解析

这是transition-group的功能限制,并非Vue3的Bug。

transition-group的tag属性仅支持原生HTML标签(如div、ul、li等),不能直接指定Vue组件(包括Vuetify的组件)。当设置tag="v-timeline"时,Vue会创建一个名为<v-timeline>的原生DOM元素,而非渲染Vuetify的VTimeline组件,因此无法加载组件对应的样式和逻辑,导致渲染异常。

解决方案

方案1:调整组件嵌套结构

将v-timeline作为父容器,内部放置transition-group(tag设为原生标签如div),既保留v-timeline的正常渲染,又能通过transition-group管理子元素的动画:

<template>
  <VCard class="text-center" variant="text" title="card title">
    <VCardText>
      <!-- 直接渲染v-timeline组件 -->
      <v-timeline
        align="start"
        justify="center"
        truncate-line="both"
        :density="$vuetify.display.smAndDown ? 'compact' : 'default'"
      >
        <!-- transition-group使用原生div作为容器 -->
        <transition-group
          tag="div"
          appears
          @before-enter="beforeEnter"
          @enter="enter"
        >
          <TimeLineItem
            v-for="(item, index) in timeLines"
            :key="item.title" <!-- 改用唯一标识作为key,避免index导致的动画异常 -->
            :data-index="index"
            :title="translate(item.title)"
            :description="translate(item.description)"
            :icon="item.icon"
            :color="item.color"
          />
        </transition-group>
      </v-timeline>
    </VCardText>
  </VCard>
</template>

方案2:手动触发GSAP动画

如果不需要transition-group的动态列表更新动画,可在组件挂载时直接通过GSAP遍历元素触发动画:

  1. 给TimeLineItem根元素添加标识class:
<template>
  <VTimelineItem class="timeline-item" size="x-small" fill-dot>
    <!-- ... 原有内容 -->
  </VTimelineItem>
</template>
  1. 在主组件的onMounted钩子中手动执行动画:
<script setup lang="ts">
// ... 原有代码
import { onMounted } from 'vue'

onMounted(() => {
  const timelineItems = document.querySelectorAll('.timeline-item')
  timelineItems.forEach((el, index) => {
    gsap.from(el, {
      opacity: 0,
      x: 100,
      duration: 0.8,
      delay: index * 0.5
    })
  })
})
</script>

内容的提问来源于stack exchange,提问作者amir tbi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 04:05:15