Поле watch и режим наблюдения

Поле watch управляет поведением Rollup в режиме наблюдения. Этот режим позволяет автоматически пересобирать проект при изменении файлов исходного кода, конфигурации, шаблонов, ресурсов и других зависимостей. На практике watch используется во время разработки, когда необходимо мгновенно получать обновлённую сборку без ручного запуска команды.

Режим наблюдения активируется через CLI:

rollup -c -w

или:

rollup --config --watch

После запуска Rollup отслеживает изменения файловой системы и инициирует повторную сборку при каждом изменении зависимостей.

Базовая конфигурация:

export default {
    input: 'src/main.js',

    output: {
        file: 'dist/bundle.js',
        format: 'esm'
    },

    watch: {
        clearScreen: false
    }
};

Как работает режим наблюдения

После запуска Rollup выполняет несколько этапов:

  1. Загружает конфигурацию.
  2. Строит граф модулей.
  3. Запускает плагины.
  4. Выполняет первую сборку.
  5. Подписывается на изменения файлов.
  6. При изменении зависимостей запускает новую сборку.

Rollup отслеживает:

  • входные файлы;
  • импортируемые модули;
  • виртуальные зависимости плагинов;
  • файлы, зарегистрированные через this.addWatchFile().

При изменении хотя бы одного отслеживаемого ресурса начинается повторная сборка.


Структура поля watch

Поле watch представляет собой объект с параметрами режима наблюдения:

watch: {
    clearScreen: true,
    include: 'src/**',
    exclude: 'node_modules/**'
}

Наиболее важные свойства:

Поле Назначение
clearScreen Очистка терминала перед сборкой
include Маска отслеживаемых файлов
exclude Исключение файлов из наблюдения
buildDelay Задержка перед пересборкой
skipWrite Отключение записи файлов
chokidar Настройки файлового наблюдателя

Поле clearScreen

Очистка терминала

По умолчанию Rollup очищает терминал перед каждой новой сборкой. Это позволяет отображать только актуальные сообщения.

Стандартное поведение:

watch: {
    clearScreen: true
}

Отключение очистки:

watch: {
    clearScreen: false
}

Полезно в случаях:

  • анализа истории сборок;
  • отладки плагинов;
  • просмотра последовательности ошибок;
  • логирования времени сборки.

Поле include

Ограничение отслеживаемых файлов

Свойство include определяет, какие файлы должны участвовать в наблюдении.

Пример:

watch: {
    include: 'src/**'
}

Rollup будет реагировать только на изменения внутри каталога src.

Допустимы массивы:

watch: {
    include: [
        'src/**',
        'templates/**'
    ]
}

Использование glob-масок

include поддерживает glob-шаблоны.

Примеры:

watch: {
    include: '**/*.js'
}
watch: {
    include: 'src/**/*.ts'
}
watch: {
    include: [
        'src/**/*.js',
        'src/**/*.vue'
    ]
}

Практическое применение

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

Особенно важно:

  • в монорепозиториях;
  • в больших проектах;
  • при большом количестве статических ресурсов;
  • в Docker-контейнерах;
  • в сетевых файловых системах.

Поле exclude

Исключение файлов из наблюдения

Свойство exclude позволяет исключить файлы и каталоги.

Пример:

watch: {
    exclude: 'node_modules/**'
}

Исключение нескольких каталогов:

watch: {
    exclude: [
        'node_modules/**',
        'dist/**',
        'coverage/**'
    ]
}

Причины исключения директорий

Наиболее часто исключаются:

Каталог Причина
node_modules Огромное количество файлов
dist Исключение циклических пересборок
coverage Временные файлы тестов
.git Системные данные Git
tmp Временные артефакты

Исключение файлов сборки

Очень важно исключать директорию вывода:

watch: {
    exclude: 'dist/**'
}

Без этого возможна ситуация:

  1. Rollup создаёт файл.
  2. Watch обнаруживает изменение.
  3. Запускается новая сборка.
  4. Rollup снова пишет файл.
  5. Возникает бесконечный цикл.

Поле buildDelay

Задержка перед пересборкой

buildDelay задаёт задержку перед запуском новой сборки.

Пример:

watch: {
    buildDelay: 100
}

Значение указывается в миллисекундах.


Для чего используется задержка

Некоторые редакторы сохраняют файлы в несколько этапов:

  1. удаление файла;
  2. создание временного файла;
  3. переименование;
  4. запись содержимого.

Без задержки Rollup может запускать несколько пересборок подряд.

buildDelay сглаживает подобные ситуации.


Работа с генераторами файлов

Задержка полезна при использовании:

  • Sass;
  • Less;
  • TypeScript;
  • шаблонизаторов;
  • генераторов ресурсов;
  • CMS-систем.

Пример:

watch: {
    buildDelay: 300
}

Поле skipWrite

Сборка без записи файлов

skipWrite запрещает Rollup записывать результат на диск.

Пример:

watch: {
    skipWrite: true
}

В этом режиме:

  • сборка выполняется;
  • плагины работают;
  • чанки генерируются;
  • файлы не сохраняются.

Где применяется skipWrite

Чаще всего:

  • в dev-серверах;
  • при интеграции с middleware;
  • в тестовых окружениях;
  • в виртуальных файловых системах;
  • при работе с HMR.

Интеграция с Node.js API

Пример:

import { watch } from 'rollup';

const watcher = watch({
    input: 'src/main.js',

    output: {
        dir: 'dist',
        format: 'esm'
    },

    watch: {
        skipWrite: true
    }
});

Поле chokidar

Настройки файлового наблюдателя

Rollup использует библиотеку Chokidar для отслеживания изменений файлов.

Поле chokidar позволяет передавать параметры напрямую наблюдателю.

Пример:

watch: {
    chokidar: {
        usePolling: true
    }
}

Проблемы файлового наблюдения

В некоторых окружениях стандартное отслеживание работает нестабильно:

  • Docker;
  • WSL;
  • Vagrant;
  • NFS;
  • SMB;
  • виртуальные машины;
  • удалённые контейнеры.

В таких случаях используется polling.


Использование polling

watch: {
    chokidar: {
        usePolling: true,
        interval: 100
    }
}

Rollup начинает периодически опрашивать файловую систему.

Недостатки:

  • повышенная нагрузка на CPU;
  • большее потребление ресурсов;
  • меньшая скорость реакции.

Преимущество — стабильность.


Интервал опроса

Настройка interval задаёт частоту проверки файлов:

watch: {
    chokidar: {
        interval: 300
    }
}

Чем меньше интервал:

  • быстрее реакция;
  • выше нагрузка.

Чем больше интервал:

  • ниже нагрузка;
  • медленнее обнаружение изменений.

Наблюдение через Node.js API

Использование функции watch

Rollup предоставляет программный API для режима наблюдения.

Пример:

import { watch } from 'rollup';

const watcher = watch({
    input: 'src/main.js',

    output: {
        file: 'dist/bundle.js',
        format: 'esm'
    }
});

События watcher

Watcher поддерживает события:

watcher.on('event', event => {
    console.log(event);
});

Основные типы событий

START

Начало работы watcher.

{
    code: 'START'
}

BUNDLE_START

Начало сборки.

{
    code: 'BUNDLE_START',
    input: ['src/main.js'],
    output: ['dist/bundle.js']
}

BUNDLE_END

Успешное завершение сборки.

{
    code: 'BUNDLE_END',
    duration: 245
}

ERROR

Ошибка сборки.

{
    code: 'ERROR',
    error: Error
}

END

Завершение текущего цикла.

{
    code: 'END'
}

Метод addWatchFile

Регистрация дополнительных зависимостей

Плагины могут вручную добавлять файлы в список наблюдения.

Пример:

export default function myPlugin() {
    return {
        name: 'my-plugin',

        buildStart() {
            this.addWatchFile('templates/config.json');
        }
    };
}

При изменении config.json Rollup инициирует новую сборку.


Наблюдение за внешними ресурсами

addWatchFile часто используется для:

  • JSON-конфигураций;
  • шаблонов;
  • Markdown-файлов;
  • YAML;
  • локализаций;
  • CMS-данных;
  • SQL-схем;
  • GraphQL-файлов.

Поведение плагинов в watch-режиме

Повторный запуск хуков

При каждой пересборке многие хуки вызываются повторно:

Hook Повторный вызов
buildStart Да
resolveId Да
load Да
transform Да
generateBundle Да

Некоторые хуки могут вызываться значительно чаще при активной разработке.


Кэширование между сборками

Rollup сохраняет внутренний граф модулей и кэширует результаты трансформаций.

Это ускоряет повторные сборки.

Плагины также могут использовать кэш:

buildStart() {
    const cache = this.cache.get('key');

    if (!cache) {
        this.cache.set('key', data);
    }
}

Инвалидация модулей

Повторная обработка зависимостей

При изменении файла Rollup:

  1. инвалидирует модуль;
  2. пересчитывает зависимости;
  3. обновляет граф;
  4. повторно выполняет трансформации;
  5. генерирует новые чанки.

Rollup старается минимизировать объём повторной работы.


Watch-режим и производительность

Причины медленных пересборок

На скорость влияют:

  • размер графа модулей;
  • количество плагинов;
  • тяжёлые трансформации;
  • TypeScript;
  • Babel;
  • минификация;
  • polling;
  • сетевые диски.

Способы ускорения

Исключение лишних директорий

watch: {
    exclude: [
        'node_modules/**',
        'dist/**'
    ]
}

Уменьшение области наблюдения

watch: {
    include: 'src/**'
}

Отключение минификации

Во время разработки часто отключают Terser:

plugins: process.env.NODE_ENV === 'production'
    ? [terser()]
    : []

Разделение конфигураций

Обычно создаются две конфигурации:

  • development;
  • production.

Пример:

const isDev = process.env.NODE_ENV === 'development';

export default {
    watch: isDev
        ? {
            clearScreen: false
        }
        : undefined
};

Watch-режим и HMR

Отличие watch от Hot Module Replacement

Watch-режим:

  • пересобирает проект;
  • генерирует новые файлы;
  • не обновляет браузер автоматически.

HMR:

  • заменяет модули без перезагрузки страницы;
  • сохраняет состояние приложения;
  • работает через dev-сервер.

Интеграция с dev-серверами

Rollup watch часто используется вместе с:

  • Vite;
  • Browsersync;
  • live-server;
  • webpack-dev-server;
  • Express middleware.

Использование нескольких watcher

Параллельное наблюдение

В больших проектах возможно создание нескольких watcher.

Пример:

import { watch } from 'rollup';

const jsWatcher = watch(jsConfig);
const cssWatcher = watch(cssConfig);

Разделение ответственности

Отдельные watcher могут обслуживать:

Watcher Назначение
JS Javascript
CSS Стили
SSR Серверная сборка
Legacy Старые браузеры
Types Генерация типов

Типичные ошибки

Бесконечная пересборка

Причина:

  • наблюдение за директорией вывода;
  • изменение файлов самим плагином;
  • генерация временных файлов внутри src.

Потеря изменений

Иногда watcher не видит изменения:

  • на сетевых дисках;
  • внутри Docker;
  • в WSL;
  • в виртуальных машинах.

Решение:

watch: {
    chokidar: {
        usePolling: true
    }
}

Избыточное потребление CPU

Причины:

  • слишком маленький polling interval;
  • большое количество файлов;
  • отсутствие exclude;
  • тяжёлые плагины.

Практическая конфигурация для разработки

export default {
    input: 'src/main.js',

    output: {
        dir: 'dist',
        format: 'esm',
        sourcemap: true
    },

    watch: {
        clearScreen: false,

        include: 'src/**',

        exclude: [
            'node_modules/**',
            'dist/**'
        ],

        buildDelay: 100,

        chokidar: {
            usePolling: false
        }
    }
};

Такая конфигурация обеспечивает:

  • стабильное наблюдение;
  • быстрые пересборки;
  • защиту от циклических изменений;
  • удобную отладку;
  • оптимальную работу в режиме разработки.