Хук watchChange вызывается в режиме
rollup --watch при изменении файлов, за которыми наблюдает
сборщик. Он является частью плагинной системы Rollup и позволяет
перехватывать события файловой системы до того, как произойдёт повторная
сборка. Основная задача этого хука — дать плагину возможность
отреагировать на изменения исходного кода, сбросить внутренние кеши,
инициировать побочные эффекты или подготовить данные к следующей
компиляции.
Сигнатура хука:
watchChange(id, change)
id — абсолютный путь к изменённому файлу
change — объект с метаданными изменения, обычно содержащий
тип события (create, update,
delete) и дополнительные сведения в зависимости от
окружения
Хук относится к категории watcher hooks и не влияет напрямую на граф модулей, но может косвенно воздействовать на него через кеширование и invalidate-логику.
При активированном режиме наблюдения Rollup использует файловую систему или внешние watch-сервисы для отслеживания изменений. Когда происходит событие изменения файла, последовательность выглядит следующим образом:
watchChange у всех подключённых
плагиновВажно, что watchChange вызывается до пересборки, что
делает его пригодным для подготовки состояния перед новым циклом
сборки.
Основные применения хука связаны с управлением состоянием плагина:
Пример базового использования:
export default function myPlugin() {
const cache = new Map();
return {
name: 'my-plugin',
watchChange(id, change) {
cache.delete(id);
if (change.event === 'delete') {
cache.delete(id);
}
}
};
}
В данном примере кеш сбрасывается при любом изменении файла, что гарантирует актуальность данных при следующей сборке.
Хук имеет несколько важных особенностей, влияющих на архитектуру плагинов:
Асинхронный вариант:
export default function plugin() {
return {
name: 'async-watch',
async watchChange(id, change) {
await someExternalSync(id, change);
}
};
}
Асинхронность особенно важна при интеграции с файловыми индексами, базами данных или генераторами артефактов.
Хотя Rollup не фиксирует строго единый интерфейс, на практике объект изменения может включать:
event — тип изменения (create,
update, delete)oldPath — предыдущий путь (при переименовании)timestamp — время событияПример обработки:
watchChange(id, change) {
switch (change.event) {
case 'create':
console.log('Файл создан:', id);
break;
case 'upd ate':
console.log('Файл изменён:', id);
break;
case 'delete':
console.log('Файл удалён:', id);
break;
}
}
Хук watchChange не изменяет граф модулей напрямую,
однако может использоваться для косвенного влияния на него через кеши и
invalidate-механизмы.
В типичной архитектуре Rollup:
watchChange используется как предварительный слой
реакцииload,
resolveId, transformПри этом некорректное использование хука может привести к избыточным пересборкам, если плагин инициирует внешние триггеры без фильтрации событий.
При большом количестве файлов watchChange становится
горячей точкой. Основные проблемы:
Типичная оптимизация — дедупликация:
const pending = new Se t();
export default function plugin() {
return {
name: 'optimized-watch',
watchChange(id) {
if (pending.has(id)) return;
pending.add(id);
setTimeout(() => {
pending.delete(id);
}, 50);
}
};
}
Такой подход снижает нагрузку при всплесках событий.
Хук closeWatcher вызывается при завершении работы
watch-режима, когда процесс наблюдения за файлами останавливается. Это
может происходить при ручном завершении процесса, перезапуске сборки или
закрытии CLI-инстанса Rollup.
Сигнатура хука:
closeWatcher()
Хук не принимает аргументов и предназначен исключительно для финализации ресурсов.
closeWatcher является завершающей точкой
watcher-пайплайна. Его основная задача — корректное освобождение
ресурсов, которые были созданы в процессе наблюдения.
Типичный порядок завершения:
closeWatcher у всех плагиновХук применяется для:
Пример:
export default function plugin() {
let interval;
return {
name: 'resource-plugin',
buildStart() {
interval = setInterval(() => {
// фоновые задачи
}, 1000);
},
closeWatcher() {
clearInterval(interval);
}
};
}
Без очистки подобных ресурсов процесс может зависнуть или продолжить потреблять CPU даже после остановки watch-режима.
Асинхронный вариант:
export default function plugin() {
return {
name: 'async-cleanup',
async closeWatcher() {
await flushLogs();
await closeConnections();
}
};
}
Асинхронность полезна при необходимости корректного завершения внешних сервисов.
Оба хука относятся к жизненному циклу watch-режима, но решают разные задачи:
watchChange — реакция на события файловой системы в
реальном времениcloseWatcher — финализация и освобождение ресурсовОни часто используются совместно:
export default function plugin() {
const state = new Map();
return {
name: 'combined-plugin',
watchChange(id, change) {
state.set(id, change.timestamp);
},
closeWatcher() {
state.clear();
}
};
}
Такая связка обеспечивает контролируемое управление состоянием плагина на протяжении всего жизненного цикла watch-режима.
В сложных плагинах оба хука участвуют в построении реактивной системы:
watchChange как входной поток событийcloseWatcher как гарантированная точка завершенияПример батчинга:
export default function plugin() {
const queue = new Set();
let timer;
function flush() {
queue.clear();
timer = null;
}
return {
name: 'batch-plugin',
watchChange(id) {
queue.add(id);
if (!timer) {
timer = setTimeout(flush, 100);
}
},
closeWatcher() {
if (timer) clearTimeout(timer);
flush();
}
};
}