Debounce и throttle

Debounce и throttle — это два ключевых приёма оптимизации производительности при обработке событий в веб-приложениях. Они позволяют контролировать частоту вызовов функций, что особенно важно при работе с событиями ввода, прокрутки или ресайза, где частота вызова может достигать сотен раз в секунду.


Debounce

Debounce — это техника, при которой функция вызывается только после того, как событие перестало происходить в течение определённого времени. Основная цель — сгладить резкие, часто повторяющиеся вызовы функции и выполнить её только после «паузы».

Принцип работы

  1. Функция сохраняется в замыкании вместе с таймером.
  2. Каждый новый вызов функции сбрасывает таймер.
  3. Функция выполняется только после того, как таймер достигнет заданного интервала без новых вызовов.
function debounce(func, delay) {
  let timer;
  return function(...args) {
    clearTimeout(timer);
    timer = setTimeout(() => func.apply(this, args), delay);
  };
}

// Пример использования:
const handleResize = debounce(() => {
  console.log('Resize завершён');
}, 300);

window.addEventListener('resize', handleResize);

Ключевые моменты debounce:

  • Идеален для событий, которые происходят очень часто, но реакция требуется только один раз после завершения серии событий (например, завершение ввода текста, ресайз окна).
  • Позволяет уменьшить количество вызовов функций и снизить нагрузку на DOM и рендеринг.

Throttle

Throttle ограничивает частоту вызова функции. В отличие от debounce, функция вызывается регулярно, но не чаще установленного интервала времени. Используется, когда важно реагировать на событие с определённой периодичностью, а не только после завершения серии вызовов.

Принцип работы

  1. Создаётся замыкание с переменной времени последнего вызова.
  2. Функция вызывается только если с предыдущего вызова прошло больше заданного интервала.
  3. Дополнительно можно реализовать «leading» и «trailing» вызовы для тонкой настройки поведения.
function throttle(func, limit) {
  let lastCall = 0;
  return function(...args) {
    const now = Date.now();
    if (now - lastCall >= limit) {
      lastCall = now;
      func.apply(this, args);
    }
  };
}

// Пример использования:
const handleScroll = throttle(() => {
  console.log('Scroll событие обработано');
}, 200);

window.addEventListener('scroll', handleScroll);

Ключевые моменты throttle:

  • Подходит для событий, где важна постоянная реакция с ограниченной частотой, например, скролл, движение мыши, анимации.
  • Обеспечивает стабильное обновление состояния без перегрузки браузера.

Различия между debounce и throttle

Параметр Debounce Throttle
Поведение Вызывает функцию после паузы Вызывает функцию с фиксированным интервалом
Идеальное применение Ввод текста, ресайз окна, фильтры Скролл, drag & drop, события мыши
Основной эффект Сглаживание резких повторений Ограничение частоты вызова

Использование debounce и throttle в Naive UI

При работе с библиотекой Naive UI debounce и throttle часто применяются для оптимизации взаимодействия с компонентами ввода и таблицами данных.

Пример: Input с debounce

<template>
  <n-input v-model="searchQuery" @input="debouncedSearch" placeholder="Поиск..." />
</template>

<script setup>
import { ref } from 'vue';
import { debounce } from 'lodash';

const searchQuery = ref('');

const performSearch = (query) => {
  console.log('Поиск по запросу:', query);
};

const debouncedSearch = debounce(() => performSearch(searchQuery.value), 300);
</script>

Здесь debounce позволяет не вызывать функцию поиска при каждом нажатии клавиши, а только после паузы в наборе текста.

Пример: Scroll с throttle

<template>
  <div @scroll="throttledScroll" style="height: 300px; overflow: auto;">
    <n-list>
      <n-list-item v-for="i in 100" :key="i">Элемент {{ i }}</n-list-item>
    </n-list>
  </div>
</template>

<script setup>
import { throttle } from 'lodash';

const handleScroll = (event) => {
  console.log('Позиция скролла:', event.target.scrollTop);
};

const throttledScroll = throttle(handleScroll, 200);
</script>

Использование throttle предотвращает чрезмерное срабатывание обработчика скролла, снижая нагрузку на рендеринг компонентов.


Настройка оптимального поведения

  • Debounce: выбрать интервал delay важно исходя из сценария: для ввода текста достаточно 200–500 мс, для ресайза окна — 300–400 мс.
  • Throttle: интервал limit подбирается по частоте обновления UI: для плавного скролла — 100–200 мс, для drag & drop — 16–50 мс (≈60 FPS).
  • Комбинация debounce + throttle: иногда целесообразно использовать throttle для регулярного обновления состояния и debounce для окончательной обработки данных.

Взаимодействие с компонентами Naive UI

  • Компоненты ввода (n-input, n-select) — часто оборачиваются в debounce для оптимизации поиска или автозаполнения.
  • Компоненты прокрутки и таблицы (n-list, n-table) — используют throttle для обработки событий scroll и resize, чтобы поддерживать плавную производительность.
  • Любой кастомный компонент с частыми событиями может выиграть от этих подходов, что критично при работе с большими списками и динамическим контентом.

Debounce и throttle — базовые инструменты оптимизации производительности веб-приложений, которые в сочетании с Naive UI позволяют создавать отзывчивые, плавные интерфейсы без лишней нагрузки на браузер. Правильное применение этих техник делает взаимодействие с компонентами быстрым и предсказуемым даже при интенсивной работе с DOM.