这是我参与更文挑战的第19天,活动详情查看: 更文挑战
在重构过程中,我们还有一个方向值得去折腾,那就是不断把引入的第三方组件一个个想办法移除。
今天突发奇想开始自己折腾做一个倒计时的组件,目标是想替换了 vue3-clock-countdown
。
我看了看这个 vue3-clock-countdown
组件的源代码,貌似也没那么难 (当然不是不尊重原作者!)。
注:我也想推荐原作者把事件相关功能加上,这样才显得完整。
显示效果
废话不多说,先看看显示效果:
- 显示「天-时-分-秒」:
- 显示「时-分-秒」:
- 显示「分-秒」:
制作的逻辑就是保持所有东西:简洁、简洁、简洁。
布局
这个布局我主要使用 NCard
:
具体代码:
<template>
<n-card :title="title" :bordered="false" :style="style">
<template #header-extra>
<n-button
text
style="font-size: 24px;"
@click="$emit('finish')"
>
<n-icon>
<times-circle-regular-icon />
</n-icon>
</n-button>
</template>
<n-grid x-gap="12" :cols="cols">
...
</n-grid>
</n-card>
</template>
复制代码
这样,基本把 header 部分解决了。
在显示「天-时-分-秒」部分,直接使用了「布局」组件,再根据 cols
来定显示几个 NGi
:
<n-grid x-gap="12" :cols="cols">
<n-gi v-if="cols == 4">
<n-card :title="formatTime(days)" size="large">天</n-card>
</n-gi>
<n-gi v-if="cols >= 3">
<n-card :title="formatTime(hours)" size="large">时</n-card>
</n-gi>
<n-gi>
<n-card :title="formatTime(minutes)" size="large">分</n-card>
</n-gi>
<n-gi>
<n-card :title="formatTime(seconds)" size="large">秒</n-card>
</n-gi>
</n-grid>
复制代码
具体是不是显示「天」和「时」,太简单了,都不好意思说了:
cols(): number {
if (this.days > 0) {
return 4;
}
if (this.hours > 0) {
return 3;
}
return 2;
},
复制代码
就四个布局,可以直接写出来,也不是什么不可以,一样的,这边完全可以用 v-for 来减少重复代码,然后用 v-if 来判断是否显示,但在 Vue 官网文档里,明确写了以下限制:
永远不要把 v-if 和 v-for 同时用在同一个元素上。
为了过滤一个列表中的项目 (比如 v-for=”user in users” v-if=”user.isActive”)。在这种情形下,请将 users 替换为一个计算属性 (比如 activeUsers),让其返回过滤后的列表。
逻辑处理
在逻辑部分,主要有三方面的数据需要考虑:
- 时间和倒计时逻辑
seconds(): number {
return Math.floor((this.currentTime / 1000) % 60);
},
minutes(): number {
return Math.floor((this.currentTime / 1000 / 60) % 60);
},
hours(): number {
return Math.floor((this.currentTime / (1000 * 60 * 60)) % 24);
},
days(): number {
return Math.floor(this.currentTime / (1000 * 60 * 60 * 24));
},
...
mounted() {
setTimeout(this.countdown, 1000);
},
methods: {
formatTime(value: number): string {
if (value < 10) {
return '0' + value;
}
return value.toString();
},
countdown () {
this.currentTime = Date.parse(this.deadline) - Date.parse(new Date());
if (this.currentTime > 0) {
setTimeout(this.countdown, this.speed);
} else {
this.$emit('finish');
}
}
}
复制代码
seconds、minutes、hours、days 这些数据时刻随着 currentTime 的变化而变化,所以放在了 computed 里;倒计时直接在组件加载时,可以自动进入倒计时执行,所以放在 mounted 里。
- 时间和标题
这个我参考原作者的方式,直接用的 Provide / Inject 方式,省了在其他地方赋值和引入。
setup() {
const deadline = inject('deadline');
const title = inject('title', '');
return {
deadline,
title,
};
},
复制代码
- props 和 emits
props: {
speed: {
type: Number,
default: 1000
},
height: Number,
},
emits: [
'finish',
],
复制代码
因为我想把一些属性,如高度 height 这个可以交给组件外部传入,增加可自定义多一种方式。
事件处理,是我们和原作者不一样的地方,真实的在点击退出或者倒计时到了,把事件传出去。
<n-button
text
style="font-size: 24px;"
@click="$emit('finish')"
>
复制代码
小结
好了,有了自定义组件,我们就可以移除 vue3-clock-countdown
组件。
后续可以不断优化自己开发的组件,也不断移除第三方组件,让自己的项目越来越可控。未完待续!
代码已同步到 github 上了:github.com/fanly/fanly…