Vue2项目中关于两个倒计时的实现

676 阅读1分钟

关于在Vue中写倒计时,可参考大佬写的:juejin.cn/post/703840…

为什么要用setTimeout来模拟setInterval的行为

定时器指定的时间间隔,表示的是何时将定时器的代码添加到消息队列,而不是何时执行代码。所以真正何时执行代码的时间是不能保证的,取决于何时被主线程的事件循环取到,并执行。

也就是说,每个setTimeout产生的任务会直接push到任务队列中;而setInterval在每次把任务push到任务队列前,都要进行一下判断(看上次的任务是否仍在队列中)

1.发送验证码后60秒倒计时

我们可以采用setTimeout配合侦听属性来实现获取验证码之后的60秒倒计时:

    data(){
        return{
            ...
            // 获取验证码按钮是否禁用
            getCodeBtn:false,
            // n秒倒计时
            time:0,
            // 定时器
            timer:null
            ...
       }
    },
    watch:{
        time(){ // 监视time如果变化就调用-1函数
            this.countDown(this.time)
        }
    },
    methods: {
        //获取验证码
        async getCode(){
            ...
                try {
                    await this.$store.dispatch('getCode',mobile) // 让仓库派发getCode发送请求
                    this.getCodeBtn = !this.getCodeBtn // 禁用按钮
                    this.time = 60 // 设置定时时间
                    this.countDown(this.time) // 调用倒计时函数
                } catch (error) {
                    this.$message.error(`${error}`)
                }
            ...
        },
        // 时间-1
        countDown(time){ // 传入时间
            if(time <= 0) { // <=0 时完成计时,解禁按钮,清除定时器
                this.getCodeBtn = !this.getCodeBtn
                clearTimeout(this.timer)
                return
            }
            this.timer = setTimeout(()=>{
                this.time = time - 1 // 每次调用对this.time进行-1并重新赋值,触发watch
            },1000)
        },
        ...

效果如下:

20221231_152026Edit.gif

2.未支付订单的30分钟倒计时

当然,我们也可以不采用watch属性进行监视,直接用定时器,配合递归调用的方式,实现倒计时,具体操作如下:

在对应的订单页面中,order.vue

    ...
    data() {
        return {
            // 剩余分钟
            m:'',
            // 剩余秒钟
            s:'',
            ...
        };
    },
    computed:{
        ...mapState({
            order: state => state.trade.orderInfo // 订单详情
        }),
    },
    methods: {
        // 30分钟倒计时
        countdown () {
            let end = new Date(this.order.createTime).getTime();// createTime是后台返的数据,代表创建订单的那一个时刻的毫秒数
            let now = new Date().getTime();  // 获取当前时间的毫秒数
            let minus = now - end; // 现在和下单时刻的毫秒差
            let m_30 = 30 * 60 * 1000; // 30分钟毫秒数
            let differ = m_30 - minus;   // 时间差
            let m = parseInt(differ / 1000 / 60 % 60); // 求出分钟数
            let s = parseInt(differ / 1000 % 60); // 求出秒数
            this.m = m > 9 ? m : '0' + m;
            this.s = s > 9 ? s : '0' + s;
            let _this = this; // 注意保存this
            if(m >= 0 && s >= 0) {
                if(m == 0 && s == 0) {
                    // 倒计时,派发action结束关闭订单
                    this.$store.dispatch('reqOrderInfo',this.$route.params.orderId);
                    return;
                }
                setTimeout(function () {
                    _this.countdown(); // 还没结束就递归调用
                }, 1000)
            }
        },
            ...
    },
    mounted(){
        // 派发获取订单详情
        this.$store.dispatch('reqOrderInfo',this.$route.params.orderId).then(()=>{
            this.countdown();
        })
    },
    ...

效果如下:

20221231_152308Edit 00_00_00-00_00_30.gif