DarkRiDDeR16 мин

Кэш сборки: ключ определяет, что именно вы повторяете

FrontendСборка

Проблема cache в сборке появляется в двух противоположных видах. Кэш всегда промахивается, и команда считает сборку медленной. Или кэш попадает, но после изменения lockfile, конфигурации или linked package остаётся старый output. Цена одинаково неприятна: либо CI тратит время на повторную работу, либо браузер и разработчик видят результат, который не соответствует исходникам.

Кэш не хранит абстрактное «состояние проекта». Он хранит результат конкретной функции от входов. Если ключ не включает lockfile, релевантную конфигурацию, runtime и исходный граф, система не может понять, что результат устарел. Если ключ включает лишние шумные поля, повторяемость исчезает. Поэтому механизм нужно обсуждать как контракт ключа, значения и правила invalidation.

Что входит в ключ

Vite описывает несколько источников invalidation pre-bundling: lockfile, patches, релевантные поля конфигурации и NODE_ENV. Для linked dependency важен способ разрешения пакета и необходимость повторной оптимизации. Это пример хорошей инженерной границы: в документации названы не только кнопка «force», но и данные, по которым система принимает решение.

В webpack 5 cache может быть memory или filesystem. Эти режимы различаются временем жизни и стоимостью сериализации. Даже корректный ключ не спасёт, если два job используют одну директорию с разными правами или разными версиями Node. Состояние cache нужно видеть в отчёте: hit/miss, key, location, runtime и причина invalidation.

Матрица ключа frontend-кэша: lockfile, конфигурация, runtime и исходный digest образуют вход, а изменение любого слоя инвалидирует результат.
Диаграмма отделяет входы ключа от результата cache. Красная ветка означает, что неполный ключ нельзя считать доказательством корректного повтора.
Состав ключа и последствия пропуска
ВходЧто меняетсяПризнак устареванияКонтроль
Lockfileверсии и граф зависимостейновый package versionhash lockfile
Configplugins, target, aliasesдругой outputнормализованный config digest
RuntimeNode, bundler, platformразное поведение cacheверсия и ABI окружения
Sourceкод и linked packageизменённый модульcommit/source digest
Cache locationобщая или локальная областьчужой результатnamespace и права

Локальный ключ с прозрачными входами

В примере используется SHA-256 и фиксированный порядок четырёх значений. Результат — короткий идентификатор, который можно поместить в имя cache namespace. Учебный код не знает, какие поля нужны конкретному bundler: он показывает главное правило — каждый источник изменения должен иметь явное место в ключе, а delimiter не должен позволять склеить разные наборы в одну строку.

import { makeDependencyCacheKey } from './upgrade-2027-04.mjs';

const base = {
  lockfile: 'lock-v1',
  config: 'target=es2022;minify=true',
  runtime: 'node-24',
  sourceDigest: 'src-001',
};

console.log(makeDependencyCacheKey(base));
console.log(makeDependencyCacheKey({ ...base, lockfile: 'lock-v2' }));
// 16-символьный key
// другой key после изменения lockfile

Ожидаемый результат важнее конкретных hex-значений: замена lockfile меняет key, а повтор одного объекта даёт тот же key. Перед реальным использованием нормализуйте конфигурацию и закрепите кодировку. Не добавляйте timestamp, случайный UUID или абсолютный путь, если они не являются частью результата: такие поля превратят каждый запуск в промах.

Cache hit не измеряет качество результата

Попадание в cache говорит, что найден результат с совпавшим ключом. Оно не говорит, что ключ полный, output опубликован, source map соответствует bundle или браузер получил свежий файл. Для dev-server это особенно заметно: Vite может жёстко кэшировать resolved dependency requests, а локальная правка linked package потребует явного re-bundle. В отчёте отделяйте cache state от проверки содержимого.

Промах тоже не всегда ошибка. Изменение lockfile должно инвалидировать dependency cache. Слишком агрессивное reuse иногда дешевле, чем сложная логика восстановления, если сборка короткая. Решение зависит от стоимости работы и риска устаревшего результата. Назовите обе величины: секунды cache miss и ущерб от неправильного hit.

Действия по порядку

  1. Выписать все поля, которые меняют dependency graph, transform, target или состав output.
  2. Нормализовать значения и собрать deterministic key; исключить случайные и абсолютные поля.
  3. Проверить hit и miss на изменении lockfile, конфигурации, runtime и одного исходного модуля.
  4. Сохранить key, cache location, режим cold/warm и причину invalidation в техническом отчёте.
  5. Проверить содержимое output и source map после hit; одного совпавшего key недостаточно.

Ограничения и следующий шаг

Функция делает hash строки и не знает о том, как bundler нормализует config, разрешает symlink или читает lockfile. Разный порядок полей может дать лишний miss, а забытый plugin — неправильный hit. Кэш файловой системы зависит от прав, диска и версии сериализации. Нельзя переносить key между toolchain без проверки семантики входов.

Следующий шаг — добавить тест invalidation для каждого входа и отдельный тест на source map и output после cache hit. В CI выводите первые символы key, но не секреты и содержимое приватного source. Если причина miss неизвестна, сначала расширьте диагностику ключа, а не включайте постоянный force.

Проверяемые источники

  • Vite Guide: Dependency Pre-Bundling — версия и дата: Vite guide, checked 31 July 2026. Применение: Источники invalidation Vite dependency pre-bundling и поведение force/linked dependency. Граница: Руководство относится к Vite dependency optimizer и не является универсальным контрактом любого bundler.
  • webpack 5 Configuration: cache — версия и дата: webpack 5 configuration reference, checked 31 July 2026. Применение: Режимы memory и filesystem cache webpack 5 используются для различения времени жизни результата. Граница: Справочник не знает cache directory, права и runtime конкретного CI.
  • webpack 5 Guide: Caching — версия и дата: webpack 5 guide, checked 31 July 2026. Применение: Стабильные output names и детерминированные ids используются как пример отделения результата от случайности. Граница: Руководство не доказывает корректность неполного cache key в другом проекте.