在本人着手開發一個考試系統的過程中,出現了如下一個需求:制作一個倒計時的控件顯示在試卷頁面上。本文所記錄的就是這樣的一個過程。
前期工作
對於這個需求,自然我想到的是有沒有現成的組件可以直接使用(本着不重復發明輪子的原則)。於是我就在 GitHub 上找尋。確實找到了不少,但是與需求之間的差距還比較大。從零開始寫又不太現實(時間擺在那里,加之自己的前端也是剛學,還沒有從零開始手擼一個控件的能力),所以在已有組件的基礎上進行二次封裝便成了一個比較可行的方法(幾乎也是唯一解)。遂在 npm 上以 countdown 為關鍵詞搜索,最后找到了 Vue Awesome Countdown 這個組件。這個組件幾乎滿足了需求,只是它還沒有像樣的展示形式。所以針對它的二次封裝主要就是圍繞這個展開。
對於考試倒計時的組件,我希望它有兩個功能:在頁面上展示剩余時間、在考試結束時自動交卷。接下來的內容就圍繞這個展開。
時間顯示
要想在頁面上進行時間顯示。首先需要知道這個倒計時組件是如何保存時間的。翻閱文檔得知,保存在組件中的時間是以 timeObj
的形式進行存儲(timeObj
的完整格式見下)。對於需求來說,我們只需要其中的 timeObj.h
、timeObj.m
、timeObj.s
即可。
{
"endTime": 1542634411361,
"speed": 1000,
"leftTime": 97019,
"d": "0",
"h": "00",
"m": "01",
"s": "37",
"ms": "019",
"org": {
"d": 0.001134247685185185,
"h": 0.02722194444444444,
"m": 1.6333166666666665,
"s": 37.998999999999995,
"ms": 19
},
"ceil": {
"d": 1,
"h": 1,
"m": 2,
"s": 98
}
}
倒計時的時長,則可以通過后端傳過來的考試記錄信息進行推算,然后作為一個參數傳入,結合網站提供的示例,很快就寫出了以下代碼:
<template>
<div id="timer-view">
<p class="title">距離考試結束還有</p>
<countdown :end-time="new Date().getTime() + remainingTimes">
<div class="timer" slot="process" slot-scope="{ timeObj }">
{{ `${timeObj.h}:${timeObj.m}:${timeObj.s}` }}
</div>
</countdown>
</div>
</template>
<script>
export default {
name: 'timer',
props: {
remainingTimes: Number
}
}
</script>
<style scoped>
#timer-view {
margin: 15px;
border: solid 1px;
border-radius: 5px;
box-shadow: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04);
}
.title {
text-align: center;
}
.timer {
text-align: center;
font-family: Arial, Helvetica, sans-serif;
font-size: 50px;
color: red;
font-weight: bold;
}
</style>
計時結束事件
顯示的問題解決后,下面要處理的是計時結束后的自動交卷。官方文檔中提到了該組件有如下四個事件可以處理:
Event | Explain | Parameters |
---|---|---|
start | Functions executed at the beginning of countdown | vm |
process | Function executed when countdown is performed | vm |
stop | Function executed when countdown stops | vm |
finish | Function executed when countdown finished | vm |
不難看出,對於需求來說,可以響應它的 finish
事件完成這一功能。這里限於本人對 Vue 的事件響應了解還不是很透徹,這里采用了一個比較麻煩的手法處理該問題(將響應 finish
事件的函數作為參數傳入組件,然后對應的部分直接寫 @finish="傳入的參數"
)。之后對 Vue 的理解更加深入后會改回事件響應的那套模型。
<!--
計時器組件
Author: 劉忠燏 (seLiuZhongyu@outlook.com)
-->
<template>
<div id="timer-view">
<p class="title">距離考試結束還有</p>
<countdown :end-time="new Date().getTime() + remainingTimes" @finish="endCallback">
<div class="timer" slot="process" slot-scope="{ timeObj }">
{{ `${timeObj.h}:${timeObj.m}:${timeObj.s}` }}
</div>
</countdown>
</div>
</template>
<script>
export default {
name: 'timer',
props: {
remainingTimes: Number,
endCallback: Function
}
}
</script>
<style scoped>
#timer-view {
margin: 15px;
border: solid 1px;
border-radius: 5px;
box-shadow: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04);
}
.title {
text-align: center;
}
.timer {
text-align: center;
font-family: Arial, Helvetica, sans-serif;
font-size: 50px;
color: red;
font-weight: bold;
}
</style>
以上就是計時器的完整代碼,要使用它,只要傳入合適的參數給該組件即可:
<template>
<div>
<timer :remaining-times="remainingTimes" :end-callback="onFinished"></timer>
</div>
</template>
<script>
import Timer from '@/components/Timer'
export default {
name: 'paper-view',
components: {
'timer': Timer
},
methods: {
onFinished () {
// ...
},
},
computed: {
remainingTimes () {
// ...
}
}
}
</script>
<style>
</style>
總結
通過 這次的組件封裝,我對 Vue 的組件有了進一步的認識,也暴露出我在 Vue 的事件模型上了解得還不夠深入,在之后的工作中我還需要在這方面繼續深入學習。