函数定义
lodash 中的节流函数throttle
和防抖函数debounce
官方文档是这样定义的:
_.throttle(func, [wait=0], [options=])
参数
func (Function): 要节流的函数。
[wait=0] (number): 需要节流的毫秒。
[options=] (Object): 选项对象。
[options.leading=true] (boolean): 指定调用在节流开始前。
[options.trailing=true] (boolean): 指定调用在节流结束后。
返回
(Function): 返回节流的函数。
_.debounce(func, [wait=0], [options=])
参数
func (Function): 要防抖动的函数。
[wait=0] (number): 需要延迟的毫秒数。
[options=] (Object): 选项对象。
[options.leading=false] (boolean): 指定在延迟开始前调用。
[options.maxWait] (number): 设置 func 允许被延迟的最大值。
[options.trailing=true] (boolean): 指定在延迟结束后调用。
返回
(Function): 返回新的 debounced(防抖动)函数。
现实需求
把一个带参数的函数转化为节流和防抖版本的,如何保证传参可行。在使用Vue开发时,如何转换节流和防抖函数。现有的情形描述一下就是,有个button的click
事件,目前click
事件的回调函数是带参数的,而且参数不能省,如何将这个回调函数转换为节流或者防抖版本的。
<el-button @click="handleOnSale(scope.row)"/>
async handleOnSale(row) {
if (row.isOnShelf === 0) {
try {
await this.$confirm('确定要上架吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
} catch (e) {
console.log(e)
return
}
try {
const topic = Object.assign({}, { isOnShelf: 1, topicKey: row.topicKey })
const res = await this.$api.knowledge.updateTopic(topic)
if (res && res.code === 0) {
this.$message({
message: '上架成功',
type: 'success'
})
setTimeout(() => {
this.pagination.current = 1
this.tableReload(this.formSearch)
}, 2000)
}
} catch (e) {
console.log(e)
}
}
if (row.isOnShelf === 1) {
try {
await this.$confirm('确定要下架吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
} catch (e) {
console.log(e)
return
}
try {
const topic = Object.assign({}, { isOnShelf: 0, topicKey: row.topicKey })
const res = await this.$api.knowledge.updateTopic(topic)
if (res && res.code === 0) {
this.$message({
message: '下架成功',
type: 'success'
})
setTimeout(() => {
this.pagination.current = 1
this.tableReload(this.formSearch)
}, 2000)
}
} catch (e) {
console.log(e)
}
}
},
理解节流和防抖函数的使用
要想使用节流或者防抖函数,需要传递一个纯函数,即不带参数的函数(其实是不带括号的函数),这里需要指出带参和不带参(不带括号)的区别:
function foo(){}
function bar(a,b){}
console.log(typeof foo) //function
console.log(typeof bar) //function
console.log(typeof bar(1,2)) //undefined
通过typeof
运算,可以看出,如果一个函数是带参的,会判断为undefined
,原理是函数本质是一个地址,函数调用是一个返回结果,如果一个函数带参(其实就是带括号了,参数没有都可以)了,会解释为函数调用,如果函数没有返回值,typeof
就是undefined
,如果有返回值,typeof
就是返回值的类型
function bar(a,b){return false}
console.log(typeof bar(1,2)) //boolean
那如果函数必须要带参数,如何使用节流和防抖呢?
其实,只要传递函数名称就可以了,不要带参(带括号)就行了。譬如:
function bar(a,b){return false}
let throttleBar = _.throttle(bar,2000,{leading:true,trailing:false})
let debounceBar = _.debounce(bar,2000,{leading:true,trailing:false})
这样throttleBar
、debounceBar
就是节流和防抖版本的bar函数了,调用时,
throttleBar(1,2)
debounceBar(1,2)
即可
Vue中如何对绑定的事件回调函数转换为节流或者防抖
正如我们现实情况遇到的,绑定的click
事件回调函数,里面涉及异步操作,特别的我们使用了async
语法,那么回调函数本质是一个promise function
而不是function
,而 _.throttle(func, [wait=0], [options=])、 _.debounce(func, [wait=0], [options=])都要求入参func
为function
,所以需要将promise function
类型转换为function
类型,为此可以想到这样做:
handleOnSaleFun(row) {
this.handleOnSale(row)
},
另外,节流和防抖函数转换,需要提前转换,也就是在绑定回调函数时,回调函数需要提前转换为节流和防抖版本,否则节流和防抖不会生效。
探索1:
<el-button @click="_.debounce(handleOnSaleFun, 2000, { leading: true, trailing: false })"/>
这样的方式不行,因为参数必须要传递
探索2:
<el-button @click="_.debounce(handleOnSaleFun, 2000, { leading: true, trailing: false })(scope.row)"/>
这样也不行,事件绑定回调也是纯函数,不是函数调用
探索3:
<el-button @click="debounceHandleOnSaleFun(scope.row)"/>
computed: {
debounceHandleOnSaleFun() {
return _.debounce(this.handleOnSaleFun, 2000, { leading: true, trailing: false })
}
},
利用计算属性得到一个防抖或者节流版本的函数,这样的函数其实就是纯函数,在事件的回调函数中,传递参数不会解释为函数调用,而是带参的函数,这样就实现了带参函数的防抖和节流。