精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

研究大佬寫的倒計時組件(Vue),學到了不少東西

開發 前端
入職的第一個需求是跟著一位前端大佬一起完成的一個活動項目。由于是一起開發,喜歡閱讀源碼的我,當然不會放過閱讀大佬的代碼的機會。

[[412028]]

一、前言

入職的第一個需求是跟著一位前端大佬一起完成的一個活動項目。

由于是一起開發,喜歡閱讀源碼的我,當然不會放過閱讀大佬的代碼的機會。

因為我的頁面中需要使用到倒計時功能,發現大佬的已經寫了個現成的倒計時組件,于是直接就拿過來用了。

傳個參數就實現了功能的感覺真是太棒了。項目完成后,就膜拜了一下大佬的倒計時組件的代碼。真是讓我學到了不少。列舉如下:

  1. 計時器為什么要用setTimeout而不用setInterval
  2. 為什么不直接將剩余時間-1。
  3. 如何將所需要的時間返回出去(有可能我只需要分鐘和秒數,那就只返回分鐘和秒數,也有可能我全都要)。
  4. 不確定接口返回的是剩余時間還是截止日期,該怎么同時兼容這兩種情況。
  5. 不確定接口返回的時間是秒還是毫秒單位。

好了,你可能不太理解這些問題,但是沒關系,看完下面的解釋,相信你會豁然開朗。

二、開始手操

1. 先創建一個vue組件

  1. <template> 
  2.   <div class="_base-count-down"
  3.   </div> 
  4. </template> 
  5. <script> 
  6.  
  7. export default { 
  8.   data: () => ({ 
  9.     
  10.   }), 
  11.   props: { 
  12.      
  13.   }, 
  14. }; 
  15. </script> 
  16. <style lang='scss' scoped> 
  17.  
  18. </style> 

2. 實現基本的倒計時組件

接下來,假設接口獲得的是一個剩余時間。

將剩余時間time傳入這個倒計時組件,由于time可能是秒為單位的,也有可能是毫秒為單位的,所以我們需要在傳入time的是有也傳入一個isMilliSecond來告訴倒計時組件這個time是毫秒還是秒為單位的。如下代碼中的props所示。

  1. <template> 
  2.   <div class="_base-count-down"
  3.   </div> 
  4. </template> 
  5. <script> 
  6.  
  7. export default { 
  8.   data: () => ({ 
  9.   }), 
  10.   props: { 
  11.     time: { 
  12.       type: [Number, String], 
  13.       default: 0 
  14.     }, 
  15.     isMilliSecond: { 
  16.       type: Boolean, 
  17.       defaultfalse 
  18.     } 
  19.   }, 
  20.   computed: { 
  21.     duration() { 
  22.       const time = this.isMiniSecond ? Math.round(+this.time / 1000) : Math.round(+this.time); 
  23.       return time
  24.     } 
  25.   }, 
  26. }; 
  27. </script> 
  28. <style lang='scss' scoped> 
  29.  
  30. </style> 

computed中的duration是將time進行轉化的結果,不管time是毫秒還是秒,都轉化為秒 不知道你注意到了沒有:+this.time。為什么要在前面加個‘+’號。這點很值得我們學習,因為接口返回的一串數字有時候是字符串的形式,有時候是數字的形式(不能過分相信后端同學,必須自己做好防范)。所以通過前面加個‘+’號 通通轉化為數字。現在的duration就是轉化后的time啦!

我們獲得duration之后就可以開始倒計時了

  1. <template> 
  2.   <div class="_base-count-down"
  3.   </div> 
  4. </template> 
  5. <script> 
  6.  
  7. export default { 
  8.   data: () => ({ 
  9.   }), 
  10.   props: { 
  11.     time: { 
  12.       type: [Number, String], 
  13.       default: 0 
  14.     }, 
  15.     isMilliSecond: { 
  16.       type: Boolean, 
  17.       defaultfalse 
  18.     } 
  19.   }, 
  20.   computed: { 
  21.     duration() { 
  22.       const time = this.isMiniSecond ? Math.round(+this.time / 1000) : Math.round(+this.time); 
  23.       return time
  24.     } 
  25.   }, 
  26.   // 新增代碼: 
  27.   mounted() { 
  28.     this.countDown(); 
  29.   }, 
  30.   methods: { 
  31.     countDown() { 
  32.       this.getTime(this.duration); 
  33.     }, 
  34.   } 
  35. }; 
  36. </script> 
  37. <style lang='scss' scoped> 
  38.  
  39. </style> 

在這里創建了一個countDown方法,表示開始倒計時的意思,已進入頁面就開始執行countdown方法。

countDown方法調用了getTime方法,getTime需要傳入duration這個參數,也就是我們獲得的剩余時間。

現在來實現一下這個方法。

  1. <template> 
  2.   <div class="_base-count-down"
  3.     還剩{{day}}天{{hours}}:{{mins}}:{{seconds}} 
  4.   </div> 
  5. </template> 
  6. <script> 
  7.  
  8. export default { 
  9.   data: () => ({ 
  10.     days: '0'
  11.     hours: '00'
  12.     mins: '00'
  13.     seconds: '00'
  14.     timer: null
  15.   }), 
  16.   props: { 
  17.     time: { 
  18.       type: [Number, String], 
  19.       default: 0 
  20.     }, 
  21.     isMilliSecond: { 
  22.       type: Boolean, 
  23.       defaultfalse 
  24.     } 
  25.   }, 
  26.   computed: { 
  27.     duration() { 
  28.       const time = this.isMiniSecond ? Math.round(+this.time / 1000) : Math.round(+this.time); 
  29.       return time
  30.     } 
  31.   }, 
  32.   mounted() { 
  33.     this.countDown(); 
  34.   }, 
  35.   methods: { 
  36.     countDown() { 
  37.       this.getTime(this.duration); 
  38.     }, 
  39.     // 新增代碼: 
  40.     getTime(duration) { 
  41.       this.timer && clearTimeout(this.timer); 
  42.       if (duration < 0) { 
  43.         return
  44.       } 
  45.       const { dd, hh, mm, ss } = this.durationFormatter(duration); 
  46.       this.days = dd || 0; 
  47.       this.hours = hh || 0; 
  48.       this.mins = mm || 0; 
  49.       this.seconds = ss || 0; 
  50.       this.timer = setTimeout(() => { 
  51.         this.getTime(duration - 1); 
  52.       }, 1000); 
  53.     } 
  54.   } 
  55. }; 
  56. </script> 
  57. <style lang='scss' scoped> 
  58.  
  59. </style> 

可以看到,getTime的目的就是獲得 days,hours,mins,seconds,然后顯示到html上,并且通過定時器實時來刷新days,hours,mins,seconds這個幾個值。從而實現了倒計時。很簡單,有木有?

durationFormatter是一個將duration轉化成天數,小時,分鐘,秒數的方法,很簡單,可以看下它的具體實現。

  1. durationFormatter(time) { 
  2.   if (!timereturn { ss: 0 }; 
  3.   let t = time
  4.   const ss = t % 60; 
  5.   t = (t - ss) / 60; 
  6.   if (t < 1) return { ss }; 
  7.   const mm = t % 60; 
  8.   t = (t - mm) / 60; 
  9.   if (t < 1) return { mm, ss }; 
  10.   const hh = t % 24; 
  11.   t = (t - hh) / 24; 
  12.   if (t < 1) return { hh, mm, ss }; 
  13.   const dd = t; 
  14.   return { dd, hh, mm, ss }; 
  15. }, 

好了,問題開始來了!!

3. 為什么要用setTimeout來模擬setInterval的行為?

這里用setInerval不是更方便嗎?

  1. setTimeout(function(){··· }, n); // n毫秒后執行function 
  1. setInterval(function(){··· }, n); // 每隔n毫秒執行一次function 

可以看看setInterval有什么缺點:

再次強調,定時器指定的時間間隔,表示的是何時將定時器的代碼添加到消息隊列,而不是何時執行代碼。所以真正何時執行代碼的時間是不能保證的,取決于何時被主線程的事件循環取到,并執行。

  1. setInterval(function, N)   
  2. //即:每隔N秒把function事件推到消息隊列中 
圖片

上圖可見,setInterval每隔100ms往隊列中添加一個事件;100ms后,添加T1定時器代碼至隊列中,主線程中還有任務在執行,所以等待,some event執行結束后執行T1定時器代碼;又過了100ms,T2定時器被添加到隊列中,主線程還在執行T1代碼,所以等待;又過了100ms,理論上又要往隊列里推一個定時器代碼,但由于此時T2還在隊列中,所以T3不會被添加,結果就是此時被跳過;這里我們可以看到,T1定時器執行結束后馬上執行了T2代碼,所以并沒有達到定時器的效果。

綜上所述,setInterval有兩個缺點:

  1. 使用setInterval時,某些間隔會被跳過;
  2. 可能多個定時器會連續執行;

可以這么理解:每個setTimeout產生的任務會直接push到任務隊列中;而setInterval在每次把任務push到任務隊列前,都要進行一下判斷(看上次的任務是否仍在隊列中)。

因而我們一般用setTimeout模擬setInterval,來規避掉上面的缺點。

4. 為什么要clearTimeout(this.timer)

第二問:為什么要有this.timer && clearTimeout(this.timer);這一句?

假設一個場景:

如圖所示,在倒計時的父組件中,有兩個按鈕,點擊活動一就會傳入活動一的剩余時間,點擊活動二,就會傳入活動二的時間。

圖片

如果此時倒計時組件正在做活動一的倒計時,然后點擊活動二,就要會馬上傳入新的time,這個時候就需要重新計時。當然,這里并不會重新計時,因為組件的mounted只會執行一次。也就是說this.countDown();只會執行一次,也就是說this.getTime(this.duration);只會執行一次,因此duration還是活動一的時間,怎么辦呢?watch派上用場了。

我們來監聽duration,如果發現duration變化,說明新的時間time傳入組件,這時就要重新調用this.countDown()。

代碼如下:

  1. <template> 
  2.   <div class="_base-count-down"
  3.     還剩{{day}}天{{hours}}:{{mins}}:{{seconds}} 
  4.   </div> 
  5. </template> 
  6. <script> 
  7.  
  8. export default { 
  9.   data: () => ({ 
  10.     days: '0'
  11.     hours: '00'
  12.     mins: '00'
  13.     seconds: '00'
  14.     timer: null
  15.   }), 
  16.   props: { 
  17.     time: { 
  18.       type: [Number, String], 
  19.       default: 0 
  20.     }, 
  21.     isMilliSecond: { 
  22.       type: Boolean, 
  23.       defaultfalse 
  24.     } 
  25.   }, 
  26.   computed: { 
  27.     duration() { 
  28.       const time = this.isMiniSecond ? Math.round(+this.time / 1000) : Math.round(+this.time); 
  29.       return time
  30.     } 
  31.   }, 
  32.   mounted() { 
  33.     this.countDown(); 
  34.   }, 
  35.   // 新增代碼: 
  36.   watch: { 
  37.     duration() { 
  38.       this.countDown(); 
  39.     } 
  40.   }, 
  41.   methods: { 
  42.     countDown() { 
  43.       this.getTime(this.duration); 
  44.     }, 
  45.     durationFormatter(){...} 
  46.     getTime(duration) { 
  47.       this.timer && clearTimeout(this.timer); 
  48.       if (duration < 0) { 
  49.         return
  50.       } 
  51.       const { dd, hh, mm, ss } = this.durationFormatter(duration); 
  52.       this.days = dd || 0; 
  53.       this.hours = hh || 0; 
  54.       this.mins = mm || 0; 
  55.       this.seconds = ss || 0; 
  56.       this.timer = setTimeout(() => { 
  57.         this.getTime(duration - 1); 
  58.       }, 1000); 
  59.     } 
  60.   } 
  61. }; 
  62. </script> 
  63. <style lang='scss' scoped> 
  64.  
  65. </style> 

好了,但是并沒有解釋上面提出的那個問題:為什么要有this.timer && clearTimeout(this.timer);這一句?

這樣,假設現在頁面顯示的是活動一的時間,這時,執行到setTimeout,在一秒后就會把setTimeout里的回調函數放到任務隊列中,注意是一秒后哦!這時,然而,在這一秒的開頭,我們點擊了活動二按鈕,這時候的活動二的時間就會傳入倒計時組件中,然后觸發countDown(),也就調用this.getTime(this.duration);,然后執行到setTimeout,也會一秒后把回調函數放到任務隊列中。

這時,任務隊列中就會有兩個setTimeout的回調函數了。等待一秒過去,兩個回調函數相繼執行,我們就會看到頁面上的時間一下子背減了2,實際上是很快速地進行了兩遍減1的操作。

這就是為什么要添加上this.timer && clearTimeout(this.timer);這一句的原因了。就是要把上一個setTimeout清除掉。

5. 使用 diffTime

當你認為這是一個完美的組件的時候,你想把這個組件用到項目上,假設你也確實用了,而且還上線了,確發現出現了個大問題:當頁面打開的時候,倒計時開始了,時間是 還剩1天12:25:25,然后有人給你發微信,你馬上切換到微信,回復消息后切回瀏覽器,發現倒計時時間卻還是還剩1天12:25:25。你慌了:你寫的代碼出現bug了!

這是怎么回事?

出于節能的考慮, 部分瀏覽器在進入后臺時(或者失去焦點時), 會將 setTimeout 等定時任務暫停 待用戶回到瀏覽器時, 才會重新激活定時任務

說是暫停, 其實應該說是延遲, 1s 的任務延遲到 2s, 2s 的延遲到 5s, 實際情況因瀏覽器而異。

原來如此,看來不能每次都只是減1這么簡單了(畢竟你把瀏覽器切到后臺之后setTimeout就冷卻了,等幾秒后切回,然后執行setTimeout,只是減了一秒而已)。

所以我們需要改寫一下getTime方法。

  1. <template> 
  2.   <div class="_base-count-down"
  3.     還剩{{day}}天{{hours}}:{{mins}}:{{seconds}} 
  4.   </div> 
  5. </template> 
  6. <script> 
  7.  
  8. export default { 
  9.   data: () => ({ 
  10.     days: '0'
  11.     hours: '00'
  12.     mins: '00'
  13.     seconds: '00'
  14.     timer: null
  15.     curTime: 0,// 新增代碼: 
  16.   }), 
  17.   props: { 
  18.     time: { 
  19.       type: [Number, String], 
  20.       default: 0 
  21.     }, 
  22.     isMilliSecond: { 
  23.       type: Boolean, 
  24.       defaultfalse 
  25.     } 
  26.   }, 
  27.   computed: { 
  28.     duration() { 
  29.       const time = this.isMiniSecond ? Math.round(+this.time / 1000) : Math.round(+this.time); 
  30.       return time
  31.     } 
  32.   }, 
  33.   mounted() { 
  34.     this.countDown(); 
  35.   }, 
  36.    
  37.   watch: { 
  38.     duration() { 
  39.       this.countDown(); 
  40.     } 
  41.   }, 
  42.   methods: { 
  43.     countDown() { 
  44.       // 新增代碼: 
  45.       this.curTime = Date.now(); 
  46.       this.getTime(this.duration); 
  47.     }, 
  48.     durationFormatter(){...} 
  49.     getTime(duration) { 
  50.       this.timer && clearTimeout(this.timer); 
  51.       if (duration < 0) { 
  52.         return
  53.       } 
  54.       const { dd, hh, mm, ss } = this.durationFormatter(duration); 
  55.       this.days = dd || 0; 
  56.       this.hours = hh || 0; 
  57.       this.mins = mm || 0; 
  58.       this.seconds = ss || 0; 
  59.       this.timer = setTimeout(() => { 
  60.         // 新增代碼: 
  61.         const now = Date.now(); 
  62.         const diffTime = Math.floor((now - this.curTime) / 1000); 
  63.         this.curTime = now; 
  64.         this.getTime(duration - diffTime); 
  65.       }, 1000); 
  66.     } 
  67.   } 
  68. }; 
  69. </script> 
  70. <style lang='scss' scoped> 
  71.  
  72. </style> 

可以看到,我們在三個位置添加了新的代碼。

首先在data了添加了curTime這個變量,然后在執行countDown的時候給curTime賦值Date.now(),也就是當前的時刻,也就是顯示在頁面上的那個時刻。

然后看修改的第三處代碼。可以看到是將-1改成了-diffTime。

now 是 setTimeout的回調函數執行的時候的那個時刻。

因而 diffTime 則 表示 當前這個setTimeout的回調函數執行的時刻距離上 頁面上的剩余時間上一次變化的時間段。其實也就是 當前這個setTimeout的回調函數執行的時刻距離上 一個setTimeout的回調函數執行的時刻時間段。

可能你還是不太能理解diffTime。舉個例子:

你打開了這個倒計時頁面,于是執行了countDown,也就是說要執行getTime這個方法了。也就是會馬上執行下列的代碼。

  1. this.days = dd || 0; 
  2. this.hours = hh || 0; 
  3. this.mins = mm || 0; 
  4. this.seconds = ss || 0; 

執行完這些代碼頁面上就會出現剩余時間。

而this.curTime = Date.now(); 就記錄下了此刻的時間點。

然后一秒后執行setTimeout里的回調函數:

const now = Date.now(); 記錄當前這個setTimeout的回調函數執行的時間點。

const diffTime = Math.floor((now - this.curTime) / 1000); 記錄當前這個setTimeout的回調函數執行的時間點距離頁面上開始 渲染 剩余時間的 這一段時間。其實此時的diffTime就是=1。

然后this.curTime = now; 將curTime的值變成當前這個setTimeout的回調函數執行的時間點。

this.getTime(duration - diffTime); 其實就是this.getTime(duration - 1);

然后又執行getTime,就會重新執行下面的代碼,有渲染了新的剩余時間。

  1. this.days = dd || 0; 
  2. this.hours = hh || 0; 
  3. this.mins = mm || 0; 
  4. this.seconds = ss || 0; 

然后一秒后又要執行setTmieout的回調函數,在這一秒還沒結束的時候,我們將瀏覽器切到后臺,此時setTimeout冷卻了。等5秒后再切回。于是setTmieout的回調函數才得以執行。

這時const now = Date.now(); 記錄當前這個setTimeout的回調函數執行的時間點。

而curTime是上一個setTimeout的回調函數執行的時間。

所以const diffTime = Math.floor((now - this.curTime) / 1000);實際上,diffTime的值就是5秒。

因而this.getTime(duration - diffTime); 其實就是this.getTime(duration - 5);

這樣就完美解決了因為瀏覽器切到后臺,導致剩余時間不變的問題。

6. 添加新功能:可以傳入到期時間。

之前是只能傳入剩余時間的,現在希望也支持傳入到期時間。

只需要改動一下duration就好了。

  1. computed: { 
  2.   duration() { 
  3.     if (this.end) { 
  4.       let end = String(this.end).length >= 13 ? +this.end : +this.end * 1000; 
  5.       end -= Date.now(); 
  6.       return end
  7.     } 
  8.     const time = this.isMiniSecond ? Math.round(+this.time / 1000) : Math.round(+this.time); 
  9.     return time
  10.   } 
  11. }, 

判斷傳入的end的長度是否大于13來判斷是秒還是毫秒。輕松!

7. 添加新功能:可以選擇要顯示的內容,例如只顯示秒,或者只顯示小時。

只需要改動一下html:

  1. <template> 
  2.   <div class="_base-count-down no-rtl"
  3.     <div class="content"
  4.       <slot v-bind="{ 
  5.         d: days, h: hours, m: mins, s: seconds, 
  6.         hh: `00${hours}`.slice(-2), 
  7.         mm: `00${mins}`.slice(-2), 
  8.         ss: `00${seconds}`.slice(-2), 
  9.       }"></slot> 
  10.     </div> 
  11.   </div> 
  12. </template> 

很巧妙有沒有,只需要用插槽,就把倒計時組件,也就是把子組件的值傳遞給父組件了。

看看父組件是怎么使用這個組件的。

  1. <base-counter v-slot="timeObj" :time="countDown"
  2.   <div class="count-down"
  3.     <div class="icon"></div> 
  4.     {{timeObj.d}}天{{timeObj.hh}}小時{{timeObj.mm}}分鐘{{timeObj.ss}}秒 
  5.   </div> 
  6. </base-counter> 

看,如此巧妙又簡單。

發現00${hours}.slice(-2) 這種寫法也很值得學習。以前在獲得到分鐘的時候,要手動判斷獲得的分鐘是兩位數還是一位數,如果是一位數的話就要在前面手動補上0。就像下面的代碼:

  1. var StartMinute = startDate.getMinutes().toString().length >= 2 ? startDate.getMinutes() : '0' + startDate.getHours(); 

而00${hours}.slice(-2) 則不用判斷,先補上0再說,然后再從后面往前截取兩位。

[[412030]]

到此。

一個完美的倒計時組件就完成了。

三、學習總結

  1. 明白了setInterval的缺點以及用setTimeout代替setInterval。
  2. 學到了“+”,操作,不管三七二十一,將接口得到的長串數字轉化為數字保平安。
  3. 利用clearTimeout來清除掉之前的計時器,以防止造成影響。
  4. 學會使用v-slot來子傳父傳值
  5. 學會一個倒計時組件,為了以后方便cv操作。把組件完整代碼貼上:
  1. <template> 
  2.   <div class="_base-count-down no-rtl"
  3.     <div class="content"
  4.       <slot v-bind="{ 
  5.         d: days, h: hours, m: mins, s: seconds, 
  6.         hh: `00${hours}`.slice(-2), 
  7.         mm: `00${mins}`.slice(-2), 
  8.         ss: `00${seconds}`.slice(-2), 
  9.       }"></slot> 
  10.     </div> 
  11.   </div> 
  12. </template> 
  13. <script> 
  14. /* eslint-disable object-curly-newline */ 
  15.  
  16. export default { 
  17.   data: () => ({ 
  18.     days: '0'
  19.     hours: '00'
  20.     mins: '00'
  21.     seconds: '00'
  22.     timer: null
  23.     curTime: 0 
  24.   }), 
  25.   props: { 
  26.     time: { 
  27.       type: [Number, String], 
  28.       default: 0 
  29.     }, 
  30.     refreshCounter: { 
  31.       type: [Number, String], 
  32.       default: 0 
  33.     }, 
  34.     end: { 
  35.       type: [Number, String], 
  36.       default: 0 
  37.     }, 
  38.     isMiniSecond: { 
  39.       type: Boolean, 
  40.       defaultfalse 
  41.     } 
  42.   }, 
  43.   computed: { 
  44.     duration() { 
  45.       if (this.end) { 
  46.         let end = String(this.end).length >= 13 ? +this.end : +this.end * 1000; 
  47.         end -= Date.now(); 
  48.         return end
  49.       } 
  50.       const time = this.isMiniSecond ? Math.round(+this.time / 1000) : Math.round(+this.time); 
  51.       return time
  52.     } 
  53.   }, 
  54.   mounted() { 
  55.     this.countDown(); 
  56.   }, 
  57.   watch: { 
  58.     duration() { 
  59.       this.countDown(); 
  60.     }, 
  61.     refreshCounter() { 
  62.       this.countDown(); 
  63.     } 
  64.   }, 
  65.   methods: { 
  66.     durationFormatter(time) { 
  67.       if (!timereturn { ss: 0 }; 
  68.       let t = time
  69.       const ss = t % 60; 
  70.       t = (t - ss) / 60; 
  71.       if (t < 1) return { ss }; 
  72.       const mm = t % 60; 
  73.       t = (t - mm) / 60; 
  74.       if (t < 1) return { mm, ss }; 
  75.       const hh = t % 24; 
  76.       t = (t - hh) / 24; 
  77.       if (t < 1) return { hh, mm, ss }; 
  78.       const dd = t; 
  79.       return { dd, hh, mm, ss }; 
  80.     }, 
  81.     countDown() { 
  82.       // eslint-disable-next-line no-unused-expressions 
  83.       this.curTime = Date.now(); 
  84.       this.getTime(this.duration); 
  85.     }, 
  86.     getTime(time) { 
  87.       // eslint-disable-next-line no-unused-expressions 
  88.       this.timer && clearTimeout(this.timer); 
  89.       if (time < 0) { 
  90.         return
  91.       } 
  92.       // eslint-disable-next-line object-curly-newline 
  93.       const { dd, hh, mm, ss } = this.durationFormatter(time); 
  94.       this.days = dd || 0; 
  95.       // this.hours = `00${hh || ''}`.slice(-2); 
  96.       // this.mins = `00${mm || ''}`.slice(-2); 
  97.       // this.seconds = `00${ss || ''}`.slice(-2); 
  98.       this.hours = hh || 0; 
  99.       this.mins = mm || 0; 
  100.       this.seconds = ss || 0; 
  101.       this.timer = setTimeout(() => { 
  102.         const now = Date.now(); 
  103.         const diffTime = Math.floor((now - this.curTime) / 1000); 
  104.         const step = diffTime > 1 ? diffTime : 1; // 頁面退到后臺的時候不會計時,對比時間差,大于1s的重置倒計時 
  105.         this.curTime = now; 
  106.         this.getTime(time - step); 
  107.       }, 1000); 
  108.     } 
  109.   } 
  110. }; 
  111. </script> 
  112. <style lang='scss' scoped> 
  113. @import '~@assets/css/common.scss'
  114.  
  115. ._base-count-down { 
  116.   color: #fff; 
  117.   text-align: left
  118.   position: relative
  119.   .content { 
  120.     width: auto; 
  121.     display: flex; 
  122.     align-items: center; 
  123.   } 
  124.   span { 
  125.     display: inline-block; 
  126.   } 
  127.   .section { 
  128.     position: relative
  129.   } 
  130. </style> 

 

責任編輯:姜華 來源: 前端陽光
相關推薦

2014-08-18 14:30:27

Android倒計時

2022-10-21 15:42:21

倒計時鴻蒙

2011-04-11 09:17:28

Ubuntu倒計時

2015-03-23 17:58:04

驗證碼倒計時并行

2017-07-20 16:21:52

UICountDownTidelay

2014-03-21 13:46:45

2011-04-11 09:50:56

Ubuntu 11.0

2014-02-18 10:36:33

2025-03-14 00:00:00

2013-04-09 10:01:18

微軟Windows XP

2020-10-28 17:54:49

成都信息安全

2013-10-10 09:23:15

Android 4.4Kitkat

2019-12-13 19:37:00

BashLinux命令

2013-10-08 09:24:39

Windows 8.1Windows 8

2022-06-14 08:45:27

瀏覽器IEWindows

2011-03-06 15:49:25

webOSBlackBerry

2011-05-23 08:43:40

jQueryjQuery插件

2017-02-09 16:35:17

戴爾

2013-06-06 11:27:52

iRadioWWDC2013
點贊
收藏

51CTO技術棧公眾號

日本大片在线播放| 理论片中文字幕| 97色伦图片97综合影院| 91精品婷婷国产综合久久性色 | 中文字幕一区二区三区在线播放| 成人在线精品视频| 日本少妇性高潮| 日韩av在线播放网址| 日韩欧美视频在线| 无码人妻丰满熟妇区毛片| 久久77777| 91在线一区二区| 成人av在线天堂| 日韩欧美激情视频| 婷婷久久综合| 亚洲免费小视频| 原创真实夫妻啪啪av| 免费电影日韩网站| 亚洲在线一区二区三区| 婷婷五月色综合| 丰满人妻一区二区三区无码av| 丝袜a∨在线一区二区三区不卡| 久久人人爽人人爽爽久久| 亚洲国产欧美视频| 无人区乱码一区二区三区| 在线观看亚洲精品| 国产精品裸体瑜伽视频| 综合久久2019| 国产精品国产精品国产专区不片 | 91国内在线播放| 色一区二区三区| 亚洲国产欧美另类丝袜| 永久久久久久| 国产免费视频在线| av电影天堂一区二区在线观看| 成人精品福利视频| 男操女视频网站| 午夜在线观看免费一区| 久久久久久久久久久91| 欧美视频www| 久久密一区二区三区| 亚洲全黄一级网站| 国产伦精品一区二区三区妓女| 91综合精品国产丝袜长腿久久| 欧美日韩成人综合| 北条麻妃av高潮尖叫在线观看| 忘忧草在线日韩www影院| 亚洲成人在线观看视频| 日本wwwcom| 羞羞视频在线观看免费| 亚洲欧洲三级电影| 大地资源第二页在线观看高清版| 在线国产情侣| 国产精品久久久久9999吃药| 亚洲国内在线| 天堂а√在线官网| 国产精品久久久久久久浪潮网站 | 亚洲午夜极品| 色综合视频网站| 欧美高清视频一区二区三区| 欧美国产91| 欧美激情在线视频二区| 久久精品视频8| 在线观看一区| 奇门遁甲1982国语版免费观看高清| wwwxxx亚洲| 久久人人超碰| 国产精品高清在线| 在线免费观看一区二区| 久久99久国产精品黄毛片色诱| 国产综合久久久久| 99久久夜色精品国产亚洲| 国产精品99久久久久久似苏梦涵 | 天天亚洲美女在线视频| 日韩av一二三四区| 欧美日韩精品一区二区三区视频| 欧美三级电影网| 午夜免费一级片| 亚洲高清999| 亚洲精品国产拍免费91在线| 黄色aaa视频| 91综合在线| 欧美激情视频在线免费观看 欧美视频免费一| 免费毛片在线播放免费| 在线综合欧美| 国产欧美一区二区三区在线看 | 亚洲精品一区二区久| 久久久久久久久久久国产精品| 国产欧美一区二区精品久久久| 日韩在线观看免费高清| 久久精品女人毛片国产| 日本欧美一区二区| 国产精品久久国产精品| 国产系列在线观看| 亚洲免费在线电影| 日韩伦理在线免费观看| 日本黄色一区| 欧美成人乱码一区二区三区| 这里只有久久精品| 永久亚洲成a人片777777| 欧美亚洲激情在线| 国产又大又长又粗| 26uuu色噜噜精品一区二区| 中文字幕中文字幕在线中心一区| sm捆绑调教国产免费网站在线观看| 色94色欧美sute亚洲13| 在线观看一区二区三区四区| 国产欧美日韩免费观看| 国内精品国产三级国产在线专| 丰满人妻一区二区三区四区| 成a人片国产精品| 国产系列第一页| 无限资源日本好片| 亚洲av无码一区二区三区性色| 99久久亚洲一区二区三区青草| 亚洲日本欧美在线| 国产调教在线| 日韩一区二区三区在线| 日韩女同一区二区三区| 99riav国产精品| 91色中文字幕| 国产精品一二三区视频| 亚洲高清一区二区三区| 蜜臀一区二区三区精品免费视频| 久久综合五月婷婷| 免费97视频在线精品国自产拍| 国产精品第5页| 成人性生交大合| 97精品国产97久久久久久粉红| 深夜视频一区二区| 亚洲欧美激情四射在线日| 久久精品国产亚洲AV无码麻豆| 久久99久国产精品黄毛片色诱| 欧美日韩一区二区视频在线观看| 成全电影大全在线观看| 欧美不卡123| 日韩在线一卡二卡| 美女尤物国产一区| 日韩国产一区久久| 国产欧美一区二区三区精品酒店| 欧美精品一区二区三区视频| 久久久久久久九九九九| 国产精品一区二区果冻传媒| 一卡二卡3卡四卡高清精品视频| 欧美××××黑人××性爽 | 久青青在线观看视频国产| 午夜在线成人av| 制服.丝袜.亚洲.中文.综合懂| 91精品国产乱码久久久久久久 | 美日韩精品免费观看视频| 91精品国产乱码久久久久| 国产精品沙发午睡系列990531| 国产精品入口免费软件| 欧美一区二区三| 国产精品永久免费| 美女免费久久| 日韩视频在线永久播放| 妺妺窝人体色www聚色窝仙踪| 国产成人免费在线观看不卡| 国产又粗又猛又爽又黄的网站| 亚洲精品一区二区三区中文字幕 | 亚洲欧美偷拍视频| 国产亚洲成aⅴ人片在线观看 | 成a人片亚洲日本久久| 精品无码国模私拍视频| 日韩极品在线| 国产精品99久久久久久人| 99中文字幕一区| 666欧美在线视频| 美女视频黄免费| 91视频免费播放| 午夜免费福利在线| 2023国产精品久久久精品双| 成人欧美一区二区三区在线观看 | 婷婷视频在线| 精品日韩在线一区| 久久免费激情视频| 欧美激情在线一区二区| 99精品视频免费版的特色功能| 在线观看不卡| 午夜久久资源| 久久精品九色| 全球成人中文在线| 日韩伦理在线电影| 亚洲成人久久一区| 99久久久无码国产精品免费蜜柚| 日韩毛片精品高清免费| av在线播放网址| 日韩av电影免费观看高清完整版| 路边理发店露脸熟妇泻火| 美女午夜精品| 国产热re99久久6国产精品| 91资源在线观看| 中文欧美在线视频| 色欲av永久无码精品无码蜜桃| 欧美优质美女网站| 欧美成人aaaaⅴ片在线看| 国产精品每日更新| wwwxx日本| 蜜桃久久久久久| 91国视频在线| 亚洲一区二区三区| 欧美日韩最好看的视频| 欧一区二区三区| 国产精品福利小视频| 在线观看中文| 伊人精品在线观看| 日本黄色免费视频| 欧美高清视频www夜色资源网| 午夜精品久久久久久久久久久久久蜜桃| 国产精品久久久久久久岛一牛影视| 手机在线看片日韩| 国产大陆a不卡| 邪恶网站在线观看| 亚洲永久免费精品| 国产精品日韩三级| 99久久精品网站| 日本视频一区二区在线观看| 粉嫩的18在线观看极品精品| 国产精品丝袜久久久久久高清| 欧美极品videos大乳护士| 欧美精品在线网站| 日本在线观看www| 国产亚洲精品美女| 艳母动漫在线看| 亚洲大尺度美女在线| 国产sm主人调教女m视频| 欧美日韩一区二区三区免费看 | 欧洲亚洲一区二区三区四区五区| 精品国产午夜肉伦伦影院| 91久久偷偷做嫩草影院| 日韩免费大片| 国产欧美韩国高清| 成人做爰视频www| 国产精品美女视频网站| 中文字幕在线直播| 55夜色66夜色国产精品视频| 岛国av在线网站| 国内自拍欧美激情| 91制片在线观看| 97在线观看视频国产| 男女免费观看在线爽爽爽视频| 欧美成aaa人片免费看| 国产精品一区二区三区视频网站| 精品国产自在精品国产浪潮| 在线免费观看黄色av| 中文字幕日韩av电影| 精品乱码一区二区三四区视频 | 日韩欧美在线播放| 中文字幕亚洲高清| 欧美性猛交xxxx久久久| 欧美精品一二三四区| 欧美丝袜第一区| 日本高清不卡码| 日本道精品一区二区三区| 五月婷婷亚洲综合| 日本韩国视频一区二区| 国产精品午夜一区二区| 欧美日韩亚洲综合在线 欧美亚洲特黄一级| 国产第一页在线观看| 欧美色国产精品| 国产精品美女一区| 欧美电影精品一区二区| 亚洲精品字幕在线| 精品偷拍各种wc美女嘘嘘| 成年女人的天堂在线| 久久综合伊人77777尤物| 欧美人动性xxxxz0oz| 91高清视频免费观看| 国产亚洲一区二区手机在线观看| 国产日韩视频在线观看| 国产95亚洲| 国产亚洲精品美女久久久m| 亚洲欧美日本伦理| 亚洲日本无吗高清不卡| 欧美视频一区| 无码无遮挡又大又爽又黄的视频| 美女任你摸久久| 又大又长粗又爽又黄少妇视频| 99久久99久久精品国产片果冻| 精品国产成人亚洲午夜福利| 综合色中文字幕| 国产精品黄色网| 欧美日韩国产片| 人妻无码中文字幕免费视频蜜桃| 亚洲丝袜一区在线| v片在线观看| 欧美一区二区.| 国产麻豆一区二区三区| 国产伦视频一区二区三区| 精品久久影院| 欧美这里只有精品| 日本美女一区二区三区视频| 污免费在线观看| 久久色成人在线| 欧美在线视频第一页| 狠狠做深爱婷婷久久综合一区| ,一级淫片a看免费| 亚洲精品mp4| 九义人在线观看完整免费版电视剧| 91国在线精品国内播放| 久久99精品久久久野外观看| 日本一区二区三区视频在线播放 | 亚洲欧美日韩一区二区 | 丁香花在线高清完整版视频 | 亚洲一区二区人妻| 亚洲精品国产suv| dy888亚洲精品一区二区三区| 日本成熟性欧美| 国产精品videossex| 欧美日韩在线免费观看视频| 久久综合九色| 中文字幕精品视频在线| 亚洲欧洲中文日韩久久av乱码| 国产一级免费视频| 亚洲的天堂在线中文字幕| 高潮毛片在线观看| 国产精品美女网站| 国产精品一区2区3区| 18禁网站免费无遮挡无码中文| 国产麻豆一精品一av一免费| 成人黄色a级片| 欧美日韩在线免费观看| 国产成人无码www免费视频播放| 日韩中文理论片| 另类中文字幕国产精品| 欧美性色黄大片人与善| 99热免费精品| 日本一卡二卡在线| 亚洲自拍与偷拍| 99久久精品国产一区色| 精品国产拍在线观看| 国产精品麻豆成人av电影艾秋| 欧美日韩最好看的视频| 美女91精品| 少妇光屁股影院| 日韩欧美高清视频| 四虎影院在线播放| 欧美亚洲成人网| 亚洲人挤奶视频| 日韩av三级在线| 337p粉嫩大胆噜噜噜噜噜91av| 日韩av男人天堂| 亚洲国产黄色片| 国产免费拔擦拔擦8x在线播放| 国产伦理一区二区三区| 一本久道久久久| 亚洲 欧美 日韩在线| 亚洲高清视频中文字幕| 午夜视频在线免费播放| 97国产真实伦对白精彩视频8| 激情小说亚洲色图| 男人天堂999| 久久精品一区二区三区不卡牛牛| 亚洲精品久久久久久久蜜桃| 亚洲免费成人av电影| 经典三级一区二区| 亚洲综合欧美日韩| 国产一区二区三区在线观看免费 | 99国产高清| 影音先锋久久久| av鲁丝一区鲁丝二区鲁丝三区| 欧美日韩一二三四五区| 狠狠狠综合7777久夜色撩人| 国产成人一区二区| 欧美r级电影| 免费不卡av网站| 亚洲国产精品尤物yw在线观看| 午夜福利一区二区三区| 国产成人久久久| 91九色精品| 逼特逼视频在线观看| 欧美性猛xxx| aⅴ在线视频男人的天堂 | 成年人免费在线播放| 中文av一区特黄| 亚洲xxx在线| 日本精品中文字幕| 亚洲大全视频| 欧美日韩一区二区三区四区五区六区| 欧美日韩亚洲一区二区三区| 日本精品在线| 国产一区二区三区四区hd| 日本亚洲三级在线| 久久久全国免费视频| 亚洲男女性事视频| 国产一区二区三区亚洲综合| 97国产精东麻豆人妻电影| 国产精品的网站| 人妻无码中文字幕| 成人福利在线视频| 日韩视频二区| 我要看一级黄色录像| 精品国产电影一区二区| 2019年精品视频自拍| 日韩中字在线观看| 中文字幕视频一区| 九色视频在线播放| 国产伦精品一区二区三区视频黑人| 美女一区二区久久|