在開發(fā)中經(jīng)常會遇到頻繁觸發(fā)的事件
如onScroll铅匹,onChange等,當這些頻繁觸發(fā)的事件和請求相關聯(lián)的時候饺藤,每次觸發(fā)都去發(fā)請求會造成巨大的服務器壓力包斑,比如簡書的實時保存,都是等待onKeyUp事件后500ms左右去執(zhí)行的save操作涕俗。
由此我們需要實現(xiàn)一個防抖函數(shù)罗丰,當頻繁觸發(fā)的事件,停止某個時間后再姑,觸發(fā)函數(shù)
思路:建立一個定時器萌抵,如果觸發(fā)事件,則取消定時器元镀,直到定時器執(zhí)行绍填,循環(huán)往復,這個防抖函數(shù)在我看來栖疑,很像裝飾器讨永,對函數(shù)進行了強化
注意點:針對防抖的操作,需要直接調(diào)用強化后函數(shù)
第一個例子遇革,onMouseMove
當鼠標滑過某個區(qū)域后卿闹,停止滑動后,500ms萝快,計數(shù)器加一
第一版代碼如下
import React from 'react';
import { debounce } from '@/util/plugins'
import './index.scss'; // 樣式隨便寫個矩形區(qū)域就行
class Debounce extends React.Component {
constructor(props) {
super(props)
this.state = {
count: 1,
}
this.onMouseMove = debounce(this.onMouseMove, 500);
}
onMouseMove = () => {
this.setState(prev => ({
count: prev.count + 1
}), () => {
console.log(this.state)
})
}
render() {
return <div className="debounce-wrap" onMouseMove={this.onMouseMove} ></div>
}
}
export default Debounce;
export const debounce = (fn, wait) => {
let timeout = null
return () => {
clearTimeout(timeout)
timeout = setTimeout(()=>{
fn()
}, wait)
}
}
第二份例子锻霎,onChange
上面的例子實現(xiàn)了,滑動停止后500ms揪漩,執(zhí)行計數(shù)器+1操作量窘,這次我們對onChange這個需要掛參數(shù)的例子進行實現(xiàn)
需要實現(xiàn)的效果:input輸入文字后,1s后顯示在頁面上
第二版氢拥,需要把參數(shù)帶過去
import React from 'react';
import { debounce } from '@/util/plugins'
class Debounce extends React.Component {
constructor(props) {
super(props)
this.state = {
text: '',
cloneText: ''
}
this.setCloneText = debounce(this.setCloneText, 1000);
}
onChange = (value) => {
this.setState({text: value}, ()=>{
this.setCloneText(value)
})
}
setCloneText = text => {
this.setState({ cloneText: text })
}
render() {
return <React.Fragment>
<input value={this.state.text} onChange={e => this.onChange(e.target.value)} />
<p>{this.state.cloneText}</p>
</React.Fragment>
}
}
export default Debounce;
export const debounce = (fn, wait) => {
let timeout = null
return (...args) => {
clearTimeout(timeout)
timeout = setTimeout(()=>{
fn(...args)
}, wait)
}
}
小結
在第二版蚌铜,可以通過...
擴展運算符將agrs(數(shù)組)轉(zhuǎn)換成函數(shù)參數(shù),同時我們在日常開發(fā)中一直用的是箭頭函數(shù)嫩海,this指向問題已經(jīng)被解決冬殃,為了兼容低版本語法,所以我們有第三版
// 防抖
export const debounce = (fn, wait) => {
let timeout = null
return (...args) => {
clearTimeout(timeout)
var context = this;
timeout = setTimeout(()=>{
fn.apply(context, args)
}, wait)
}
}
第三份例子叁怪,防止短時間內(nèi)重復點擊
這是在寫RN的時候遇到的問題审葬,在點擊RN頁面的時候,快速多次點擊頁面功能,可能會喚起多個webView涣觉,導致用戶體驗極差痴荐,用戶的本意是只想喚起一起某個頁面,當時我處理的方案是寫了一個高階組件官册,處理這個問題生兆,不過其核心點還是防抖函數(shù)
上面已經(jīng)完成的,是在用戶多次觸發(fā)停止后膝宁,n個時間后觸發(fā)函數(shù)
我們的需求修改為事件直接觸發(fā)鸦难,如果n時間內(nèi)又觸發(fā),則不執(zhí)行员淫,知道n時間后合蔽,變?yōu)榭捎|發(fā)
import React from 'react';
import { debounce } from '@/util/plugins'
import './index.scss';
class Debounce extends React.Component {
constructor(props) {
super(props)
this.state = { }
this.clickBtn = debounce(this.clickBtn, 10000, true)
}
clickBtn = () => {
console.log('click')
}
render() {
return <React.Fragment>
<button onClick={this.clickBtn}>開心按鈕</button>
</React.Fragment>
}
}
export default Debounce;
// 防抖--防止重復觸發(fā)
export const debounce = (fn, wait, immediate) => {
var timeout = null;
return (...args) => {
var context = this;
if(immediate){
let callNow = !timeout;
// 建立計時器,設定timeout后失效
timeout = setTimeout(()=>{timeout = null;}, wait)
// 如果不存在計時器介返,表面可以直接執(zhí)行
if(callNow) fn.apply(context, args)
} else {
clearTimeout(timeout)
timeout = setTimeout(()=>{ fn.apply(context, args) }, wait)
}
}
}
第三份例子強化拴事,解除防抖
還是在RN里遇到的,如果webview已經(jīng)被新開了圣蝎,我們就應該把鎖定的重復點擊關閉刃宵,當然重新觸發(fā)點擊的時候,我們的防抖又會被啟動
import React from 'react';
import { debounce } from '@/util/plugins'
import './index.scss';
class Debounce extends React.Component {
constructor(props) {
super(props)
this.state = { }
this.clickBtn = debounce(this.clickBtn, 10000, true)
}
clickBtn = () => {
console.log('click')
}
render() {
return <React.Fragment>
<button onClick={this.clickBtn}>開心按鈕</button>
<button onClick={()=>{this.clickBtn.cancal()}}>解除按鈕</button>
</React.Fragment>
}
}
export default Debounce;
export const debounce = (fn, wait, immediate) => {
var timeout = null
var debounced = (...args) => {
clearTimeout(timeout)
var context = this;
if(immediate){
// 如果計時器不存在捅彻,就立即執(zhí)行,計數(shù)器在wait秒后解除
let callNow = !timeout
timeout = setTimeout(()=>{
timeout = null
}, wait)
if(callNow){
fn.apply(context, args)
}
}else{
timeout = setTimeout(()=>{
fn.apply(context, args)
}, wait)
}
}
debounced.cancal = () => {
clearTimeout(timeout)
timeout = null
}
return debounced
}