这是个老生常谈的话题了,之因此还搬出来说讲,缘由之一是以前根本就没在乎,近期面临的一些问题须要用到这两个小技巧;缘由之二,这两个技巧带来的优化不小;缘由之三,顺便复习一下闭包。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));
复制代码