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

Vue3 Composition API下表单渲染时如何正确显示加载器?

Vue3 Composition API:Tab切换时表单渲染期间显示加载器的解决方案

你当前的代码中,nextTick()仅等待Vue完成当前DOM更新队列(即切换currentTab后显示对应的容器),但FormKit的表单渲染(尤其是包含大量字段时)是在DOM结构更新后进行的内部渲染过程,耗时3-4秒,导致loading状态过早被关闭,用户看不到加载效果。

以下是几种可行的解决方案:

方案一:利用FormKit的ready事件(推荐)

FormKit提供了@ready事件,当表单完成初始化和渲染后会触发,这是追踪表单渲染完成最准确的方式。

修改模板:

<template>
  <div>
    <p v-if="loading">Loading...</p>
    <TabView @tabChange="handleTabChange">
      <TabPanel v-for="(tab, index) in tabs" :key="tab.id">
        <template #header>{{ tab.label }}</template>
        <div v-if="currentTab === index">
          <FormKit 
            :data="tab.formData" 
            @ready="() => onFormReady(tab.id)"
          />
        </div>
      </TabPanel>
    </TabView>
  </div>
</template>

修改脚本:

<script setup lang="ts">
import { ref, nextTick } from 'vue';
import TabView from './components/TabView.vue';
import TabPanel from './components/TabPanel.vue';
import FormKit from './components/FormKit.vue';

const tabs = ref([
  { id: 1, label: 'Tab 1', formData: { /* 表单数据 */ } },
  { id: 2, label: 'Tab 2', formData: { /* 表单数据 */ } },
]);

const loading = ref(false);
const currentTab = ref(0);

const handleTabChange = (index: number) => {
  loading.value = true;
  currentTab.value = index;
};

const onFormReady = (tabId: number) => {
  // 仅关闭当前激活标签页的加载状态
  const activeTab = tabs.value[currentTab.value];
  if (activeTab.id === tabId) {
    nextTick().then(() => {
      loading.value = false;
    });
  }
};
</script>

方案二:监听DOM元素判断渲染完成(无组件事件时使用)

如果FormKit没有提供ready事件,可以通过监听表单容器的DOM元素,检查表单是否渲染完成:

修改模板:

<template>
  <div>
    <p v-if="loading">Loading...</p>
    <TabView @tabChange="handleTabChange">
      <TabPanel v-for="(tab, index) in tabs" :key="index">
        <template #header>{{ tab.label }}</template>
        <div 
          v-if="currentTab === index" 
          ref="formContainers"
        >
          <FormKit :data="tab.formData" />
        </div>
      </TabPanel>
    </TabView>
  </div>
</template>

修改脚本:

<script setup lang="ts">
import { ref, nextTick } from 'vue';
import TabView from './components/TabView.vue';
import TabPanel from './components/TabPanel.vue';
import FormKit from './components/FormKit.vue';

const tabs = ref([
  { label: 'Tab 1', formData: { /* 表单数据 */ } },
  { label: 'Tab 2', formData: { /* 表单数据 */ } },
]);

const loading = ref(false);
const currentTab = ref(0);
const formContainers = ref<HTMLElement[]>([]);

const handleTabChange = async (index: number) => {
  loading.value = true;
  currentTab.value = index;
  
  await nextTick(); // 等待容器DOM生成
  await waitForFormRender();
  loading.value = false;
};

const waitForFormRender = () => {
  return new Promise((resolve) => {
    const checkComplete = () => {
      const container = formContainers.value[currentTab.value];
      if (!container) return resolve(null);
      
      // 根据FormKit实际生成的DOM结构调整选择器,比如检查是否存在表单字段
      const hasRenderedFields = container.querySelectorAll('.formkit-field').length > 0;
      if (hasRenderedFields) {
        nextTick().then(resolve);
      } else {
        requestAnimationFrame(checkComplete);
      }
    };
    requestAnimationFrame(checkComplete);
  });
};
</script>

方案三:兜底延迟方案(应急使用)

如果以上方法都无法实现,可以根据表单实际渲染时间设置固定延迟(不推荐,无法适配不同环境的渲染速度):

const handleTabChange = async (index: number) => {
  loading.value = true;
  currentTab.value = index;
  
  // 根据实际渲染时间调整延迟时长
  await new Promise(resolve => setTimeout(resolve, 3500));
  loading.value = false;
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 03:10:58