Проблема 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.
| Вход | Что меняется | Признак устаревания | Контроль |
|---|---|---|---|
| Lockfile | версии и граф зависимостей | новый package version | hash lockfile |
| Config | plugins, target, aliases | другой output | нормализованный config digest |
| Runtime | Node, 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.
Действия по порядку
- Выписать все поля, которые меняют dependency graph, transform, target или состав output.
- Нормализовать значения и собрать deterministic key; исключить случайные и абсолютные поля.
- Проверить hit и miss на изменении lockfile, конфигурации, runtime и одного исходного модуля.
- Сохранить key, cache location, режим cold/warm и причину invalidation в техническом отчёте.
- Проверить содержимое 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 в другом проекте.