Содержание
Коротко
После деплоя SPA пользователь с открытой вкладкой может увидеть пустой экран: React.lazy() тянет chunk со старым хешем, а на CDN уже новый бандл. На Dev.to — короткий глобальный обработчик, который перезагружает страницу с параметром сброса кеша.
Что произошло
Классический сценарий: в памяти браузера остаётся старый index.html, при переходе по маршруту динамический импорт бьётся в 404 или сетевую ошибку. В консоли — варианты вроде Failed to fetch dynamically imported module или Loading chunk N failed (формулировки различаются в Chrome, Safari и старых сборках Webpack).
Обычный пользователь не знает про жёсткое обновление — он просто уходит.
Почему это важно
Code splitting экономит первый байт, но связывает версию оболочки и версию чанков. Любой деплой без стратегии кеша HTML создаёт окно, где часть сессий «застревает» между релизами.
| Симптом | Причина | Без фикса |
|---|---|---|
| Белый экран после навигации | Chunk удалён/переименован | Потеря конверсии |
| Ошибка только у части пользователей | Старые вкладки | Сложно воспроизвести в support |
| Hard reload помогает | Новый index.html с актуальными хешами |
Не масштабируется |
Пятнадцать строк обработчика дешевле, чем потерянные сессии после каждого релиза.
На практике
- Повесьте
window.addEventListener('error', …)с набором regex под разные тексты ошибок (chunk, dynamic import, CSS chunk, Safari «Importing a module script failed»). - При совпадении —
location.replace()с?_r=<timestamp>, чтобы подтянуть свежий HTML без лишней записи в history (кнопка «Назад» работает). - Добавьте флаг «уже перезагружали», чтобы не уйти в цикл при реальном обрыве сети.
- При необходимости дублируйте логику на
unhandledrejection, если lazy-импорт падает внутри promise. - Проверка: задеплойте новый билд, оставьте старую вкладку, перейдите на lazy-маршрут — страница должна сама восстановиться.
Долгосрочно дополняйте политикой кеша: короткий TTL или no-cache для index.html, длинный — для hashed assets.
Итог
React.lazy + частые деплои без recovery — предсказуемый инцидент. Глобальный listener не заменяет правильный кеш HTML, но страхует живые вкладки в момент выката.