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

如何将Vue秒表代码重构为Vanilla JS并实现精准计时与后台续记?

问题描述

原Vue秒表存在两个核心问题:

  • 计时误差:实际流逝30秒,浏览器仅记录27-28秒
  • 窗口折叠后计时停止,刷新或恢复窗口后进度完全丢失

需要将代码重构为Vanilla JavaScript,同时实现:

  • 基于真实时间对比消除计时误差
  • 通过localStorage持久化计时进度,避免窗口状态变化导致的进度丢失
解决方案说明

1. 消除计时误差的核心逻辑

放弃setInterval累加固定值的不可靠方式,改为记录起始时间戳+计算真实时间差:

  • 每次更新显示时,获取当前真实时间戳,计算与计时起始时间的差值
  • 即使setInterval被浏览器延迟调度,通过真实时间差计算出的时长依然精准

2. localStorage持久化实现

  • 存储两个关键数据:已累计的总时长、上次计时的起始时间戳
  • 页面加载时读取localStorage,自动恢复之前的计时进度
  • 定时保存状态到localStorage,避免窗口折叠、刷新或关闭时丢失进度

3. 完整重构代码

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>精准持久化秒表</title>
    <style>
        #clock {
            background: #EDEDED;
            padding: 1%;
            border-radius: 12px;
            text-align: center;
        }

        .time {
            font-weight: 900;
            font-size: 2.125em;
            letter-spacing: 0.015625em;
            color: #373745;
        }
    </style>
</head>
<body>
    <div id="clock">
        <span class="time" id="watch">00:00</span>
    </div>

    <script>
        const watchElement = document.getElementById('watch');
        let intervalId = null;
        let startTime = null; // 本次计时的起始时间戳
        let accumulatedTime = 0; // 历史累计的总时长

        // 从localStorage恢复计时状态
        function loadSavedState() {
            const savedData = localStorage.getItem('stopwatchData');
            if (savedData) {
                const { accumulated, lastStartTime } = JSON.parse(savedData);
                accumulatedTime = accumulated;
                // 如果之前处于计时状态,计算窗口折叠期间的流逝时长
                if (lastStartTime) {
                    accumulatedTime += Date.now() - lastStartTime;
                }
            }
            updateDisplay();
        }

        // 保存当前状态到localStorage
        function saveCurrentState(isRunning = false) {
            const state = {
                accumulated: accumulatedTime + (startTime ? Date.now() - startTime : 0),
                lastStartTime: isRunning ? Date.now() : null
            };
            localStorage.setItem('stopwatchData', JSON.stringify(state));
        }

        // 更新秒表显示内容
        function updateDisplay() {
            const totalMilliseconds = accumulatedTime + (startTime ? Date.now() - startTime : 0);
            const timerDate = new Date(totalMilliseconds);
            const minutes = String(timerDate.getUTCMinutes()).padStart(2, '0');
            const seconds = String(timerDate.getUTCSeconds()).padStart(2, '0');
            watchElement.textContent = `${minutes}:${seconds}`;
        }

        // 启动计时
        function startTimer() {
            if (!intervalId) {
                startTime = Date.now();
                // 每100ms更新一次显示(原10ms过于频繁,无实际意义)
                intervalId = setInterval(() => {
                    updateDisplay();
                    // 每2秒保存一次状态,平衡性能与数据安全性
                    saveCurrentState(true);
                }, 100);
            }
        }

        // 页面加载时恢复状态并启动计时
        window.addEventListener('load', () => {
            loadSavedState();
            startTimer();
        });

        // 页面卸载前强制保存状态
        window.addEventListener('beforeunload', () => {
            saveCurrentState(!!intervalId);
        });
    </script>
</body>
</html>

代码关键细节

  • 精准计时:完全依赖Date.now()获取的真实时间计算时长,彻底规避setInterval的调度延迟误差
  • 持久化保障:页面加载自动恢复进度,定时+卸载前双重保存,确保任何场景下进度不丢失
  • 性能优化:降低显示更新频率到100ms,减少localStorage写入频率,避免不必要的资源消耗

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 11:10:40