How to Achieve Real‑Time Version Updates in Micro‑Frontend Apps with Pure Frontend Polling
This article details a zero‑intrusion solution for synchronizing version numbers across multiple micro‑frontend environments using a lightweight frontend poller, static version files, Nginx cache control, and Ant Design Vue modals, enabling testers to see updates within 30 seconds without backend changes.
Background and Problem
In a micro‑frontend ecosystem each sub‑application is deployed to dev, test, release and production environments. Frequent releases cause testers to keep working on stale pages because the displayed version does not change, leading to repeated “old version” complaints.
Requirements
Detect a newer version within 30 seconds of release.
Show a modal that displays the current version, the latest version and the environment.
Provide “Refresh now” and “Later” actions without forcing an immediate interruption.
Implement the solution without any backend changes and keep it compatible with the existing CI/CD pipeline.
Chosen Approach
Four alternatives were evaluated (pure frontend polling of version.json, Service‑Worker/PWA, WebSocket push, backend unified API). Considering implementation speed and team resources, the team selected the pure frontend polling + static version file method.
Key Principles
Unique, traceable version number : baseVersion‑env‑timestamp.
Zero‑intrusion release : Jenkins continues to run the existing npm run build‑xxx commands; the only addition is the generated version.json file.
Technical Overview
1. Build‑time Generation of version.json
The version is computed once in vue.config.js, injected into process.env.APP_VERSION, and written to a static version.json placed under the sub‑app’s public path.
const buildEnvName = getEnvName();
const buildVersion = getAppVersion();
module.exports = {
configureWebpack: {
plugins: [
new webpack.DefinePlugin({
"process.env.APP_VERSION": JSON.stringify(buildVersion),
"process.env.APP_ENV": JSON.stringify(buildEnvName),
})
]
},
chainWebpack(config) {
config.plugin("generate-version-json").use({
apply(compiler) {
compiler.hooks.done.tap("GenerateVersionJsonPlugin", () => {
fs.writeFileSync(
path.resolve(__dirname, "edu/version.json"),
JSON.stringify({
version: buildVersion,
env: buildEnvName,
timestamp: new Date().toISOString(),
publicPath: "/child/edu"
}, null, 2)
);
});
}
});
}
};2. Frontend Polling and Comparison
After the app mounts, a poller requests version.json every 30 seconds with cache: "no-store" and a timestamp query parameter to bypass any CDN or browser cache. If the fetched version differs from the bundled version, a modal is shown.
class VersionChecker {
currentVersion = process.env.APP_VERSION;
publicPath = "/child/edu";
checkInterval = 30 * 1000;
init() {
console.log(`📌 Current frontend version: ${this.currentVersion} (${process.env.APP_ENV})`);
this.startChecking();
document.addEventListener("visibilitychange", () => {
if (document.visibilityState === "visible" && !this.hasNotified) {
this.checkForUpdate();
}
});
}
async checkForUpdate() {
const url = `${this.publicPath}/version.json?t=${Date.now()}`;
const response = await fetch(url, { cache: "no-store" });
if (!response.ok) return;
const latestInfo = await response.json();
if (latestInfo.version !== this.currentVersion && !this.hasNotified) {
this.hasNotified = true;
this.stopChecking();
this.showUpdateModal(latestInfo.version, latestInfo.env);
}
}
}3. Interaction Prompt
The modal is implemented with Ant Design Vue’s Modal.confirm. It displays the current version, the latest version and the environment, and offers two actions:
Refresh now – forces a full page reload.
Later – dismisses the modal and resumes polling.
4. Nginx Cache Strategy
HTML pages and version.json are served with Cache‑Control: no-store (and related headers) to guarantee fresh retrieval. Static assets (JS/CSS/images) keep long‑term caching.
location / {
if ($request_filename ~* .html$) {
add_header Cache-Control "no-store, no-cache, must-revalidate";
}
}
location /child/edu {
if ($request_filename ~* .html$) {
add_header Cache-Control "no-store, no-cache, must-revalidate";
}
}
location ~* /child/edu/version.json$ {
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate";
add_header Pragma "no-cache";
add_header Expires "0";
add_header Surrogate-Control "no-store";
}5. CI/CD Integration
The existing Jenkins scripts are unchanged; the only addition is the generated version.json placed under /child/edu. All build commands include the OpenSSL compatibility flag.
develop : npm run build-develop → /child/edu testing : npm run build-testing → /child/edu release : npm run build-release → /child/edu production : npm run build-production → /child/edu Each command is prefixed with cross-env NODE_OPTIONS=--openssl-legacy-provider to handle OpenSSL version differences.
Testing and Validation
First‑visit: open a dev page, verify the console logs the bundled version and that version.json is fetched without caching.
Trigger new version: modify any source, rebuild, keep the old page open – it should not auto‑refresh.
Polling verification: within 30 seconds a modal appears showing the new version.
Interaction paths: “Refresh now” forces a reload; “Later” records the choice and resumes polling.
Edge cases: switching browser tabs, clearing cache, accessing from a new device, or rapid successive releases all correctly detect the latest version.
Common Issues and Remedies
No modal – version.json missing or unchanged. Check deployment path and ensure the file is generated.
Modal shows old version after refresh – static assets cached. Verify Nginx cache headers and browser cache settings.
Build failure – missing cross-env or permission issue. Install the dependency and ensure the Jenkins workspace is writable.
False update alerts – version generated multiple times during build. Cache buildVersion at the top of vue.config.js and reuse it globally.
Impact
Testers receive version‑update prompts within 30 seconds, dramatically improving testing efficiency.
“Ghost” modals disappear; version comparison logic stabilizes.
No backend changes are required; the release pipeline remains untouched.
Documentation enables other sub‑applications to adopt the solution without reinventing it.
Future Work
Package the logic as a reusable SDK supporting Vue CLI and Vite.
Build a visual version panel in the host app to aggregate versions across environments.
Introduce differentiated refresh strategies: forced refresh for high‑priority releases, optional for regular updates.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
