当前位置:首页 > 技术 > 正文内容

Vue.js组件实现多种场景倒计时功能

访客 技术 2026年8月25日 1

在前端应用开发中,倒计时功能是一种非常常见的需求,例如短信验证码计时、活动截止计时或任务限定时间提醒等。Vue.js 提供了强大的响应式数据绑定和组件生命周期管理能力,使得实现各种倒计时逻辑变得直观且高效。

短信验证码60秒倒计时

短信验证码场景下的倒计时通常是点击按钮后开始60秒计时,期间按钮变为不可用状态并显示剩余时间,计时结束后按钮恢复可点击状态。

<template>
  <div>
    <button v-if="!isSmsTimerActive" @click="startSmsCountdown">获取验证码</button>
    <button v-else disabled>{{ remainingSmsSeconds }}s 后重试</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      remainingSmsSeconds: 60, // 初始倒计时秒数
      isSmsTimerActive: false, // 标识倒计时是否进行中
      smsIntervalId: null      // 定时器ID
    };
  },
  methods: {
    startSmsCountdown() {
      // 在实际应用中,这里会先发送验证码请求
      // 只有请求成功后才启动倒计时
      this.isSmsTimerActive = true;
      this.remainingSmsSeconds = 60; // 每次启动重置为60秒

      this.smsIntervalId = setInterval(() => {
        if (this.remainingSmsSeconds > 0) {
          this.remainingSmsSeconds--;
        } else {
          // 倒计时结束
          clearInterval(this.smsIntervalId);
          this.isSmsTimerActive = false;
          this.smsIntervalId = null;
          // 可在此处添加倒计时结束后的其他逻辑,如提示用户重发
        }
      }, 1000);
    },
    // 停止倒计时的方法,确保在组件销毁时清除定时器
    stopSmsCountdown() {
      if (this.smsIntervalId) {
        clearInterval(this.smsIntervalId);
        this.smsIntervalId = null;
      }
      this.isSmsTimerActive = false;
      this.remainingSmsSeconds = 60;
    }
  },
  // 在组件销毁前清除定时器,避免内存泄漏
  beforeUnmount() { // Vue 3 uses beforeUnmount, Vue 2 uses beforeDestroy
    this.stopSmsCountdown();
  }
};
</script>

固定时长倒计时(例如15分钟)

这种倒计时通常用于显示一个固定时间段的剩余时间,例如活动结束、订单支付时限等。通过计算总秒数并利用计算属性格式化显示。

<template>
  <div>
    <p>距离结束还剩:<span>{{ formattedMinutes }}:{{ formattedSeconds }}</span></p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      totalTimeInSeconds: 15 * 60, // 初始总时长,例如15分钟
      fixedDurationIntervalId: null
    };
  },
  computed: {
    // 格式化分钟数,确保两位显示
    formattedMinutes() {
      const minutes = Math.floor(this.totalTimeInSeconds / 60);
      return minutes < 10 ? '0' + minutes : String(minutes);
    },
    // 格式化秒数,确保两位显示
    formattedSeconds() {
      const seconds = this.totalTimeInSeconds % 60;
      return seconds < 10 ? '0' + seconds : String(seconds);
    }
  },
  mounted() {
    this.startFixedDurationCountdown();
  },
  methods: {
    startFixedDurationCountdown() {
      // 避免重复创建定时器
      if (this.fixedDurationIntervalId) return;

      this.fixedDurationIntervalId = setInterval(() => {
        if (this.totalTimeInSeconds > 0) {
          this.totalTimeInSeconds--;
        } else {
          // 倒计时结束
          clearInterval(this.fixedDurationIntervalId);
          this.fixedDurationIntervalId = null;
          console.log('固定时长倒计时已结束!');
          // 可在此处触发结束事件或执行其他操作
        }
      }, 1000);
    },
    // 停止固定时长倒计时
    stopFixedDurationCountdown() {
      if (this.fixedDurationIntervalId) {
        clearInterval(this.fixedDurationIntervalId);
        this.fixedDurationIntervalId = null;
      }
    }
  },
  beforeUnmount() { // Vue 3 uses beforeUnmount, Vue 2 uses beforeDestroy
    this.stopFixedDurationCountdown();
  }
};
</script>

支持页面跳转不重置、结束自动续期的倒计时(例如5分钟)

对于需要在页面跳转或刷新后保持倒计时状态,并且在倒计时结束后自动续期(例如,每次倒计时结束或用户首次进入页面都从5分钟开始)的场景,我们可以结合 localStorage 来实现。

<template>
  <div>
    <p>当前会话剩余:<span>{{ persistentFormattedMinutes }}:{{ persistentFormattedSeconds }}</span></p>
    <button @click="handlePageNavigation">前往其他页面</button>
  </div>
</template>

<script>
// 定义LocalStorage键名和倒计时的默认时长
const SESSION_END_TIMESTAMP_KEY = 'session_countdown_end_time';
const SESSION_DURATION_SECONDS = 5 * 60; // 5分钟

export default {
  data() {
    return {
      currentRemainingSessionSeconds: 0, // 当前剩余秒数
      sessionIntervalId: null,           // 定时器ID
    };
  },
  computed: {
    // 格式化分钟数
    persistentFormattedMinutes() {
      const minutes = Math.floor(this.currentRemainingSessionSeconds / 60);
      return minutes < 10 ? '0' + minutes : String(minutes);
    },
    // 格式化秒数
    persistentFormattedSeconds() {
      const seconds = this.currentRemainingSessionSeconds % 60;
      return seconds < 10 ? '0' + seconds : String(seconds);
    }
  },
  created() {
    this.initSessionCountdown();
  },
  methods: {
    initSessionCountdown() {
      let storedEndTime = localStorage.getItem(SESSION_END_TIMESTAMP_KEY);
      let currentTime = Date.now(); // 当前时间戳(毫秒)
      let calculatedEndTime;

      if (storedEndTime) {
        calculatedEndTime = parseInt(storedEndTime, 10);
        if (calculatedEndTime > currentTime) {
          // 如果存储的结束时间在未来,则继续倒计时
          this.currentRemainingSessionSeconds = Math.floor((calculatedEndTime - currentTime) / 1000);
        } else {
          // 如果存储的结束时间已过期,则重新开始并续期
          calculatedEndTime = currentTime + SESSION_DURATION_SECONDS * 1000;
          localStorage.setItem(SESSION_END_TIMESTAMP_KEY, calculatedEndTime);
          this.currentRemainingSessionSeconds = SESSION_DURATION_SECONDS;
          console.log('会话倒计时已过期,自动续期5分钟。');
        }
      } else {
        // 首次进入页面,没有存储的结束时间,则开始新的倒计时
        calculatedEndTime = currentTime + SESSION_DURATION_SECONDS * 1000;
        localStorage.setItem(SESSION_END_TIMESTAMP_KEY, calculatedEndTime);
        this.currentRemainingSessionSeconds = SESSION_DURATION_SECONDS;
        console.log('首次启动会话倒计时,设置为5分钟。');
      }

      this.startSessionTimerLoop();
    },
    startSessionTimerLoop() {
      if (this.sessionIntervalId) return; // 避免重复创建定时器

      this.sessionIntervalId = setInterval(() => {
        if (this.currentRemainingSessionSeconds > 0) {
          this.currentRemainingSessionSeconds--;
        } else {
          // 倒计时归零,自动续期
          let newEndTime = Date.now() + SESSION_DURATION_SECONDS * 1000;
          localStorage.setItem(SESSION_END_TIMESTAMP_KEY, newEndTime);
          this.currentRemainingSessionSeconds = SESSION_DURATION_SECONDS;
          console.log('会话倒计时再次续期5分钟!');
        }
      }, 1000);
    },
    // 停止会话倒计时
    stopSessionTimer() {
      if (this.sessionIntervalId) {
        clearInterval(this.sessionIntervalId);
        this.sessionIntervalId = null;
      }
    },
    // 模拟页面导航,确保倒计时状态被保留
    handlePageNavigation() {
      if (this.$router) {
        this.$router.push({ name: 'AnotherRoute' }); // 替换为你的路由名称
      } else {
        console.warn('Vue Router 未初始化。无法执行页面跳转。');
      }
    }
  },
  beforeUnmount() { // Vue 3 uses beforeUnmount, Vue 2 uses beforeDestroy
    this.stopSessionTimer();
  },
};
</script>

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

linux screen 用法详情 (nohup 的替代方案)

一、screen 是什么?能干嘛?screen 是一个终端复用器,可以:在一个 SSH 会话中开多个“虚拟终端”SSH 断线后,程序仍然在后台运行随时重新连接到原来的会话特别适合:nohup 的替代方案跑脚本 / 爬虫 / 训练模型运维、远程开发二、安装 screen# CentOS / Rocky / Almayum install -y screen# Debian / Ubuntuapt i...

PHPStan 有什么用?怎么用?

PHPStan 是一个 PHP 的静态分析工具,在不运行代码的情况下就能帮你发现潜在问题,比如:传错类型(把 string 传给接受 int 的函数)访问不存在的属性 / 方法null 没处理好永远不会执行到的代码数组 key/值类型不一致返回值不符合声明注释和真实类型不匹配它非常适合:想提升代码质量、减少线上 bug、统一团队风格的人(尤其是中大型项目)。一、PHPStan 有什么用(通俗点说)...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。