灵感来源
在项目开发的过程中 测试提出了一个优化问题**'每点击一次就会弹出一条提示语'(what fuck?难道不该点一次就出一条提示语吗?)**好吧 我是个职业素质很高的程序员,所以第一时间想到了用防抖函数来解决.
什么是防抖?
首先用一个最简单的例子介绍一下什么是防抖,玩过LOL的朋友都应该知道,英雄在释放技能时只会有前摇的,所以这个技能前摇就可以称为防抖.假想一下 如果一个英雄的技能没有任何前摇 那其他玩家是不是没有任何办法克制他 预判他.所以在编程中我们可以写一个防抖函数 防止某些213用户一直点 造成提示无限出现.
防抖函数
可以直接复制代码到F12 console中尝试
function debounce(fn, time) {
//接收一个函数和延迟时间
let timer = null //初始化
return function(...args) { //返回函数接收参数
if (timer)clearTimeout(timer) //如果timer有值那就清空它 再重新计时
timer = setTimeout(() => { //开始计时 两秒后进行函数
fn.apply(this, args) //将return的函数this指向实际调用者(本文中为func)
}, time)
}
}
let func=debounce(()=>{console.log('1')},1000)
func()
什么是节流?
再用一个最简单的例子介绍一下,节流就是技能CD,当你释放一个技能必须再过N秒之后才能释放.在程序中一般用在对某些特殊事件的监听后的DOM操作上.如:滚动条 鼠标移动事件.
节流函数
function throttle(fn, time) { //接收一个函数和延迟时间
let pre = 0 //初始化
return function(...args) { //返回函数接收参数
let now = +new Date() //定义一个变量接收最新的时间搓
if (now - pre > time) { //判断如果最新时间减去初始化时间大于延迟时间就执行
fn.apply(this, args) //将return的函数this指向实际调用者(本文中为func)
pre = now //将开始时间赋值为上一次的结束时间
}
}
}
组件想法的来源
函数都写好了,但是我突然想到一个项目里有这么多点击事件需要我去做防抖,节流操作,就算我把函数封装在一个JS里 光在每个页面中导入也很麻烦,有没有渐变的方法呢?我就在百度搜索了一下发现这篇分享的文章.blog.csdn.net/userkang/ar…
这篇文章写得很不错,原理是利用VUE的抽象组件的方法,但是并不能'直接'用在我的项目中,因为我司开发的项目都是基于elementUI框架,所有的事件和方法并不跟原生一样存放在vnode.data.on中 elementUI的事件存放在vnode.componentOptions.listeners中,所以我对这个组件进行了二次封装,将其适配HTML原生的标签和elementUI的标签.
所以我做了以下改动
-
适配原生以及elementUI标签
-
可自由选择组件功能 防抖or节流 适应不同的业务需求
import Vue from 'vue' const debounce = (func, time, ctx, immediate) => { let timer const rtn = (...params) => { clearTimeout(timer) if (immediate) { let callNow = !timer console.log(timer) timer = setTimeout(() => { timer = null }, time) if (callNow) func.apply(ctx, params) } else { timer = setTimeout(() => { func.apply(ctx, params) }, time) } } return rtn } const throttle = (func, time, ctx, immedidate) => { let pre if (immedidate) { pre = 0 } else { pre = +new Date() } return function(args) { let now = +new Date() if (now - pre > time) { func.apply(ctx, args) pre = now } } } Vue.component('Debounce', { abstract: true, props: ['time', 'events', 'immediate', 'way', 'proto'], created() { this.eventKeys = this.events && this.events.split(',') this.types = this.way || '防抖' this.prototype = this.proto || 'element' }, render() { const vnode = this.$slots.default[0] // 如果默认没有传 events,则对所有绑定事件加上防抖 if (!this.eventKeys) { this.prototype == 'element' ? this.eventKeys = Object.keys(vnode.componentOptions.listeners) : this.eventKeys = Object.keys(vnode.data.on) } if (this.types == '防抖') { if (this.prototype == 'element') { this.eventKeys.forEach(key => { vnode.componentOptions.listeners[key] = debounce( vnode.componentOptions.listeners[key], this.time, vnode, this.immediate ) }) } else { this.eventKeys.forEach(key => { vnode.data.on[key] = debounce( vnode.data.on[key], this.time, vnode, this.immediate ) }) } } else { if (this.prototype == 'element') { this.eventKeys.forEach(key => { vnode.componentOptions.listeners[key] = throttle( vnode.componentOptions.listeners[key], this.time, vnode, this.immediate ) }) } else { this.eventKeys.forEach(key => { vnode.data.on[key] = throttle( vnode.data.on[key], this.time, vnode, this.immediate ) }) } } return vnode } })import '@/common/debounce' // 导入防抖节流组件 /** * @param {原生还是element,默认为element且只要值不是element皆为原生} proto * @param {需要进行防抖的事件名,默认为所有} event * @param {延迟时间} time * @param {选择防抖节流,默认为防抖且只要值不是防抖皆为节流} way * PS 为什么way的方法属性值 不用英文?我怕公司同事不会拼debounce 所以还是写中文吧 * @param {是否立即执行} immediate */ //element用法 <Debounce event='click' :time='500' :immediate='true' way='防抖' proto='element'> <el-button @click="submitCheck()" >确认提交</el-button> </Debounce> //原生用法 <Debounce event='click' :time='500' :immediate='true' way='节流' proto='html'> <button @click="submitCheck()" >确认提交</button> </Debounce>```