這是個老生常談的話題了,之因此還搬出來說講,緣由之一是以前根本就沒在乎,近期面臨的一些問題須要用到這兩個小技巧;緣由之二,這兩個技巧帶來的優化不小;緣由之三,順便複習一下閉包。html
開發中你可能會遇到下面的狀況:react
Window
對象的resize
,scroll
事件mousemove
markdwon
轉換成html
第一種和第三種狀況,事件短期內被頻繁出發,若是在事件中有大量的計算,頻繁操做DOM,資源加載等重行爲,可能會致使UI卡頓,嚴重點甚至讓瀏覽器掛掉。對於第四種狀況,有的開發者保存編輯好的文件喜歡按屢次Ctrl+S
,如果快速的重啓服務還能Hold住,可是要是重啓一個應用,就可能屢次沒必要要的重啓。npm
針對上面這一系列的需求,因而有了debounce
和throttle
兩種解決辦法。瀏覽器
函數按照一個週期執行,例如給window
綁定一個resize
事件以後,只要窗口改變大小改變就打印1,若是不採用函數節流,當咱們將窗口調節的時候發現控制檯一直打印1
,可是使用了函數節流後咱們會發現調節的過程當中,每隔一段時間纔打印1
。閉包
一個函數節流的簡單實現:app
/** * * @param func {Function} 實際要執行的函數 * @param wait {Number} 執行間隔,單位是毫秒(ms),默認100ms * * @return {Function} 返回一個「節流」函數 */
function throttle(func, wait = 100) {
// 利用閉包保存定時器和上次執行時間
let timer = null;
let previous; // 上次執行時間
return function() {
// 保存函數調用時的上下文和參數,傳遞給 fn
const context = this;
const args = arguments;
const now = +new Date();
if (previous && now < previous + wait) { // 週期之中
clearTimeout(timer);
timer = setTimeout(function() {
previous = now;
func.apply(context, args);
}, wait);
} else {
previous = now;
func.apply(context, args);
}
};
}
複製代碼
使用的方法也很簡單:electron
const btn = document.getElementById('btn');
function demo() {
console.log('click');
}
btn.addEventListener('click', throttle(demo, 1000));
複製代碼
看看React中怎麼使用的,下面監聽窗口的resize
和輸入框的onChange
事件:函數
import React, { Component } from 'react';
import { throttle } from '../../utils/utils';
export default class Demo extends Component {
constructor() {
super();
this.change = throttle((e) => {
console.log(e.target.value);
console.log('throttle');
}, 100);
}
componentDidMount() {
window.addEventListener('resize', throttle(this.onWindowResize, 60));
}
componentWillUnmount() {
window.removeEventListener('resize', throttle(this.onWindowResize, 60));
}
onWindowResize = () => {
console.log('resize');
}
handleChange = (e) => {
e.persist();
this.change(e);
}
render() {
return (
<input onChange={this.handleChange} /> ); } } 複製代碼
當事件觸發以後,必須等待某一個時間(N)以後,回調函數纔會執行,倘若再等待的時間內,事件又觸發了則從新再等待時間N,直到事件N內事件不被觸發,那麼最後一次觸發過了事件N後,執行函數。優化
仍是窗口resize
,若是一直改變窗口大小,則不會打印1,只有中止改變窗口大小並等待一段時間後,纔會打印1。ui
函數去抖簡單實現:
/** * @param func {Function} 實際要執行的函數 * @param delay {Number} 延遲時間,單位是毫秒(ms) * @return {Function} */
function debounce(fn, delay = 1000) {
let timer;
// 返回一個函數,這個函數會在一個時間區間結束後的 delay 毫秒時執行 func 函數
return function () {
// 保存函數調用時的上下文和參數,傳遞給func
var context = this
var args = arguments
// 函數被調用,清除定時器
clearTimeout(timer)
// 當返回的函數被最後一次調用後(也就是用戶中止了某個連續的操做),
// 再過 delay 毫秒就執行 func
timer = setTimeout(function () {
fn.apply(context, args);
}, delay);
}
}
複製代碼
應用場景,監聽文件變化,重啓應用:
const debounce = require('./debounce');
watcher.on('change', debounce(() => {
const child = spawn('npm', ['run', 'dev:electron'], {
cwd,
detached: true,
stdio: 'inherit'
})
child.unref();
electron.app.quit();
}, delay));
複製代碼