网页容错恢复
2025/9/3大约 3 分钟
网页容错恢复
在实际项目中,用户的电脑可能会进入 睡眠、休眠,或者切换网络、锁屏,甚至浏览器将页面冻结。这些情况会对网页的运行产生多方面的影响,比如 WebSocket 断开、渲染上下文丢失、定时器冻结 等。
本文整理了一份 前端容错恢复方案 Checklist,并为每个方案提供了 代码实现示例,帮助你在开发中逐项检查,提升应用的稳定性与可用性。
1. 网络层面
WebSocket 自动重连与心跳检测
class ReconnectWebSocket {
constructor(url) {
this.url = url;
this.connect();
}
connect() {
this.ws = new WebSocket(this.url);
this.ws.onopen = () => {
console.log("WebSocket 已连接");
this.startHeartbeat();
};
this.ws.onmessage = (e) => {
console.log("收到消息:", e.data);
};
this.ws.onclose = () => {
console.warn("WebSocket 已关闭,3秒后重连...");
this.stopHeartbeat();
setTimeout(() => this.connect(), 3000);
};
this.ws.onerror = () => {
console.error("WebSocket 出错,关闭后重连");
this.ws.close();
};
}
startHeartbeat() {
this.heartbeat = setInterval(() => {
if (this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify({ type: "ping" }));
}
}, 5000);
}
stopHeartbeat() {
clearInterval(this.heartbeat);
}
}
// 使用
const socket = new ReconnectWebSocket("wss://example.com/ws");HTTP 请求重试与断点续传
async function fetchWithRetry(url, options = {}, retries = 3, delay = 1000) {
for (let i = 0; i < retries; i++) {
try {
const res = await fetch(url, options);
if (!res.ok) throw new Error("请求失败");
return res;
} catch (err) {
console.warn(`请求失败,第 ${i + 1} 次重试...`);
await new Promise(r => setTimeout(r, delay));
}
}
throw new Error("请求失败,已达到最大重试次数");
}
// 使用
fetchWithRetry("/api/data", {}, 3, 2000).then(res => res.json());2. 渲染/UI 层面
WebGL 上下文丢失恢复
const canvas = document.querySelector("canvas");
const gl = canvas.getContext("webgl");
canvas.addEventListener("webglcontextlost", (e) => {
e.preventDefault();
console.warn("WebGL 上下文丢失");
});
canvas.addEventListener("webglcontextrestored", () => {
console.log("WebGL 上下文恢复,重新初始化");
initWebGL(gl);
});
function initWebGL(gl) {
// 重新加载着色器、纹理等资源
}视频/音频自动恢复
const video = document.querySelector("video");
document.addEventListener("visibilitychange", () => {
if (!document.hidden && video.paused) {
console.log("页面回到前台,恢复播放");
video.play().catch(() => {});
}
});CSS 动画基于时间戳
let start = performance.now();
function animate(timestamp) {
const elapsed = timestamp - start;
const progress = Math.min(elapsed / 2000, 1); // 动画时长 2s
box.style.transform = `translateX(${progress * 200}px)`;
if (progress < 1) requestAnimationFrame(animate);
}
requestAnimationFrame(animate);3. 定时器/任务调度
使用时间差校正逻辑
let last = Date.now();
setInterval(() => {
const now = Date.now();
const delta = now - last; // 实际经过的时间
last = now;
console.log(`过去了 ${delta} ms`);
}, 1000);倒计时基于服务器时间
async function getServerTime() {
const res = await fetch("/api/time");
const data = await res.json();
return data.timestamp;
}
async function startCountdown() {
const serverTime = await getServerTime();
const endTime = serverTime + 60000; // 60s 倒计时
const timer = setInterval(() => {
const remaining = endTime - Date.now();
if (remaining <= 0) {
clearInterval(timer);
console.log("倒计时结束");
} else {
console.log(`剩余 ${Math.floor(remaining / 1000)} 秒`);
}
}, 1000);
}
startCountdown();4. 硬件/资源状态
摄像头/麦克风恢复
navigator.mediaDevices.addEventListener("devicechange", async () => {
console.log("设备发生变化,重新申请权限");
try {
const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
video.srcObject = stream;
} catch (err) {
console.error("无法访问摄像头/麦克风", err);
}
});蓝牙设备重新连接
async function connectBluetooth() {
try {
const device = await navigator.bluetooth.requestDevice({ filters: [{ services: ['battery_service'] }] });
const server = await device.gatt.connect();
console.log("蓝牙已连接:", device.name);
} catch (err) {
console.error("蓝牙连接失败", err);
}
}5. 浏览器策略
后台任务交给 Service Worker
// sw.js
self.addEventListener("sync", event => {
if (event.tag === "sync-messages") {
event.waitUntil(sendPendingMessages());
}
});
async function sendPendingMessages() {
const messages = await loadPendingMessages();
for (const msg of messages) {
await fetch("/api/send", { method: "POST", body: JSON.stringify(msg) });
}
}注册后台同步:
navigator.serviceWorker.ready.then(sw => {
return sw.sync.register("sync-messages");
});6. 通用监测与恢复
// 页面可见性
document.addEventListener("visibilitychange", () => {
if (document.hidden) {
console.log("页面进入后台");
} else {
console.log("页面回到前台");
reconnectWebSocket();
refreshTokenIfNeeded();
syncMissedData();
}
});
// 网络状态
window.addEventListener("online", () => reconnectWebSocket());
window.addEventListener("offline", () => console.warn("网络断开"));✅ 最终 Checklist
总结
电脑睡眠、休眠、锁屏、后台冻结等情况不可避免,但通过合理的 容错与恢复策略,可以最大限度地保证应用的稳定性和用户体验。
在实际项目中,可以根据业务场景逐项对照以上 Checklist,并直接使用文中的 代码示例,实现更健壮的前端应用。
