跪拜 Guibai
← All articles
Frontend

The Blank Screen After Every Deploy Is Not a Bug—It's Your Architecture

By Liora_Yvonne ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Every team that ships a JavaScript SPA hits this problem, yet Vue and React provide zero built-in defenses. A layered strategy—correct caching, version polling, and chunk-error recovery—turns a guaranteed support headache into a solved problem, and the LYStack reference implementation shows how to make it automatic rather than a per-project checklist.

Summary

A blank screen after deployment happens because the server replaces all files at once while a user's browser may still hold old HTML, cached resources, or a running application in a surviving tab. Four distinct failure modes exist: old HTML referencing deleted hashed assets, new HTML mixed with stale cached resources, lazy-loaded chunk 404s in long-lived tabs, and Service Worker version lock-in. No single fix covers them all. The industry standard is layered defense: correct cache headers for HTML and hashed assets, ChunkLoadError self-healing with a single forced reload, proactive version polling against a build-time `version.json`, and optional Service Worker precaching. Monitoring for silent blank screens and retaining old assets on the CDN round out the strategy. LYStack, a frontend platform, bakes version detection and SW precaching into its build pipeline so application code gets these protections for free. Its build fingerprint is content-addressed, not time-addressed, preventing false update notifications on rebuilds. The platform deliberately leaves ChunkLoadError recovery and blank-screen monitoring to the application layer, treating those as product decisions rather than infrastructure.

Takeaways
HTML must use conditional caching (no-cache) while hashed assets use permanent caching (max-age=31536000, immutable); getting this pair wrong is the root cause of most blank screens.
ChunkLoadError self-healing catches failed dynamic imports and forces a single reload, but it loses user state and must guard against infinite refresh loops with a sessionStorage flag.
Proactive version detection polls a build-time `version.json` and notifies users when a new version is available, but the refresh decision stays with the user to avoid destroying in-progress work.
Service Worker precaching is the most powerful defense but also the easiest to misconfigure; cache-first strategies without background revalidation cause permanent version lock-in.
LYStack computes buildId from all artifact contents, not timestamps, so rebuilding unchanged code produces the same fingerprint and never triggers a false update notification.
LYStack deliberately omits ChunkLoadError recovery and blank-screen monitoring from its platform because the first is a product decision about state loss and the second depends on external monitoring tools.
Retaining old hashed assets on the CDN for the last N releases physically eliminates lazy-load 404s in surviving tabs, a zero-code infrastructure fix used by large companies.
Conclusions

Content-addressed build fingerprints are a quiet but powerful idea: by stripping timestamps and version metadata from the hash input, a rebuild of identical code produces the same ID, eliminating the noisy false update notifications that plague time-based fingerprints.

LYStack's decision to leave ChunkLoadError recovery and blank-screen monitoring out of the platform is a deliberate architectural choice, not an oversight. Forcing a reload destroys user state, and whether that tradeoff is acceptable belongs to the product layer, not the infrastructure layer.

The Service Worker template avoids Workbox entirely and stays under 200 lines, treating the SW as a state machine that is easier to debug when kept small. Cache names embed the build fingerprint so version changes physically isolate old and new caches, preventing cross-version pollution.

Enabling offline caching only in test and production environments is a practical safeguard: dev builds change too rapidly for SW caching to be useful, and running it in test ensures the update flow is rehearsed under production-like conditions before going live.

The four-trigger version check (load, online, focus, visibilitychange plus polling) covers the realistic ways users encounter stale tabs, and the 15-second timeout with no-store fetch ensures the detection mechanism itself never becomes a performance or reliability problem.

Concepts & terms
Content-addressed build fingerprint
A build identifier computed by hashing all artifact contents while deliberately excluding volatile data like timestamps. Identical code produces the same fingerprint on rebuild, preventing false 'new version available' notifications.
ChunkLoadError
An error thrown when a dynamically imported JavaScript module (code-split chunk) fails to load, typically because the server has deleted the old hashed file after a deployment while a long-lived browser tab still references it.
Version lock-in (Service Worker)
A failure mode where a Service Worker caches an entire application version and, due to a missing or broken update strategy, never activates a newer version, leaving users permanently stuck on the first version they visited.
preEntry injection
A build-time technique that injects scripts before the application's own entry point, allowing version detection or SW registration to run before any business code executes, making the mechanism transparent to the application.
From the discussion
Featured comments
少说话_多学习

Does the LYStack solution mentioned in the article refer to https://lynx-stack.dev/zh/?

Liora_Yvonne

https://github.com/liangy0323/LYStack It refers to my open-source project. The bottom of the article also mentions this is a column.

少说话_多学习  → Liora_Yvonne

Got it, thanks boss.

See top comments, translated →
Source: juejin.cn ↗ Google Translate ↗ Backup ↗