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

Vue组件传入新timeout props时倒计时不显示的修复方法

问题原因

三个核心问题导致倒计时显示异常:

  • Vue的watch默认仅在监听值发生变化时触发,组件首次通过v-if挂载时,timeout prop已经完成赋值,不会触发监听回调,因此第一次3秒倒计时根本没有启动。
  • 每次调用countDown方法启动新倒计时前,没有清空上一次遗留的interval定时器,旧定时器的回调执行时会误清新启动的定时器,导致第三次9秒倒计时刚启动就被清除,无法正常显示。
  • 原倒计时逻辑没有在启动时立即给timer赋初始值,存在1秒的显示空白,且计时赋值顺序错误,会导致实际等待时长和页面显示的时长不匹配。
修复方案

1. 修改error-request组件逻辑

给timeout的watch添加immediate: true配置,保证组件首次挂载时就启动倒计时;每次启动新倒计时前先清除旧定时器,修正倒计时赋值逻辑,组件销毁时也清除定时器避免内存泄漏。
修复后的组件代码:

<template>
  <div>
    <h1>The next attempt to fetch data will be made in:</h1>
    <h2>{{ timer }}</h2>
  </div>
</template>

<script>
export default {
  props: ["timeout"],
  data() {
    return {
      timer: null,
      interval: null,
    };
  },
  methods: {
    countDown(sec) {
      // 提前清除旧定时器,避免多定时器冲突
      if (this.interval) {
        clearInterval(this.interval);
      }
      // 启动时立即赋值初始秒数,避免1秒空白
      this.timer = sec;
      this.interval = setInterval(() => {
        if (this.timer <= 0) {
          clearInterval(this.interval);
          return;
        }
        this.timer--;
      }, 1000);
    },
  },
  watch: {
    timeout: {
      handler(val) {
        this.countDown(val);
      },
      // 首次挂载立即触发,解决第一次倒计时不显示问题
      immediate: true
    }
  },
  beforeUnmount() {
    // 组件销毁清定时器,避免内存泄漏
    if (this.interval) {
      clearInterval(this.interval);
    }
  }
};
</script>

2. (可选优化)统一Vuex侧的计时逻辑

原逻辑中Vuex的等待和组件的倒计时是并行执行的,容易出现时间差,如果要完全保证显示时间和实际重试时间一致,可以把倒计时逻辑统一放到Vuex中,组件只负责展示数值即可。
修改后的Vuex代码:

import { createStore } from "vuex";

const wait = async (ms, onTick) => {
  return new Promise((resolve) => {
    let remaining = ms / 1000;
    const timer = setInterval(() => {
      remaining--;
      onTick(remaining);
      if (remaining <= 0) {
        clearInterval(timer);
        resolve();
      }
    }, 1000);
  });
};

export default createStore({
  state: {
    error: {
      isError: false,
      timerSec: null
    }
  },

  mutations: {
    setError(state, payload) {
      state.error = payload.error;
    }
  },

  actions: {
    async fetchProducts({ commit, dispatch }, attempt = 1) {
      try {
        const response = await fetch("https://fakestoreapi.com/products222");

        if (!response.ok) {
          throw new Error("Something went wrong");
        }
        // 请求成功重置错误状态
        commit("setError", {
          error: {
            isError: false,
            timerSec: null
          }
        });
      } catch (e) {
        console.log("Request:", attempt);
        const waitSec = attempt * 3;
        commit("setError", {
          error: {
            isError: true,
            timerSec: waitSec
          }
        });

        if (attempt >= 3) {
          return;
        }
        // 等待过程中每秒更新倒计时数值
        await wait(waitSec * 1000, (remaining) => {
          commit("setError", {
            error: {
              isError: true,
              timerSec: remaining
            }
          });
        });
        return dispatch("fetchProducts", attempt + 1);
      }
    }
  }
});

如果采用这种方案,error-request组件可以简化,不需要自己维护定时器逻辑,直接渲染传入的timeout prop即可。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 06:06:25