秒殺組件開發-可實現多種倒計時功能

1、功能特性

  • 服務端時間不須要屢次請求:其由客戶端與服務端的時間差,和當前的客戶端時間比對得出;從而減小了服務端時間的請求次數,提高性能;且防止了瀏覽器切換,程序掛起時,倒計時的失真;serverTime = frontTime - gaphtml

  • 客戶端時間校準:增長了時間校準,防止用戶在倒計時階段修改客戶端時間後,產生的倒計時時間錯誤或者程序假死等;(pc版淘寶倒計時無時間校準功能)ios

  • 模版自定義:使用者可根據須要自定義模版,將根據模版的關鍵字進行時間格式的換算及模版展示邏輯的轉換,甚至能夠跳過特定的時間順序,隨意組合時間特性,以下:git

    a、時間隨意組合:
    輸入'$Y年,$W周, $m:$s' => 'x年,x周, x:x'es6

    b、模版展示邏輯:
    輸入'$isBefore{即將開始}' => '即將開始'
    or 簡單的「或運算」:輸入'$isBefore||$isIng{倒計時運行中}' => '倒計時運行中'github

    c、時間佔位符:正則表達式

    佔位符列表:[$Y(年), $M(月), $W(周), $D(日), $h(時), $m(分), $s(秒), $_100ms(100毫秒)];算法

    模版替換方法:將取到每類佔位符的數量,從而將數值按照佔位符數量進行填坑;axios

    坑位的填充算法:坑位大於數值,數值前補‘0’;坑位小於數值,多餘數值填入第一個坑位;示例以下:promise

    ```
    // 剩餘時間
    restTime = { 
        day: 112, 
        minute: 4, 
        second: 15
    }
    
    // 輸入模版
    template = [
        '<span>$D</span>',
        '<span>$D</span>',
        'days,'
        '<span>$m</span>',
        '<span>$m</span>',
        ','
        '<span>$s</span>',
        '<span>$s</span>'
    ].join('')
    =>
    // 編譯結果
    resultTemplate = [
        '<span>11</span>',
        '<span>2</span>',
        'days,'
        '<span>0</span>',
        '<span>4</span>',
        ','
        '<span>1</span>',
        '<span>5</span>'
    ].join('')

    ```瀏覽器

  • 單元測試

    增長了類方法的單元測試,使用mocha平臺和chai斷言庫,從而下降了開發、調試成本;有些功能開發和驗證,均可經過跑單元測試直接拿到,並不須要將這個頁面跑起來~~

2、功能流程圖

圖2-1 秒殺組件流程圖

流程分析:

a、時間處理:在整個流程中,涉及許多時間的運算,因此將時間轉換成統一的數值,將簡化運算;另輸入的日期可能帶有'-',在ios中沒法識別,轉換函數中作了兼容處理;

b、客戶端時間校準:該流程包括「驗證客戶端時間異常」,及「校準」,兩個環節。第一個環節,經過將當前客戶端時間與上次客戶端時間的差值,與異常閾值進行比對得出,該閾值須要大於倒計時during。第二個環節,經過獲取服務器時間的回調函數給出,該回調函數返回promise對象,其resolve給出{success, serverTime},經過這種封裝,將業務層與組件邏輯進行分離。

c、編譯

  • step1:獲取模版包含的佔位符類型;
  • step2:計算佔位符相應的剩餘時間對象;
  • step3:根據倒計時狀態,處理模版展示/隱藏邏輯;其中使用了正則表達式的test方法和捕獲功能,進行模版替換;
  • step4:根據佔位符坑位與剩餘時間對象,填坑,輸入編譯後的html string片斷;其中使用了正則表達式的match方法,掌握佔位符坑位的數量。

3、使用

3.一、參數

  • style: 樣式,會提供默認樣式;
  • template: 倒計時渲染的模版,其中包含:

    a、展示/顯示命令:$isBefore$isIng$isAfter 和 或運算(||)
    如:$isBefore{xxx} 若是狀態爲未開始,則露出xxx,不然不顯示;
    ($isIng||$isBefore||$isAfter{xxx},支持以上三個命令的或邏輯)

    b、佔位符[$Y(年), $M(月), $W(周), $D(日), $h(時), $m(分), $s(秒), $_100ms(100毫秒)]
    若是包含$W,則日不大於7!

  • el: 掛載點(默認爲body);
  • pClass: 自定義秒殺組件容器的類;
  • threshold: 檢驗客戶端時間是否異常的閾值,單位ms; 必須大於during;默認爲30s
  • *getServerTime[與gap必有其一]: 獲取服務端時間的函數,可校驗客戶端時間變化;要求其返回promise對象,resolve數據的結構爲{success, serverTime};
  • *gap[與getServerTime必有其一]: 服務端與客戶端時間差 gap = frontTime - serverTime;
  • *startTime[必須]: 秒殺開始時間;
  • *endTime[必須]:秒殺結束時間;
  • during: UI渲染頻率,單位ms; 默認爲100ms;
  • perCb: 倒計時中的回調,dom掛載成功後,執行perCallback,其參數爲Object,{ time:{year, month, week, day, hour, minute, second, _100ms}, state: {isBefore, isIng, isAfter}}
  • beginSeckillCb: 秒殺開始時的回調;[參數同perCb]
  • endSeckillCb: 秒殺結束時的回調。[無參數同perCb]

3.二、調用

import Seckill from './seckill'

new Seckill({
    endTime: '2018-3-29 11:00:00',
    startTime: '2018-3-23 15:00:00',
    getServerTime: () => {
        return new Promise((resolve, reject) => {
            axios.get('/common/getNow').then(({data: {success, result}}) => {
                resolve({
                    success,
                    serverTime: result
                })
            })
        })
    }
}).init()

UI渲染如圖:

圖3-2-1 默認template渲染圖

3.三、單元測試
命令:mocha --compilers js:babel-core/register ./seckill.test.js
經過使用編譯參數,支持es6語法~

引用:

import Seckill from './seckill'
import { expect } from 'chai'

const seckillObj = new Seckill()
const Util = {
    _formatDate: seckillObj._formatDate,
    _currServerTime: seckillObj._currServerTime
}

須要留意,再測試類時,須要進行new生成實例對象,再對其方法進行單元測試;另,使用es6的class生成的類,不能枚舉,因此,沒法使用Object.assign方法,直接將對象方法進行拷貝,因此,一些工具函數,需手動複製。

如檢測數據方法是否正確:

describe('expect',function(){
    it('checkAFormatData_required',function(){
        expect(seckillObj._checkAFormatData.apply(Object.assign({
            getServerTime: function() {},
            startTime: '2018-11-12 12:12:12',
            endTime: '2018-11-13 12:12:12',
        }, Util))).to.be.ok;
    });
});

經過使用將工具函數手動放入Util對象當中,再傳遞給當前做用域。

而有些狀況下,無需使用實際的函數方法進行測試,可重置依賴的函數,從而下降單元測試的藕合度,提高靈活性和錯誤定位能力,如:

// 編譯模版顯示隱藏邏輯
    describe('expect',function(){
        it('compileDisplay',function(){
            expect(seckillObj._compileDisplay.call({
                _isIng: () => true,
                _isAfter: () => false,
                _isBefore: () => false
            }, '$isIng{<span>即將開始</span>}')).to.be.equal('<span>即將開始</span>')
        });
    });

上述測試代碼,若是使用實際的_isIng()等狀態函數時,將須要傳入額外的startTime、endTime、format函數等,這樣依賴一層層往上傳遞,測試將變得複雜。而這些狀態函數、format函數等,都已經通過了自身的單元測試,無需再拿來重測。對於該顯示隱藏的函數單元來講,只須要拿到各狀態的布爾值,即可完成該編譯單元的獨立測試,所以,重置狀態函數,返回須要的狀態布爾值即可。

單元測試結果如圖:

圖3-3-1 單元測試結果圖

3.四、UI渲染個性化
在當前提供的模版邏輯和佔位符沒法知足UI須要的狀況下,可經過使用perCb來實現個性化定製。因爲perCb回調是在每一個倒計時中進行調用,且其調用位置位於template模版掛載以後,所以此時將#seckill的內容置換掉,將實現這一特定的需求。但須要留意的是,perCb中返回的time對象由模版的佔位符計算得出,所以,須要在模版中給出所需的佔位符。代碼以下:

new Seckill({
    template: '<p style="display:none">$Y $M $W $D $h $m $s $_100ms</p>',
    endTime: '2019-4-29 11:00:00',
    startTime: '2018-3-23 15:00:00',
    getServerTime: () => {
        return new Promise((resolve, reject) => {
            axios.get('/common/getNow').then(({data: {success, result}}) => {
                resolve({
                    success,
                    serverTime: result
                })
            })
        })
    },
    perCb: ({time, state}) => {
        document.querySelector('#seckill').innerHTML = [
            '<p style="..."> ' + time.year + ' 年</p>',
            '<p style="..."> ' + time.month + ' 月</p>',
            '<p style="..."> ' + time.day + ' 日</p>',
            '<p style="..."> ' + time.second + ' 秒</p>'
        ].join('')
        console.log('per activity', time, state)
    }
}).init()

控制檯捕獲消息及UI渲染結果以下:

圖3-4-1 控制檯捕獲數據圖

圖3-4-2 個性化定製UI渲染圖

tips: a、在輸入的template中,請將DOM元素設置display:none,防止頁面抖動~~
b、在個性化渲染中,須要哪些時間,在模版中制定便可;出如今模版中的佔位符,都將參與計算,不然不參與。

4、代碼以下

code 地址

相關文章
相關標籤/搜索