VUI创建日志(二)——防抖节流组件的实现

1. 项目实现介绍

vue 项目搭建参考《Webpack4 搭建 Vue 项目》

文档使用 vuepress, 官方文档 vuepress.vuejs.org

发布文档 github pages + gh-page

项目地址 github.com/zxpsuper/vu…

文档地址 zxpsuper.github.io/vui-vue

组件地址 zxpsuper.github.io/vui-vue/com…

处于自我摸索阶段,期待留下您的宝贵意见!

2. Throttle 组件的实现

  1. 首先,写一个防抖节流的通用函数
/**
 * @param {function} func 执行函数
 * @param {number} time 防抖节流时间
 * @param {boolean} isDebounce 是否为防抖组件
 * @param {this} ctx this 的指向
*/
const debounce = (func, time, isDebounce, ctx) => {
    var timer, lastCall, rtn;
    if (isDebounce) {
        rtn = (...params) => {
            if (timer) clearTimeout(timer);
            timer = setTimeout(() => {
                func.apply(ctx, params);
            }, time);
        };
    } else {
        rtn = (...params) => {
            const now = new Date().getTime();
            if (now - lastCall < time && lastCall) return;
            lastCall = now;
            func.apply(ctx, params);
        };
    }
    return rtn;
};
复制代码
  1. 使用抽象组件
export default {
    name: 'Throttle',
    abstract: true,
    props: {
        time: {
            type: Number,
            default: 800,
        },
        events: {
            type: String,
            default: 'click',
        },
        isDebounce: {
            type: Boolean,
            default: false,
        },
    },
    created() {
        this.eventKeys = this.events.split(','); // 分隔事件
        this.originMap = {}; // 储存事件,用于重新render时与子事件的对比
        this.debouncedMap = {}; // 储存防抖节流事件
    },
    render() {
        const vnode = this.$slots.default[0];
        this.eventKeys.forEach(key => {
            const target = vnode.data.on[key];
            if (target === this.originMap[key] && this.debouncedMap[key]) {
                vnode.data.on[key] = this.debouncedMap[key];
            } else if (target) {
                this.originMap[key] = target;
                this.debouncedMap[key] = debounce(
                    target,
                    this.time,
                    this.isDebounce,
                    vnode
                );
                vnode.data.on[key] = this.debouncedMap[key]; // 重写子组件的事件
            }
        });
        return vnode;
    },
};

复制代码

3. 使用组件

需全局或者组件内注册一下,这里只展示全局注册代码:

Vue.component("Throttle", Throttle);
复制代码

使用方法:

<Throttle :time="5000"  isDebounce>
    <span @click="decounceFunction">
        <Button color="purple" light>防抖按钮</Button>
    </span>
</Throttle>
复制代码

4. 存在问题与解决方法

当页面元素在防抖节流时间内发生了更新(渲染)(可以用定时器修改页面,如页面倒计时),那么此组件会重新执行一遍

this.debouncedMap[key] = debounce(target,this.time,this.isDebounce,vnode);
复制代码

导致防抖节流失效,目前的解决方法是在此组件的子元素添加 v-once,如下:

<Throttle :time="5000"  isDebounce>
    <span @click="decounceFunction" v-once>
        <Button color="purple" light>防抖按钮</Button>
    </span>
</Throttle>
复制代码

可解决此问题,有更好的方法请在评论区留言

5. 完整代码

/*
 * @descript: 防抖节流组件,前提是页面在等待时间内无其他渲染方可使用,重新渲染导致 debounce() 函数不断重置
 * @Author: super
 * @Date: 2019-04-09 14:21:18
 * @Last Modified by: super
 * @Last Modified time: 2019-10-24 16:37:43
 */

const debounce = (func, time, isDebounce, ctx) => {
    var timer, lastCall, rtn;
    if (isDebounce) {
        rtn = (...params) => {
            if (timer) clearTimeout(timer);
            timer = setTimeout(() => {
                func.apply(ctx, params);
            }, time);
        };
    } else {
        rtn = (...params) => {
            const now = new Date().getTime();
            if (now - lastCall < time && lastCall) return;
            lastCall = now;
            func.apply(ctx, params);
        };
    }
    return rtn;
};

export default {
    name: 'Throttle',
    abstract: true,
    props: {
        time: {
            type: Number,
            default: 800,
        },
        events: {
            type: String,
            default: 'click',
        },
        isDebounce: {
            type: Boolean,
            default: false,
        },
    },
    created() {
        this.eventKeys = this.events.split(',');
        this.originMap = {};
        this.debouncedMap = {};
    },
    render() {
        const vnode = this.$slots.default[0];
        this.eventKeys.forEach(key => {
            const target = vnode.data.on[key];
            if (target === this.originMap[key] && this.debouncedMap[key]) {
                vnode.data.on[key] = this.debouncedMap[key];
            } else if (target) {
                this.originMap[key] = target;
                this.debouncedMap[key] = debounce(
                    target,
                    this.time,
                    this.isDebounce,
                    vnode
                );
                vnode.data.on[key] = this.debouncedMap[key];
            }
        });
        return vnode;
    },
};

复制代码

总结

本文是对render及抽象组件的使用总结,若有错误,望指出共同进步。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏csxiaoyao

团队技术文档构建利器vuepress上手实践

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 ...

10120
来自专栏雪胖纸的玩蛇日常

14.非父子组件之间传值

7220
来自专栏Python绿色通道

爬虫篇 | 200 行代码实现一个滑动验证码

做网络爬虫的同学肯定见过各种各样的验证码,比较高级的有滑动、点选等样式,看起来好像挺复杂的,但实际上它们的核心原理还是还是很清晰的,本文章大致说明下这些验证码的...

11020
来自专栏DevOps持续集成

vue学习篇-以优雅的姿势创建vue项目

小白一枚,今年(2019)准备学习一下前端的技术,因为发现自己对后端(python)相对比较熟悉但是还是写不出一个优雅的系统,可见前端的重要性,于是静下心学习。...

9110
来自专栏一番码客

vue.js学习(01)

之前尝试用electron做一些项目的时候,因为完全没有前端开发经验,做起来还是有点找不到头绪,思路非常乱,想到什么功能便搜什么,这样导致没有全局观,不找到整体...

7810
来自专栏web秀

uni-app: 如何高效开发?

本文主要是讲解Uni-App开发技巧,如何快速的撸码?如何调试Uni-App?通过本文,让小伙伴们开发出优秀的Uni-App。

58830
来自专栏全栈前端精选

Vue3 中的数据侦测

目前的版本是 Pre-Alpha , 仓库地址: Vue-next, 可以通过 Composition API了解更多新版本的信息, 目前版本单元测试相关情况 ...

8420
来自专栏ccf19881030的博客

七个开源的 Spring Boot 前后端分离项目,一定要收藏!

原文链接:https://blog.csdn.net/u012702547/article/details/10097...

12630
来自专栏sofu456

asp.net

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 ...

5920
来自专栏雪胖纸的玩蛇日常

15.vue中的路由vue-router

npm install vue-router --save / cnpm install vue-router --save

9320

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励