重要安全更新
版本 ${version} 包含关键更新,10秒后自动刷新
10
当新版本前端代码部署后,用户浏览器中的旧版本代码可能长期存活:
如何优雅解决这个看似简单却暗藏杀机的问题?本文提供六种阶梯式解决方案:
// 构建时通过环境变量注入版本号
window.APP_VERSION = '1.2.3';
// 轮询版本检测
setInterval(async () => {
const { version } = await fetch('/api/version');
if (version !== window.APP_VERSION) {
showUpdateAlert();
}
}, 300000); // 5分钟检测
// 更新提示组件
function showUpdateAlert() {
const div = document.createElement('div');
div.innerHTML = `
新版本已发布,点击立即获取
`;
document.body.appendChild(div);
}
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
// 监听CI/CD系统的webhook
app.post('/deploy-webhook', (req, res) => {
wss.clients.forEach(client => {
if (client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify({
type: 'FORCE_UPDATE',
version: '1.2.3'
}));
}
});
res.status(200).end();
});
const ws = new WebSocket('wss://yoursite.com/ws');
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
if (msg.type === 'FORCE_UPDATE') {
showForceUpdateModal(msg.version);
}
};
function showForceUpdateModal(version) {
// 禁用页面操作
document.documentElement.style.pointerEvents = 'none';
// 显示不可关闭的模态框
const modal = document.createElement('div');
modal.innerHTML = `
重要安全更新
版本 ${version} 包含关键更新,10秒后自动刷新
10
`;
document.body.appendChild(modal);
// 倒计时强制刷新
let counter = 10;
const timer = setInterval(() => {
counter--;
modal.querySelector('.countdown').textContent = counter;
if (counter <= 0) {
clearInterval(timer);
localStorage.clear();
location.reload(true);
}
}, 1000);
}
// sw.js
self.addEventListener('install', (event) => {
self.skipWaiting(); // 强制立即激活
});
self.addEventListener('activate', (event) => {
event.waitUntil(
clients.matchAll({type: 'window'}).then((windowClients) => {
windowClients.forEach((windowClient) => {
windowClient.navigate(windowClient.url);
});
})
);
});
// 主线程注册
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js').then((reg) => {
reg.addEventListener('updatefound', () => {
const newWorker = reg.installing;
newWorker.addEventListener('statechange', () => {
if (newWorker.state === 'activated') {
showSWUpdateAlert();
}
});
});
});
}
function showSWUpdateAlert() {
// 显示可延迟更新的提示
Toast.show({
content: '新功能已就绪',
action: '立即体验',
onAction: () => {
window.location.reload();
},
duration: 0 // 永久显示
});
}
场景 | 推荐方案 | 触发条件 | 用户干扰度 | 技术复杂度 |
---|---|---|---|---|
后台管理系统 | 定时检测 + 静默刷新 | 非活跃状态检测 | ★☆☆☆☆ | ★★☆☆☆ |
金融交易系统 | WebSocket 强更 | 关键业务更新 | ★★★★★ | ★★★★☆ |
内容型网站 | Service Worker | 资源文件hash变化 | ★★☆☆☆ | ★★★★☆ |
移动端H5 | App内嵌WebView通信 | 客户端版本不匹配 | ★★★☆☆ | ★★★☆☆ |
游戏类应用 | 资源热更新 | 场景切换时检测 | ☆☆☆☆☆ | ★★★★★ |
const UPDATE_LEVEL = {
CRITICAL: { // 必须立即更新
priority: 100,
style: 'danger'
},
FEATURE: { // 建议更新
priority: 50,
style: 'primary'
},
OPTIONAL: { // 可选更新
priority: 10,
style: 'default'
}
};
function smartNotify(level) {
const state = document.visibilityState;
const isIdle = idleTime > 5 * 60 * 1000;
// 不同场景触发策略
if (level === 'CRITICAL') {
immediateNotify();
} else if (state === 'hidden' || isIdle) {
scheduleBackgroundSync();
} else if (isUserEditingForm()) {
delayNotify(10000);
} else {
showGentleReminder();
}
}
// 用户版本分布统计
navigator.sendBeacon('/api/version-report', {
version: window.APP_VERSION,
ua: navigator.userAgent,
lastActive: Date.now() - performance.timing.navigationStart
});
// 更新成功率埋点
window.addEventListener('beforeunload', () => {
if (isUpdateReload) {
track('UPDATE_RELOAD_SUCCESS');
}
});
alert_rules:
- name: 旧版本用户占比过高
condition: >
sum(rate(version_usage{env="prod"}[1h]))
BY (version) > 30%
duration: 1h
level: P2
- name: 强制更新失败率升高
condition: >
rate(update_failure_total[5m]) > 0.1
duration: 10m
level: P1
从简单的版本检测到智能化的更新策略,前端更新通知的演进史正是一部用户体验优化史。技术方案的选择需要与业务场景深度结合,在确保功能可靠性的同时,最大限度保持用户体验的流畅性。在这个持续交付的时代,优雅的更新策略已成为现代Web应用的必备能力。