Vue 3 HeadlessUI TransitionRoot中as="template"使用及数字显示问题
问题描述
我使用Vue 3、TailwindCSS和HeadlessUI实现老虎机样式动画,点击「Click to transition」按钮时,绿色方块已能实现顶部滑入、底部滑出的效果。resetIsShowing()函数负责随机处理1-10的连续数组,现在想让每次循环时绿色方块显示10个随机数中的一个。于是重构了显示方块的主div,添加列表渲染:
<div v-for="number in numbers" :key="number.id" class="h-full w-full rounded-md bg-green-500 shadow-lg"> {{ number }} </div>
但添加后出现以下错误:
The current component
is rendering a "template".return (<TransitionChild />)
However we need to passthrough the following props:
- ref
如果将TransitionRoot中的as="template"改为as="ref",又会丢失下滑动画效果。如何设置才能让数字在每次循环时显示在方块上?
原完整代码:
<template> <div class="flex flex-col items-center py-16"> <div class="h-72 w-72"> <div class="flex justify-center items-center w-full h-full p-4 text-9xl rounded-md shadow-lg border-solid border-2 border-sky-500"> <TransitionRoot appear :show="isShowing" as="template" enter="transition ease-in-out duration-300 transform" enter-from="-translate-y-full opacity-0" enter-to="translate-y-0 opacity-100" leave="transition ease-in-out duration-300 transform" leave-from="translate-y-0 opacity-100" leave-to="translate-y-full opacity-0" > <div v-for="number in numbers" :key="number.id" class="h-full w-full rounded-md bg-green-500 shadow-lg"> {{ number }} </div> </TransitionRoot> </div> </div> <button @click="resetIsShowing" class="mt-8 flex transform items-center rounded-full bg-black bg-opacity-20 px-3 py-2 text-sm font-medium text-white transition hover:scale-105 hover:bg-opacity-30 focus:outline-none active:bg-opacity-40" > <svg viewBox="0 0 20 20" fill="none" class="h-5 w-5 opacity-70"> <path d="M14.9497 14.9498C12.2161 17.6835 7.78392 17.6835 5.05025 14.9498C2.31658 12.2162 2.31658 7.784 5.05025 5.05033C7.78392 2.31666 12.2161 2.31666 14.9497 5.05033C15.5333 5.63385 15.9922 6.29475 16.3266 7M16.9497 2L17 7H16.3266M12 7L16.3266 7" stroke="currentColor" stroke-width="1.5" /> </svg> <span class="ml-3">Click to transition</span> </button> </div> </template> <script setup> import { ref } from 'vue' import { TransitionRoot } from '@headlessui/vue' const isShowing = ref(true) const numbers = ref([]) const numberArray = ref(Array.from({length: 10}, (e, i)=> i + 1)) const sleep = (milliseconds) => { return new Promise(resolve => setTimeout(resolve, milliseconds)) } async function resetIsShowing() { for (let i = 0; i < numberArray.value.length; i++) { const index = Math.floor(Math.random() * numberArray.value.length) await sleep(800) isShowing.value = false setTimeout(() => { isShowing.value = true }, 500) numbers.value = numberArray.value.filter(r => r === (index + 1)) } } </script>
解决方案
问题根源
HeadlessUI的TransitionRoot使用as="template"时,内部只能存在单个根元素。添加v-for后会生成多个div,导致Transition无法正确传递ref属性,触发错误。
修复步骤
- 移除不必要的
v-for:每次仅需显示一个随机数,直接用单个div绑定当前随机值即可。 - 简化数据结构:无需维护
numbers数组,改用单个ref存储当前显示的数字。 - 调整动画时序:将原
setTimeout替换为await sleep,保证动画与数字更新的时序一致。
修改后的完整代码
<template> <div class="flex flex-col items-center py-16"> <div class="h-72 w-72"> <div class="flex justify-center items-center w-full h-full p-4 text-9xl rounded-md shadow-lg border-solid border-2 border-sky-500"> <TransitionRoot appear :show="isShowing" as="template" enter="transition ease-in-out duration-300 transform" enter-from="-translate-y-full opacity-0" enter-to="translate-y-0 opacity-100" leave="transition ease-in-out duration-300 transform" leave-from="translate-y-0 opacity-100" leave-to="translate-y-full opacity-0" > <!-- 单个div显示当前随机数,无需v-for --> <div class="h-full w-full rounded-md bg-green-500 shadow-lg flex items-center justify-center"> {{ currentNumber }} </div> </TransitionRoot> </div> </div> <button @click="resetIsShowing" class="mt-8 flex transform items-center rounded-full bg-black bg-opacity-20 px-3 py-2 text-sm font-medium text-white transition hover:scale-105 hover:bg-opacity-30 focus:outline-none active:bg-opacity-40" > <svg viewBox="0 0 20 20" fill="none" class="h-5 w-5 opacity-70"> <path d="M14.9497 14.9498C12.2161 17.6835 7.78392 17.6835 5.05025 14.9498C2.31658 12.2162 2.31658 7.784 5.05025 5.05033C7.78392 2.31666 12.2161 2.31666 14.9497 5.05033C15.5333 5.63385 15.9922 6.29475 16.3266 7M16.9497 2L17 7H16.3266M12 7L16.3266 7" stroke="currentColor" stroke-width="1.5" /> </svg> <span class="ml-3">Click to transition</span> </button> </div> </template> <script setup> import { ref } from 'vue' import { TransitionRoot } from '@headlessui/vue' const isShowing = ref(true) const currentNumber = ref(1) // 单个ref存储当前显示的数字 const numberArray = ref(Array.from({length: 10}, (e, i)=> i + 1)) const sleep = (milliseconds) => { return new Promise(resolve => setTimeout(resolve, milliseconds)) } async function resetIsShowing() { for (let i = 0; i < numberArray.value.length; i++) { // 先生成随机数字并更新 const randomNum = numberArray.value[Math.floor(Math.random() * numberArray.value.length)] currentNumber.value = randomNum await sleep(800) isShowing.value = false // 用await保证时序,避免异步混乱 await sleep(500) isShowing.value = true } } </script>
关键修改说明
- 用
currentNumber单个ref替代numbers数组,直接绑定到div显示,避免多元素触发Transition的ref传递错误。 - 将原
setTimeout改为await sleep(500),确保动画隐藏完成后再显示新内容,时序更稳定。 - 提前更新随机数字,保证动画显示时直接展示最新值,符合老虎机的视觉逻辑。
内容的提问来源于stack exchange,提问作者JS_is_awesome18
相关产品推荐
相关产品推荐

