предисловие
Это тема старой поговорки, и причина в том, что одна из причин в том, что нет больше беспокойства раньше. Некоторые проблемы, с которыми сталкиваются в ближайшем будущем, необходимо использовать эти два совета; причина 2, эти два навыка оптимизации не мал; причина три, а ближе рассмотрено кстати.
В процессе разработки вы можете столкнуться со следующими ситуациями:
- монитор
Window
объектresize
,scroll
мероприятие - Слушайте во время перетаскивания
mousemove
- При вводе текста входная строка обрабатывается, например, для
markdwon
Перевести вhtml
- Отслеживайте изменения файлов и перезапускайте службу
В первом и третьем случаях событие часто срабатывает в течение короткого периода времени.Если в событии присутствует большое количество вычислений, частые манипуляции с DOM, загрузка ресурсов и другие тяжелые действия, это может привести к зависанию пользовательского интерфейса, или даже заставить браузер зависнуть в тяжелых случаях. В четвертом случае некоторые разработчики любят сохранять отредактированные файлы, нажимая несколько разCtrl+S
, если вы перезапустите службу быстро, вы все равно можете ее удерживать, но если вы перезапустите приложение, оно может перезапуститься без необходимости много раз.
Ввиду вышеуказанного ряда потребностей существуетdebounce
а такжеthrottle
Два решения.
Функция дроссельной заслонки
Функция выполняется за один цикл, например, даваяwindow
связать одинresize
После события печатать 1 до тех пор, пока размер окна не изменится.Если функция throttling не используется, то при настройке окна мы обнаружим, что консоль продолжает печатать1
, но после использования функции throttling мы обнаружим, что в процессе настройки она каждый раз только печатается1
.
Простая реализация функции дросселирования:
/**
*
* @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);
}
};
}
Используемый метод очень прост:
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.
Простая реализация функции debounce:
/**
* @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));