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

Vue3 Composition API中如何实现v-for下SFC组件的依次渲染

问题描述

我创建了两个文件来演示遇到的问题:

Parent.vue

<script setup>
import { ref } from "vue";
import TypeWriter from "./TypeWriter.vue";

const strings = ref([
    "first_string",
    "second_string",
    "third_string"
]);
</script>

<template>
    <div>
        <div v-for="string in strings">
            <TypeWriter :string="string" />
        </div>
    </div>
</template>

TypeWriter.vue

<script setup>
import { ref, onMounted } from "vue";

const props = defineProps({
    string: String
});

const messageWrapper = ref(null);

function printMessage() {
    const target = messageWrapper.value;
    const message = props.string;
    
    // 逐字打印消息到 div.typewriter-message 的代码
}

onMounted(() => {
    printMessage(messageWrapper.value);
});
</script>

<template>
    <div class="typewriter-message" ref="messageWrapper"></div>
</template>

目前的问题是:TypeWriter组件需要时间逐字渲染内容,但v-for会一次性生成所有组件,导致三个组件同时开始渲染。我希望实现逐个渲染——下一个组件在上一个完全渲染完成后才开始执行打字效果。

我曾尝试把strings定义为带回调的对象,也试过async/await组合,但都没成功,可能是对Vue组件渲染机制理解不够。


实现思路与解决方案

核心思路

通过父组件控制子组件的启动时机:

  • 子组件完成打字后,向父组件发送完成通知
  • 父组件维护一个允许启动的索引阈值,只有组件索引小于等于该阈值时,才触发打字逻辑
  • 收到子组件的完成通知后,递增阈值,启动下一个组件的打字效果

具体实现

1. 修改 TypeWriter.vue,添加启动控制与完成事件

<script setup>
import { ref, watch } from "vue";

const props = defineProps({
    string: String,
    // 新增:控制是否开始打字
    start: {
        type: Boolean,
        default: false
    }
});

// 新增:定义完成事件,通知父组件
const emit = defineEmits(['complete']);

const messageWrapper = ref(null);

async function printMessage() {
    if (!props.start) return;
    
    const target = messageWrapper.value;
    const message = props.string;
    target.textContent = '';

    // 替换成你实际的逐字打印逻辑
    for (let char of message) {
        target.textContent += char;
        await new Promise(resolve => setTimeout(resolve, 150));
    }

    // 打字完成后通知父组件
    emit('complete');
}

// 监听start状态变化,触发打字
watch(() => props.start, (newVal) => {
    if (newVal) printMessage();
});
</script>

<template>
    <div class="typewriter-message" ref="messageWrapper"></div>
</template>

2. 修改 Parent.vue,控制组件启动节奏

<script setup>
import { ref, onMounted } from "vue";
import TypeWriter from "./TypeWriter.vue";

const strings = ref([
    "first_string",
    "second_string",
    "third_string"
]);

// 当前允许启动的组件最大索引
const currentActiveIndex = ref(-1);

// 处理子组件完成事件,启动下一个
const handleTypeComplete = () => {
    currentActiveIndex.value += 1;
};

// 页面加载时启动第一个组件
onMounted(() => {
    currentActiveIndex.value = 0;
});
</script>

<template>
    <div>
        <div v-for="(string, index) in strings" :key="index">
            <TypeWriter 
                :string="string" 
                :start="index <= currentActiveIndex" 
                @complete="handleTypeComplete" 
            />
        </div>
    </div>
</template>

简化方案(适合不需要保留全部组件DOM的场景)

如果不需要同时展示所有打字完成的内容,可只渲染当前正在打字的组件,完成后切换内容:

<script setup>
import { ref, onMounted } from "vue";
import TypeWriter from "./TypeWriter.vue";

const strings = ref([
    "first_string",
    "second_string",
    "third_string"
]);

const currentStringIndex = ref(0);

const handleTypeComplete = () => {
    if (currentStringIndex.value < strings.value.length - 1) {
        currentStringIndex.value += 1;
    }
};
</script>

<template>
    <div>
        <TypeWriter 
            :string="strings[currentStringIndex]" 
            :start="true"
            @complete="handleTypeComplete" 
        />
    </div>
</template>

关键说明

  • 子组件通过start props控制启动时机,避免了挂载即执行的问题
  • 父组件利用响应式索引,自动触发下一个组件的启动状态
  • 整个流程通过事件通知+响应式状态更新实现,完全契合Vue的渲染机制

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 01:17:52