<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          研究大佬寫的倒計(jì)時(shí)組件(Vue),學(xué)到了不少東西

          共 26678字,需瀏覽 54分鐘

           ·

          2021-07-20 16:33

          回復(fù)【加群】加入技術(shù)交流群


          • 一、前言

          • 二、開(kāi)始手操

            • 1. 先創(chuàng)建一個(gè)vue組件

            • 2. 實(shí)現(xiàn)基本的倒計(jì)時(shí)組件

            • 3. 為什么要**用setTimeout來(lái)模擬setInterval的行為**?

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

            • 5. 使用 diffTime

            • 6. 添加新功能:可以傳入到期時(shí)間。

            • 7. 添加新功能:可以選擇要顯示的內(nèi)容,例如只顯示秒,或者只顯示小時(shí)。

          • 三、學(xué)習(xí)總結(jié)


          一、前言

          入職的第一個(gè)需求是跟著一位前端大佬一起完成的一個(gè)活動(dòng)項(xiàng)目。

          由于是一起開(kāi)發(fā),喜歡閱讀源碼的我,當(dāng)然不會(huì)放過(guò)閱讀大佬的代碼的機(jī)會(huì)。

          因?yàn)槲业捻?yè)面中需要使用到倒計(jì)時(shí)功能,發(fā)現(xiàn)大佬的已經(jīng)寫了個(gè)現(xiàn)成的倒計(jì)時(shí)組件,于是直接就拿過(guò)來(lái)用了。

          傳個(gè)參數(shù)就實(shí)現(xiàn)了功能的感覺(jué)真是太棒了。項(xiàng)目完成后,就膜拜了一下大佬的倒計(jì)時(shí)組件的代碼。真是讓我學(xué)到了不少。列舉如下:

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

          好了,你可能不太理解這些問(wèn)題,但是沒(méi)關(guān)系,看完下面的解釋,相信你會(huì)豁然開(kāi)朗。

          二、開(kāi)始手操

          1. 先創(chuàng)建一個(gè)vue組件

          <template>
            <div class="_base-count-down">
            </div>
          </template>
          <script>

          export default {
            data() => ({
             
            }),
            props: {
              
            },
          };
          </script>
          <style lang='scss' scoped>

          </style>

          2. 實(shí)現(xiàn)基本的倒計(jì)時(shí)組件

          接下來(lái),假設(shè)接口獲得的是一個(gè)剩余時(shí)間。

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

          <template>
            <div class="_base-count-down">
            </div>
          </template>
          <script>

          export default {
            data() => ({
            }),
            props: {
              time: {
                type: [NumberString],
                default0
              },
              isMilliSecond: {
                typeBoolean,
                defaultfalse
              }
            },
            computed: {
              duration() {
                const time = this.isMiniSecond ? Math.round(+this.time / 1000) : Math.round(+this.time);
                return time;
              }
            },
          };
          </script>
          <style lang='scss' scoped>

          </style>

          computed中的duration是將time進(jìn)行轉(zhuǎn)化的結(jié)果,不管time是毫秒還是秒,都轉(zhuǎn)化為秒 不知道你注意到了沒(méi)有:+this.time。為什么要在前面加個(gè)‘+’號(hào)。這點(diǎn)很值得我們學(xué)習(xí),因?yàn)榻涌诜祷氐囊淮當(dāng)?shù)字有時(shí)候是字符串的形式,有時(shí)候是數(shù)字的形式(不能過(guò)分相信后端同學(xué),必須自己做好防范)。所以通過(guò)前面加個(gè)‘+’號(hào) 通通轉(zhuǎn)化為數(shù)字?,F(xiàn)在的duration就是轉(zhuǎn)化后的time啦!

          我們獲得duration之后就可以開(kāi)始倒計(jì)時(shí)了

          <template>
            <div class="_base-count-down">
            </div>
          </template>
          <script>

          export default {
            data() => ({
            }),
            props: {
              time: {
                type: [NumberString],
                default0
              },
              isMilliSecond: {
                typeBoolean,
                defaultfalse
              }
            },
            computed: {
              duration() {
                const time = this.isMiniSecond ? Math.round(+this.time / 1000) : Math.round(+this.time);
                return time;
              }
            },
            // 新增代碼:
            mounted() {
              this.countDown();
            },
            methods: {
              countDown() {
                this.getTime(this.duration);
              },
            }
          };
          </script>
          <style lang='scss' scoped>

          </style>

          在這里創(chuàng)建了一個(gè)countDown方法,表示開(kāi)始倒計(jì)時(shí)的意思,已進(jìn)入頁(yè)面就開(kāi)始執(zhí)行countdown方法。

          countDown方法調(diào)用了getTime方法,getTime需要傳入duration這個(gè)參數(shù),也就是我們獲得的剩余時(shí)間。

          現(xiàn)在來(lái)實(shí)現(xiàn)一下這個(gè)方法。

          <template>
            <div class="_base-count-down">
              還剩{{day}}天{{hours}}:{{mins}}:{{seconds}}
            </div>
          </template>
          <script>

          export default {
            data() => ({
              days'0',
              hours'00',
              mins'00',
              seconds'00',
              timernull,
            }),
            props: {
              time: {
                type: [NumberString],
                default0
              },
              isMilliSecond: {
                typeBoolean,
                defaultfalse
              }
            },
            computed: {
              duration() {
                const time = this.isMiniSecond ? Math.round(+this.time / 1000) : Math.round(+this.time);
                return time;
              }
            },
            mounted() {
              this.countDown();
            },
            methods: {
              countDown() {
                this.getTime(this.duration);
              },
              // 新增代碼:
              getTime(duration) {
                this.timer && clearTimeout(this.timer);
                if (duration < 0) {
                  return;
                }
                const { dd, hh, mm, ss } = this.durationFormatter(duration);
                this.days = dd || 0;
                this.hours = hh || 0;
                this.mins = mm || 0;
                this.seconds = ss || 0;
                this.timer = setTimeout(() => {
                  this.getTime(duration - 1);
                }, 1000);
              }
            }
          };
          </script>
          <style lang='scss' scoped>

          </style>

          可以看到,getTime的目的就是獲得 days,hours,mins,seconds,然后顯示到html上,并且通過(guò)定時(shí)器實(shí)時(shí)來(lái)刷新days,hours,mins,seconds這個(gè)幾個(gè)值。從而實(shí)現(xiàn)了倒計(jì)時(shí)。很簡(jiǎn)單,有木有?

          durationFormatter是一個(gè)將duration轉(zhuǎn)化成天數(shù),小時(shí),分鐘,秒數(shù)的方法,很簡(jiǎn)單,可以看下它的具體實(shí)現(xiàn)。

          durationFormatter(time) {
            if (!time) return { ss0 };
            let t = time;
            const ss = t % 60;
            t = (t - ss) / 60;
            if (t < 1return { ss };
            const mm = t % 60;
            t = (t - mm) / 60;
            if (t < 1return { mm, ss };
            const hh = t % 24;
            t = (t - hh) / 24;
            if (t < 1return { hh, mm, ss };
            const dd = t;
            return { dd, hh, mm, ss };
          },

          好了,問(wèn)題開(kāi)始來(lái)了?。?img data-ratio="1.003717472118959" src="https://filescdn.proginn.com/ffb59aa3f157f0b8b528f3b1dff8e88f/137d982e0d0f4c2d45db3067e08a330a.webp" data-type="gif" data-w="269" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;">

          3. 為什么要用setTimeout來(lái)模擬setInterval的行為?

          這里用setInerval不是更方便嗎?

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

          可以看看setInterval有什么缺點(diǎn):

          再次強(qiáng)調(diào),定時(shí)器指定的時(shí)間間隔,表示的是何時(shí)將定時(shí)器的代碼添加到消息隊(duì)列,而不是何時(shí)執(zhí)行代碼。所以真正何時(shí)執(zhí)行代碼的時(shí)間是不能保證的,取決于何時(shí)被主線程的事件循環(huán)取到,并執(zhí)行。

          setInterval(function, N)  
          //即:每隔N秒把function事件推到消息隊(duì)列中

          上圖可見(jiàn),setInterval每隔100ms往隊(duì)列中添加一個(gè)事件;100ms后,添加T1定時(shí)器代碼至隊(duì)列中,主線程中還有任務(wù)在執(zhí)行,所以等待,some event執(zhí)行結(jié)束后執(zhí)行T1定時(shí)器代碼;又過(guò)了100ms,T2定時(shí)器被添加到隊(duì)列中,主線程還在執(zhí)行T1代碼,所以等待;又過(guò)了100ms,理論上又要往隊(duì)列里推一個(gè)定時(shí)器代碼,但由于此時(shí)T2還在隊(duì)列中,所以T3不會(huì)被添加,結(jié)果就是此時(shí)被跳過(guò);這里我們可以看到,T1定時(shí)器執(zhí)行結(jié)束后馬上執(zhí)行了T2代碼,所以并沒(méi)有達(dá)到定時(shí)器的效果。

          綜上所述,setInterval有兩個(gè)缺點(diǎn):

          1. 使用setInterval時(shí),某些間隔會(huì)被跳過(guò);
          2. 可能多個(gè)定時(shí)器會(huì)連續(xù)執(zhí)行;

          可以這么理解:每個(gè)setTimeout產(chǎn)生的任務(wù)會(huì)直接push到任務(wù)隊(duì)列中;而setInterval在每次把任務(wù)push到任務(wù)隊(duì)列前,都要進(jìn)行一下判斷(看上次的任務(wù)是否仍在隊(duì)列中)

          因而我們一般用setTimeout模擬setInterval,來(lái)規(guī)避掉上面的缺點(diǎn)。

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

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

          假設(shè)一個(gè)場(chǎng)景:

          如圖所示,在倒計(jì)時(shí)的父組件中,有兩個(gè)按鈕,點(diǎn)擊活動(dòng)一就會(huì)傳入活動(dòng)一的剩余時(shí)間,點(diǎn)擊活動(dòng)二,就會(huì)傳入活動(dòng)二的時(shí)間。

          如果此時(shí)倒計(jì)時(shí)組件正在做活動(dòng)一的倒計(jì)時(shí),然后點(diǎn)擊活動(dòng)二,就要會(huì)馬上傳入新的time,這個(gè)時(shí)候就需要重新計(jì)時(shí)。當(dāng)然,這里并不會(huì)重新計(jì)時(shí),因?yàn)榻M件的mounted只會(huì)執(zhí)行一次。也就是說(shuō)this.countDown();只會(huì)執(zhí)行一次,也就是說(shuō)this.getTime(this.duration);只會(huì)執(zhí)行一次,因此duration還是活動(dòng)一的時(shí)間,怎么辦呢?watch派上用場(chǎng)了。

          我們來(lái)監(jiān)聽(tīng)duration,如果發(fā)現(xiàn)duration變化,說(shuō)明新的時(shí)間time傳入組件,這時(shí)就要重新調(diào)用this.countDown()。

          代碼如下:

          <template>
            <div class="_base-count-down">
              還剩{{day}}天{{hours}}:{{mins}}:{{seconds}}
            </div>
          </template>
          <script>

          export default {
            data() => ({
              days'0',
              hours'00',
              mins'00',
              seconds'00',
              timernull,
            }),
            props: {
              time: {
                type: [NumberString],
                default0
              },
              isMilliSecond: {
                typeBoolean,
                defaultfalse
              }
            },
            computed: {
              duration() {
                const time = this.isMiniSecond ? Math.round(+this.time / 1000) : Math.round(+this.time);
                return time;
              }
            },
            mounted() {
              this.countDown();
            },
            // 新增代碼:
            watch: {
              duration() {
                this.countDown();
              }
            },
            methods: {
              countDown() {
                this.getTime(this.duration);
              },
              durationFormatter(){...}
              getTime(duration) {
                this.timer && clearTimeout(this.timer);
                if (duration < 0) {
                  return;
                }
                const { dd, hh, mm, ss } = this.durationFormatter(duration);
                this.days = dd || 0;
                this.hours = hh || 0;
                this.mins = mm || 0;
                this.seconds = ss || 0;
                this.timer = setTimeout(() => {
                  this.getTime(duration - 1);
                }, 1000);
              }
            }
          };
          </script>
          <style lang='scss' scoped>

          </style>

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

          這樣,假設(shè)現(xiàn)在頁(yè)面顯示的是活動(dòng)一的時(shí)間,這時(shí),執(zhí)行到setTimeout,在一秒后就會(huì)把setTimeout里的回調(diào)函數(shù)放到任務(wù)隊(duì)列中,注意是一秒后哦!這時(shí),然而,在這一秒的開(kāi)頭,我們點(diǎn)擊了活動(dòng)二按鈕,這時(shí)候的活動(dòng)二的時(shí)間就會(huì)傳入倒計(jì)時(shí)組件中,然后觸發(fā)countDown(),也就調(diào)用this.getTime(this.duration);,然后執(zhí)行到setTimeout,也會(huì)一秒后把回調(diào)函數(shù)放到任務(wù)隊(duì)列中。

          這時(shí),任務(wù)隊(duì)列中就會(huì)有兩個(gè)setTimeout的回調(diào)函數(shù)了。等待一秒過(guò)去,兩個(gè)回調(diào)函數(shù)相繼執(zhí)行,我們就會(huì)看到頁(yè)面上的時(shí)間一下子背減了2,實(shí)際上是很快速地進(jìn)行了兩遍減1的操作。

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

          5. 使用 diffTime

          當(dāng)你認(rèn)為這是一個(gè)完美的組件的時(shí)候,你想把這個(gè)組件用到項(xiàng)目上,假設(shè)你也確實(shí)用了,而且還上線了,確發(fā)現(xiàn)出現(xiàn)了個(gè)大問(wèn)題:當(dāng)頁(yè)面打開(kāi)的時(shí)候,倒計(jì)時(shí)開(kāi)始了,時(shí)間是 還剩1天12:25:25,然后有人給你發(fā)微信,你馬上切換到微信,回復(fù)消息后切回瀏覽器,發(fā)現(xiàn)倒計(jì)時(shí)時(shí)間卻還是還剩1天12:25:25。你慌了:你寫的代碼出現(xiàn)bug了!

          這是怎么回事?

          出于節(jié)能的考慮, 部分瀏覽器在進(jìn)入后臺(tái)時(shí)(或者失去焦點(diǎn)時(shí)), 會(huì)將 setTimeout 等定時(shí)任務(wù)暫停 待用戶回到瀏覽器時(shí), 才會(huì)重新激活定時(shí)任務(wù)

          說(shuō)是暫停, 其實(shí)應(yīng)該說(shuō)是延遲, 1s 的任務(wù)延遲到 2s, 2s 的延遲到 5s, 實(shí)際情況因?yàn)g覽器而異。

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

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

          <template>
            <div class="_base-count-down">
              還剩{{day}}天{{hours}}:{{mins}}:{{seconds}}
            </div>
          </template>
          <script>

          export default {
            data() => ({
              days'0',
              hours'00',
              mins'00',
              seconds'00',
              timernull,
              curTime0,// 新增代碼:
            }),
            props: {
              time: {
                type: [NumberString],
                default0
              },
              isMilliSecond: {
                typeBoolean,
                defaultfalse
              }
            },
            computed: {
              duration() {
                const time = this.isMiniSecond ? Math.round(+this.time / 1000) : Math.round(+this.time);
                return time;
              }
            },
            mounted() {
              this.countDown();
            },
            
            watch: {
              duration() {
                this.countDown();
              }
            },
            methods: {
              countDown() {
                // 新增代碼:
                this.curTime = Date.now();
                this.getTime(this.duration);
              },
              durationFormatter(){...}
              getTime(duration) {
                this.timer && clearTimeout(this.timer);
                if (duration < 0) {
                  return;
                }
                const { dd, hh, mm, ss } = this.durationFormatter(duration);
                this.days = dd || 0;
                this.hours = hh || 0;
                this.mins = mm || 0;
                this.seconds = ss || 0;
                this.timer = setTimeout(() => {
                  // 新增代碼:
                  const now = Date.now();
                  const diffTime = Math.floor((now - this.curTime) / 1000);
                  this.curTime = now;
                  this.getTime(duration - diffTime);
                }, 1000);
              }
            }
          };
          </script>
          <style lang='scss' scoped>

          </style>

          可以看到,我們?cè)谌齻€(gè)位置添加了新的代碼。

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

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

          now 是 setTimeout的回調(diào)函數(shù)執(zhí)行的時(shí)候的那個(gè)時(shí)刻。

          因而 diffTime 則 表示 當(dāng)前這個(gè)setTimeout的回調(diào)函數(shù)執(zhí)行的時(shí)刻距離上 頁(yè)面上的剩余時(shí)間上一次變化的時(shí)間段。其實(shí)也就是 當(dāng)前這個(gè)setTimeout的回調(diào)函數(shù)執(zhí)行的時(shí)刻距離上 一個(gè)setTimeout的回調(diào)函數(shù)執(zhí)行的時(shí)刻時(shí)間段。

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

          你打開(kāi)了這個(gè)倒計(jì)時(shí)頁(yè)面,于是執(zhí)行了countDown,也就是說(shuō)要執(zhí)行g(shù)etTime這個(gè)方法了。也就是會(huì)馬上執(zhí)行下列的代碼。

          this.days = dd || 0;
          this.hours = hh || 0;
          this.mins = mm || 0;
          this.seconds = ss || 0;

          執(zhí)行完這些代碼頁(yè)面上就會(huì)出現(xiàn)剩余時(shí)間。

          this.curTime = Date.now(); 就記錄下了此刻的時(shí)間點(diǎn)。

          然后一秒后執(zhí)行setTimeout里的回調(diào)函數(shù):

          const now = Date.now(); 記錄當(dāng)前這個(gè)setTimeout的回調(diào)函數(shù)執(zhí)行的時(shí)間點(diǎn)。

          const diffTime = Math.floor((now - this.curTime) / 1000); 記錄當(dāng)前這個(gè)setTimeout的回調(diào)函數(shù)執(zhí)行的時(shí)間點(diǎn)距離頁(yè)面上開(kāi)始 渲染 剩余時(shí)間的 這一段時(shí)間。其實(shí)此時(shí)的diffTime就是=1。

          然后this.curTime = now; 將curTime的值變成當(dāng)前這個(gè)setTimeout的回調(diào)函數(shù)執(zhí)行的時(shí)間點(diǎn)。

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

          然后又執(zhí)行g(shù)etTime,就會(huì)重新執(zhí)行下面的代碼,有渲染了新的剩余時(shí)間。

          this.days = dd || 0;
          this.hours = hh || 0;
          this.mins = mm || 0;
          this.seconds = ss || 0;

          然后一秒后又要執(zhí)行setTmieout的回調(diào)函數(shù),在這一秒還沒(méi)結(jié)束的時(shí)候,我們將瀏覽器切到后臺(tái),此時(shí)setTimeout冷卻了。等5秒后再切回。于是setTmieout的回調(diào)函數(shù)才得以執(zhí)行。

          這時(shí)const now = Date.now(); 記錄當(dāng)前這個(gè)setTimeout的回調(diào)函數(shù)執(zhí)行的時(shí)間點(diǎn)。

          而curTime是上一個(gè)setTimeout的回調(diào)函數(shù)執(zhí)行的時(shí)間。

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

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

          這樣就完美解決了因?yàn)闉g覽器切到后臺(tái),導(dǎo)致剩余時(shí)間不變的問(wèn)題。

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

          之前是只能傳入剩余時(shí)間的,現(xiàn)在希望也支持傳入到期時(shí)間。

          只需要改動(dòng)一下duration就好了。

            computed: {
              duration() {
                if (this.end) {
                  let end = String(this.end).length >= 13 ? +this.end : +this.end * 1000;
                  end -= Date.now();
                  return end;
                }
                const time = this.isMiniSecond ? Math.round(+this.time / 1000) : Math.round(+this.time);
                return time;
              }
            },

          判斷傳入的end的長(zhǎng)度是否大于13來(lái)判斷是秒還是毫秒。輕松!

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

          只需要改動(dòng)一下html:

          <template>
            <div class="_base-count-down no-rtl">
              <div class="content">
                <slot v-bind="{
                  d: days, h: hours, m: mins, s: seconds,
                  hh: `00${hours}`.slice(-2),
                  mm: `00${mins}`.slice(-2),
                  ss: `00${seconds}`.slice(-2),
                }"
          >
          </slot>
              </div>
            </div>

          </template>

          很巧妙有沒(méi)有,只需要用插槽,就把倒計(jì)時(shí)組件,也就是把子組件的值傳遞給父組件了。

          看看父組件是怎么使用這個(gè)組件的。

          <base-counter v-slot="timeObj" :time="countDown">
            <div class="count-down">
              <div class="icon"></div>
              {{timeObj.d}}天{{timeObj.hh}}小時(shí){{timeObj.mm}}分鐘{{timeObj.ss}}秒
            </div>
          </base-counter>

          看,如此巧妙又簡(jiǎn)單。

          發(fā)現(xiàn)00${hours}.slice(-2) 這種寫法也很值得學(xué)習(xí)。以前在獲得到分鐘的時(shí)候,要手動(dòng)判斷獲得的分鐘是兩位數(shù)還是一位數(shù),如果是一位數(shù)的話就要在前面手動(dòng)補(bǔ)上0。就像下面的代碼:

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

          00${hours}.slice(-2) 則不用判斷,先補(bǔ)上0再說(shuō),然后再?gòu)暮竺嫱敖厝晌弧?img data-ratio="0.9186991869918699" src="https://filescdn.proginn.com/2849eae1cf15860a32c462efc5f73810/a7b7894d2cf10d1c3daede2299ba97f0.webp" data-type="gif" data-w="246" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;">

          到此。

          一個(gè)完美的倒計(jì)時(shí)組件就完成了。

          三、學(xué)習(xí)總結(jié)

          1. 明白了setInterval的缺點(diǎn)以及用setTimeout代替setInterval。
          2. 學(xué)到了“+”,操作,不管三七二十一,將接口得到的長(zhǎng)串?dāng)?shù)字轉(zhuǎn)化為數(shù)字保平安。
          3. 利用clearTimeout來(lái)清除掉之前的計(jì)時(shí)器,以防止造成影響。
          4. 學(xué)會(huì)使用v-slot來(lái)子傳父?jìng)髦?/section>
          5. 學(xué)會(huì)一個(gè)倒計(jì)時(shí)組件,為了以后方便cv操作。把組件完整代碼貼上:
          <template>
            <div class="_base-count-down no-rtl">
              <div class="content">
                <slot v-bind="{
                  d: days, h: hours, m: mins, s: seconds,
                  hh: `00${hours}`.slice(-2),
                  mm: `00${mins}`.slice(-2),
                  ss: `00${seconds}`.slice(-2),
                }"
          >
          </slot>
              </div>
            </div>
          </template>
          <script>
          /* eslint-disable object-curly-newline */

          export default {
            data() => ({
              days'0',
              hours'00',
              mins'00',
              seconds'00',
              timernull,
              curTime0
            }),
            props: {
              time: {
                type: [NumberString],
                default0
              },
              refreshCounter: {
                type: [NumberString],
                default0
              },
              end: {
                type: [NumberString],
                default0
              },
              isMiniSecond: {
                typeBoolean,
                defaultfalse
              }
            },
            computed: {
              duration() {
                if (this.end) {
                  let end = String(this.end).length >= 13 ? +this.end : +this.end * 1000;
                  end -= Date.now();
                  return end;
                }
                const time = this.isMiniSecond ? Math.round(+this.time / 1000) : Math.round(+this.time);
                return time;
              }
            },
            mounted() {
              this.countDown();
            },
            watch: {
              duration() {
                this.countDown();
              },
              refreshCounter() {
                this.countDown();
              }
            },
            methods: {
              durationFormatter(time) {
                if (!time) return { ss0 };
                let t = time;
                const ss = t % 60;
                t = (t - ss) / 60;
                if (t < 1return { ss };
                const mm = t % 60;
                t = (t - mm) / 60;
                if (t < 1return { mm, ss };
                const hh = t % 24;
                t = (t - hh) / 24;
                if (t < 1return { hh, mm, ss };
                const dd = t;
                return { dd, hh, mm, ss };
              },
              countDown() {
                // eslint-disable-next-line no-unused-expressions
                this.curTime = Date.now();
                this.getTime(this.duration);
              },
              getTime(time) {
                // eslint-disable-next-line no-unused-expressions
                this.timer && clearTimeout(this.timer);
                if (time < 0) {
                  return;
                }
                // eslint-disable-next-line object-curly-newline
                const { dd, hh, mm, ss } = this.durationFormatter(time);
                this.days = dd || 0;
                // this.hours = `00${hh || ''}`.slice(-2);
                // this.mins = `00${mm || ''}`.slice(-2);
                // this.seconds = `00${ss || ''}`.slice(-2);
                this.hours = hh || 0;
                this.mins = mm || 0;
                this.seconds = ss || 0;
                this.timer = setTimeout(() => {
                  const now = Date.now();
                  const diffTime = Math.floor((now - this.curTime) / 1000);
                  const step = diffTime > 1 ? diffTime : 1// 頁(yè)面退到后臺(tái)的時(shí)候不會(huì)計(jì)時(shí),對(duì)比時(shí)間差,大于1s的重置倒計(jì)時(shí)
                  this.curTime = now;
                  this.getTime(time - step);
                }, 1000);
              }
            }
          };
          </script>
          <style lang='scss' scoped>
          @import '~@assets/css/common.scss';

          ._base-count-down {
            color: #fff;
            text-align: left;
            position: relative;
            .content {
              width: auto;
              display: flex;
              align-items: center;
            }
            span {
              display: inline-block;
            }
            .section {
              position: relative;
            }
          }
          </style>

              你學(xué)到了什么?歡迎補(bǔ)充!

          瀏覽 57
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  猫咪www成人免费网站无码 | 国产黄色电影在线观看 | av天天看| 亚洲视频高清无码 | 口爆在线|