运行时提示options.onChange?.(current.value);出错,页面无法展示

来源:5-24 -自定义hooks-useCountDown 实现倒计时逻辑-03

weixin_慕丝6100415

2023-02-13

老师,您 好,控制台打印的出错信息如下:
图片描述
useCoutndown.ts文件如下:
import { ref, computed } from “vue”;
import { rAF, cancelRAF } from “@/utils/raf”;

type CurrentTime = {
days: number;
hours: number;
minutes: number;
seconds: number;
milliseconds: number;
total: number;
};

type UseCountdownOptions = {
time: number;
milliseconds?: boolean;
onChange?: (current: CurrentTime) => void;
onFinish?: () => void;
};

const SECOND = 1000;
const MINUTE = 60 * 1000;
const HOUR = 60 * MINUTE;
const DAY = 24 * HOUR;
const parseTime = (time: number) => {
const days = Math.floor(time / DAY);
const hours = Math.floor((time % DAY) / HOUR);
const minutes = Math.floor((time % HOUR) / MINUTE);
const seconds = Math.floor((time % minutes) / SECOND);
const milliseconds = Math.floor(time % SECOND);

return {
days,
hours,
minutes,
seconds,
milliseconds,
total: time
};
};

const isSameSecond = (time1: number, time2: number) => {
return Math.floor(time1 / SECOND) === Math.floor(time2 / SECOND);
};

export function useCountdown(options: UseCountdownOptions) {
let rafId: number;
// 倒计时结束时间
let endTime: number;
let counting: boolean;
const remain = ref(options.time);
const current = computed(() => parseTime(remain.value));
const start = () => {
if (!counting) {
endTime = Date.now() + remain.value;
counting = true;
tick();
}
};
const tick = () => {
if (options.milliseconds) {
microTick();
} else {
macroTick();
}
};

const macroTick = () => {
rafId = rAF(() => {
if (counting) {
// 当时剩余的倒计时时间
const remainRemain = getCurrentRemain();
if (!isSameSecond(remainRemain, remain.value) || remainRemain === 0) {
setRemain(remainRemain);
}
if (remain.value > 0) {
macroTick();
}
}
});
};

const microTick = () => {
rafId = rAF(() => {
if (counting) {
const remainRemain = getCurrentRemain();
setRemain(remainRemain);

    if (remain.value > 0) {
      microTick();
    }
  }
});

};

const getCurrentRemain = () => Math.max(endTime - Date.now(), 0);

const setRemain = (value: number) => {
remain.value = value;
options.onChange?.(current.value);
if (value === 0) {
pause();
options.onFinish?.();
}
};

const pause = () => {
counting = false;
cancelRAF(rafId);
};

const reset = (totalTime = options.time) => {
pause();
remain.value = totalTime;
};
return {
start,
pause,
reset,
current
};
}


countDown.vue文件内容如下:

{{ padStart(current.hours) }} : {{ padStart(current.minutes) }} : {{ padStart(current.seconds) }}
{{ data.goods.name }}
{{ data.goods.price }} {{ data.goods.oldPrice }}
写回答

1回答

one_pieces

2023-02-13

同学你好,看截图报错你好像用了 webpack?而且 app.js 是 js 文件,直接引用 ts 肯定不行……同学可以按照前面的章节创建项目哈~

0
4
one_pieces
回复
weixin_慕丝6100415
首先 current 是一个 computed 计算属性,它是通过 remain 这个值算出来的,而 remain 在不断地在 requestAnimationFrame 里改变值,所以 current 也会跟着变。然后 options.onChange?.(current.value);,这里看到有个 onChange 后面跟了 ?.,这个语法是 ES2020 的可选链操作符,表示前面的属性或者值可能为 null 或 undefined,如果是 null 或 undefined 就返回 undefined,这样就不会导致引用为 null 或 undefined 时读取报错了。所以没有传 onChange 时,就不会执行,也就没有错误了。
2023-02-14
共4条回复

Vue3+Pinia+Vite+TS 还原高性能外卖APP项目

Vue3+Pinia+Vite+TS 还原高性能外卖APP项目

392 学习 · 251 问题

查看课程