vue3计时器钩子函数,响应式失效,应该如何修改?

作者站长头像
站长
· 阅读数 14

封装了一个处理时间的钩子函数,想用在多个地方,一个是界面头部,一个某一界面。在界面头部响应式生效了,但在另一个界面使用时却不生效,使用了watch和computed都没监听到变化:(之前我的写法是每use一次都会创建定时器,那个时候响应式是正常的。后面我觉得定时器太多了就抽出来改成一个作为常量,就有了如上问题。)

界面头部使用:

<script lang="ts" setup>
import { useTime } from "@/hooks"
const { time } = useTime()
</script>

另一界面使用:

<script lang="ts" setup>
import { ref, watch } from "vue"
import { useTime } from "@/hooks"
const { time, timeStamp } = useTime()

watch(timeStamp, () => {
   console.log("timetime", timeStamp)
})

</script>

钩子函数代码如下,这应该如何解决呢?


useTime

import { onMounted, ref, computed, onBeforeUnmount } from "vue";
import { Timer , clearSysTimer} from "@/utils";
export const useTime = () => {
   const WEEK = ["日", "一", "二", "三", "四", "五", "六"];

   // 分割时间
   const timeSpliter = (time?: string | number): Array<number> => {
      const current: Date = time ? new Date(time) : new Date();
      const YYYY = current.getFullYear();
      const MM = current.getMonth() + 1;
      const DD = current.getDate();
      const hh = current.getHours();
      const mm = current.getMinutes();
      const ss = current.getSeconds();
      const week = current.getDay();
      return [YYYY, MM, DD, hh, mm, ss, week];
   };

   // 填充零
   const zeroPadder = (value: number | string): string => {
      return value > 9 ? value.toString() : "0" + value;
   };

   // 时间格式化
   const timeFormater = (format: string, time?: string | number): string => {
      const [YYYY, MM, DD, hh, mm, ss, week] = timeSpliter(time);
      const result = format
         .replace("YYYY", `${zeroPadder(YYYY)}`)
         .replace("MM", `${zeroPadder(MM)}`)
         .replace("DD", `${zeroPadder(DD)}`)
         .replace("hh", `${zeroPadder(hh)}`)
         .replace("mm", `${zeroPadder(mm)}`)
         .replace("ss", `${zeroPadder(ss)}`)
         .replace("WEEK", `星期${WEEK[week]}`);
      return result;
   };

   // 动态
   const timeStamp = ref(); // 时间戳
   const time = computed(() =>timeFormater("YYYY年MM月DD日 WEEK hh:mm:ss", timeStamp.value));
   const refreshTime = () => {
      timeStamp.value = new Date().getTime();
   };
   // 自增
   const timerAdder = () => {
      timeStamp.value += 1000;
   };

   onMounted(() => {
      clearSysTimer()
      refreshTime();
      if(!Timer.localTimer){
         Timer.localTimer= setInterval(() => {
               timerAdder();
         }, 1000);
      }
      if(!Timer.sysTimer){
         Timer.sysTimer= setInterval(() => {
               refreshTime();
         }, 1000 * 60 * 5);
      }
   });

   onBeforeUnmount(() => {
      clearSysTimer()
   });

   return {
      timeStamp,
      timeSpliter,
      timeFormater,
      zeroPadder,
      time,
      refreshTime,
   };
};

@/utils/index

export const Timer:{[index:string]:any}={
    sysTimer:null,
    localTimer:null
}

export const clearSysTimer=()=>{
    clearInterval(Timer.sysTime)
    clearInterval(Timer.localTime)
}
回复
1个回答
avatar
test
2024-07-16

问题在 useTime 的这个 clearSysTimer

onMounted(() => {
    clearSysTimer()
    refreshTime();
    if(!Timer.localTimer){
       Timer.localTimer= setInterval(() => {
             timerAdder();
       }, 1000);
    }
    if(!Timer.sysTimer){
       Timer.sysTimer= setInterval(() => {
             refreshTime();
       }, 1000 * 60 * 5);
    }
});

因为 Timer.localTimer 是全局唯一的,所以当 useTime 被第二次使用时,就会把第一个 useTime 中的定时器给清掉

这里如果你是想所有地方使用 useTime 都能获取到相同的一个倒计时,那么这里通过 发布订阅 来设计是比较合适的

  • useTime 只会创建一个定时器
  • 使用 useTime 需要注册一个订阅函数
  • 在定时器的逻辑中触发 所有订阅 即可
回复
likes
适合作为回答的
  • 经过验证的有效解决办法
  • 自己的经验指引,对解决问题有帮助
  • 遵循 Markdown 语法排版,代码语义正确
不该作为回答的
  • 询问内容细节或回复楼层
  • 与题目无关的内容
  • “赞”“顶”“同问”“看手册”“解决了没”等毫无意义的内容