Vue组件传入新timeout props时倒计时不显示的修复方法
问题原因
三个核心问题导致倒计时显示异常:
- Vue的
watch默认仅在监听值发生变化时触发,组件首次通过v-if挂载时,timeoutprop已经完成赋值,不会触发监听回调,因此第一次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
相关产品推荐
相关产品推荐

