秒杀组件开发-可实现多种倒计时功能

一、功能特性

  • 服务端时间不需要多次请求:其由客户端与服务端的时间差,和当前的客户端时间比对得出;从而减少了服务端时间的请求次数,提升性能;且防止了浏览器切换,程序挂起时,倒计时的失真;serverTime = frontTime - gap

  • 客户端时间校准:增加了时间校准,防止用户在倒计时阶段修改客户端时间后,产生的倒计时时间错误或者程序假死等;(pc版淘宝倒计时无时间校准功能)

  • 模版自定义:使用者可根据需要自定义模版,将根据模版的关键字进行时间格式的换算及模版展现逻辑的转换,甚至可以跳过特定的时间顺序,随意组合时间特性,如下:

    a、时间随意组合:
    输入'$Y年,$W周, $m:$s' => 'x年,x周, x:x'

    b、模版展现逻辑:
    输入'$isBefore{即将开始}' => '即将开始'
    or 简单的“或运算”:输入'$isBefore||$isIng{倒计时运行中}' => '倒计时运行中'

    c、时间占位符:

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

    模版替换方法:将取到每类占位符的数量,从而将数值按照占位符数量进行填坑;

    坑位的填充算法:坑位大于数值,数值前补‘0’;坑位小于数值,多余数值填入第一个坑位;示例如下:

    ```
    // 剩余时间
    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断言库,从而降低了开发、调试成本;有些功能开发和验证,都可通过跑单元测试直接拿到,并不需要将这个页面跑起来~~

二、功能流程图

1094893-20180327165845652-1621415052.png

图2-1 秒杀组件流程图

流程分析:

a、时间处理:在整个流程中,涉及许多时间的运算,所以将时间转换成统一的数值,将简化运算;另输入的日期可能带有'-',在ios中无法识别,转换函数中做了兼容处理;

b、客户端时间校准:该流程包括“验证客户端时间异常”,及“校准”,两个环节。第一个环节,通过将当前客户端时间与上次客户端时间的差值,与异常阈值进行比对得出,该阈值需要大于倒计时during。第二个环节,通过获取服务器时间的回调函数给出,该回调函数返回promise对象,其resolve给出{success, serverTime},通过这种封装,将业务层与组件逻辑进行分离。

c、编译

  • step1:获取模版包含的占位符类型;
  • step2:计算占位符相应的剩余时间对象;
  • step3:根据倒计时状态,处理模版展现/隐藏逻辑;其中使用了正则表达式的test方法和捕获功能,进行模版替换;
  • step4:根据占位符坑位与剩余时间对象,填坑,输入编译后的html string片段;其中使用了正则表达式的match方法,掌握占位符坑位的数量。

三、使用

3.1、参数

  • 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.2、调用

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渲染如图:

1094893-20180327171014873-199447104.png

图3-2-1 默认template渲染图

3.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函数等,都已经经过了自身的单元测试,无需再拿来重测。对于该显示隐藏的函数单元来说,只需要拿到各状态的布尔值,便可完成该编译单元的独立测试,因此,重置状态函数,返回需要的状态布尔值便可。

单元测试结果如图:

1094893-20180327165819277-261094675.png

图3-3-1 单元测试结果图

3.4、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渲染结果如下:

1094893-20180327164821878-1667381271.png

图3-4-1 控制台捕获数据图

1094893-20180327164843972-927826178.png

图3-4-2 个性化定制UI渲染图

tips: a、在输入的template中,请将DOM元素设置display:none,防止页面抖动~~
b、在个性化渲染中,需要哪些时间,在模版中制定即可;出现在模版中的占位符,都将参与计算,否则不参与。

四、代码如下

code 地址

转载于:https://www.cnblogs.com/hity-tt/p/8658406.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值