这一版代码增加了生命周期管理(Start/Stop 机制),确保"用完即走,不留垃圾"。
javascript
// stores/timerStore.js
import { defineStore } from 'pinia';
import { ref } from 'vue';
export const useTimerStore = defineStore('timer', () => {
// --- State ---
const displayDuration = ref('00:00:00');
// 内部变量 (不具有响应性,节省性能)
let timerId = null;
let baseTime = 0;
let startDuration = 0;
let isRunning = false; // 标记是否正在运行
// --- Logic ---
// 纯净的更新函数:只做计算,不涉及副作用
const update = () => {
const now = Date.now();
const elapsed = now - baseTime;
const totalSeconds = startDuration + Math.floor(elapsed / 1000);
// 性能优化:使用整数运算代替字符串操作,减少 GC 压力(虽然微乎其微)
const h = Math.floor(totalSeconds / 3600);
const m = Math.floor((totalSeconds % 3600) / 60);
const s = totalSeconds % 60;
// 模板字符串在现代 JS 引擎中优化极好,无需过度担心内存
displayDuration.value =
`${h.toString().padStart(2, '0')}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`;
};
// 处理可见性变化的句柄
const handleVisibilityChange = () => {
// 只有在"运行中"且"页面可见"时,才进行强制校准
if (document.visibilityState === 'visible' && isRunning) {
update();
}
};
// --- Actions ---
// 1. 启动/同步时间
const syncServerTime = (serverDuration) => {
// 停止旧的(如果有)
stop();
startDuration = Number(serverDuration) || 0;
baseTime = Date.now();
isRunning = true;
// 立即更新一次 UI
update();
// 开启定时器
timerId = setInterval(update, 1000);
// 注册页面可见性监听(防御性编程:先移除再添加,防止重复添加)
if (typeof document !== 'undefined') {
document.removeEventListener('visibilitychange', handleVisibilityChange);
document.addEventListener('visibilitychange', handleVisibilityChange);
}
};
// 2. 停止/销毁 (这是生产环境优化的关键!)
// 当用户离开这个页面时,务必调用此方法清理资源
const stop = () => {
isRunning = false;
if (timerId) {
clearInterval(timerId);
timerId = null;
}
// 移除事件监听,做一个讲卫生的好程序
if (typeof document !== 'undefined') {
document.removeEventListener('visibilitychange', handleVisibilityChange);
}
};
return {
displayDuration,
syncServerTime,
stop
};
});
如何在组件中配合(防止内存泄漏的关键一步)
在页面组件卸载(Unmounted)时,必须 调用 store.stop()。这是防止"僵尸定时器"的最后一道防线。
vue
<script setup>
import { onUnmounted } from 'vue';
import { useTimerStore } from '@/stores/timerStore';
const timerStore = useTimerStore();
// ... 你的业务代码,调用 timerStore.syncServerTime(...) ...
// ⭐️ 生产环境最佳实践:
// 组件销毁时,停止计时器,释放 CPU 和 事件监听
onUnmounted(() => {
timerStore.stop();
});
</script>
性能 QA (针对你的担忧)
-
问:内存会不断增大吗?
- 答:不会。 JavaScript 引擎(V8)的垃圾回收非常智能。
displayDuration.value = '新字符串'会创建一个新字符串,旧的字符串失去引用后,会在极短时间内(毫秒级)被 GC 回收。这种程度的内存波动对于现代浏览器(动辄 4GB+ 内存)来说是沧海一粟。
- 答:不会。 JavaScript 引擎(V8)的垃圾回收非常智能。
-
问:页面放久了会卡吗?
- 答:不会。 因为
setInterval只是简单地执行加减法和字符串赋值,CPU 占用率几乎为 0。
- 答:不会。 因为
-
问:
visibilitychange会导致重复绑定吗?- 答:优化后的代码解决了这个问题。 我们在
syncServerTime里先remove再add,且在stop里彻底remove。这保证了无论用户怎么狂点刷新按钮,全局永远只有一个监听器在运行。
- 答:优化后的代码解决了这个问题。 我们在