Service Worker是运行在浏览器后台的独立线程,不依赖页面主线程,能够在页面关闭后继续运行,这一特性让它成为实现后台数据同步的理想方案。通过拦截网络请求、管理本地缓存、监听网络状态变化,Service Worker可以在用户离线时暂存操作数据,网络恢复后自动将本地数据同步到服务端,保障数据的一致性。

Service Worker基础准备
首先要完成Service Worker的注册和生命周期管理,这是实现后续同步功能的前提。Service Worker的生命周期包括安装、激活、空闲、终止几个阶段,我们需要在安装阶段初始化本地存储结构,用于暂存待同步的数据。
注册Service Worker
在页面主线程中注册Service Worker,指定其脚本路径:
// 检查浏览器是否支持Service Worker
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
// 注册Service Worker脚本,路径相对于项目根目录
navigator.serviceWorker.register('/sw.js')
.then(registration => {
console.log('Service Worker注册成功,作用域:', registration.scope);
})
.catch(error => {
console.log('Service Worker注册失败:', error);
});
});
}
初始化本地存储
在Service Worker的安装事件中,使用IndexDB初始化本地存储,用来保存待同步的数据:
// sw.js 文件内容
self.addEventListener('install', event => {
event.waitUntil(
// 打开名为syncDB的IndexDB数据库,版本号为1
indexedDB.open('syncDB', 1).onsuccess = function(e) {
const db = e.target.result;
// 如果不存在syncStore对象仓库,则创建,使用自增id作为主键
if (!db.objectStoreNames.contains('syncStore')) {
db.createObjectStore('syncStore', { keyPath: 'id', autoIncrement: true });
}
}
);
// 强制跳过等待,直接进入激活状态
self.skipWaiting();
});
self.addEventListener('activate', event => {
// 激活后立即控制所有页面
event.waitUntil(self.clients.claim());
});
离线数据暂存逻辑
当用户在离线状态下提交数据时,页面主线程需要先将数据暂存到本地,同时通知Service Worker有新的待同步数据。这里通过postMessage和本地存储结合的方式实现。
页面侧暂存数据
页面检测到网络离线时,将用户操作的数据存入IndexDB,同时向Service Worker发送消息:
// 保存待同步数据到本地
function saveOfflineData(data) {
return new Promise((resolve, reject) => {
const request = indexedDB.open('syncDB', 1);
request.onsuccess = function(e) {
const db = e.target.result;
const transaction = db.transaction(['syncStore'], 'readwrite');
const store = transaction.objectStore('syncStore');
// 存入数据,包含数据内容、同步状态、时间戳
const addRequest = store.add({
data: data,
status: 'pending', // pending表示待同步,synced表示已同步
timestamp: Date.now()
});
addRequest.onsuccess = function() {
// 向Service Worker发送新数据待同步的消息
if (navigator.serviceWorker.controller) {
navigator.serviceWorker.controller.postMessage({
type: 'NEW_SYNC_DATA'
});
}
resolve();
};
addRequest.onerror = reject;
};
request.onerror = reject;
});
}
// 监听网络状态变化
window.addEventListener('offline', () => {
console.log('网络已断开,数据将暂存本地');
});
// 用户提交数据的示例
document.getElementById('submitBtn').addEventListener('click', async () => {
const userData = { content: document.getElementById('input').value };
if (!navigator.onLine) {
// 离线状态,暂存数据
await saveOfflineData(userData);
alert('当前离线,数据已暂存,网络恢复后将自动同步');
} else {
// 在线状态,直接发送请求
fetch('/api/saveData', {
method: 'POST',
body: JSON.stringify(userData)
});
}
});
后台同步核心实现
Service Worker需要监听网络状态变化,同时处理页面发送的消息,触发同步逻辑。如果浏览器支持Background Sync API,可以结合该API实现更可靠的后台同步,不支持的情况下则通过轮询网络状态实现。
监听同步触发条件
Service Worker中监听消息和网络状态,触发同步逻辑:
// 监听页面发送的消息
self.addEventListener('message', event => {
if (event.data.type === 'NEW_SYNC_DATA') {
// 收到新数据消息,尝试触发同步
triggerSync();
}
});
// 监听网络状态变化(部分浏览器支持)
self.addEventListener('online', () => {
console.log('网络已恢复,开始同步数据');
triggerSync();
});
// 触发同步的通用方法
function triggerSync() {
// 先检查是否支持Background Sync
if ('sync' in self.registration) {
// 注册后台同步任务,同步标签为dataSync
self.registration.sync.register('dataSync')
.then(() => {
console.log('后台同步任务注册成功');
})
.catch(error => {
console.log('后台同步任务注册失败,使用备用方案', error);
// 备用方案:直接执行同步逻辑
syncLocalData();
});
} else {
// 不支持Background Sync,直接执行同步
syncLocalData();
}
}
执行数据同步逻辑
从IndexDB中读取所有待同步的数据,依次发送到服务端,同步成功后更新本地数据状态:
// 同步本地待同步数据
function syncLocalData() {
return new Promise((resolve, reject) => {
const request = indexedDB.open('syncDB', 1);
request.onsuccess = async function(e) {
const db = e.target.result;
const transaction = db.transaction(['syncStore'], 'readwrite');
const store = transaction.objectStore('syncStore');
// 获取所有待同步数据
const getAllRequest = store.getAll();
getAllRequest.onsuccess = async function() {
const pendingData = getAllRequest.result.filter(item => item.status === 'pending');
if (pendingData.length === 0) {
resolve();
return;
}
// 遍历所有待同步数据,依次发送请求
for (const item of pendingData) {
try {
const response = await fetch('/api/saveData', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(item.data)
});
if (response.ok) {
// 同步成功,更新状态为已同步
item.status = 'synced';
store.put(item);
// 可选:同步成功后删除已同步的数据
// store.delete(item.id);
}
} catch (error) {
console.log('单条数据同步失败,等待下次重试', error);
}
}
resolve();
};
getAllRequest.onerror = reject;
};
request.onerror = reject;
});
}
// 监听Background Sync的同步事件
self.addEventListener('sync', event => {
if (event.tag === 'dataSync') {
// 执行同步逻辑
event.waitUntil(syncLocalData());
}
});
冲突处理与优化
实际场景中可能会出现本地数据和服务器数据冲突的情况,比如离线时修改了数据,在线后其他用户也修改了同一份数据。可以在同步时携带本地数据的时间戳,服务端根据时间戳判断冲突,返回冲突结果后,页面侧提示用户选择保留哪个版本的数据。
另外可以优化同步逻辑,比如限制单次同步的数据条数,避免大量数据同时发送导致请求失败;增加重试机制,同步失败的数据等待一段时间后再次尝试同步;定期清理本地已同步的旧数据,避免IndexDB占用过多存储空间。
注意事项
- Service Worker只能运行在HTTPS环境或者localhost本地环境下,正式部署需要配置SSL证书。
- IndexDB的操作是异步的,所有数据库操作都需要放在Promise或者回调中处理,避免逻辑执行顺序错误。
- Background Sync API目前不是所有浏览器都支持,需要做好兼容处理,备用方案可以保证同步功能在更多浏览器上可用。
- 同步逻辑中要做好错误捕获,避免单个数据同步失败导致整个同步流程中断。
Service_Worker后台数据同步离线存储Web Worker前端缓存修改时间:2026-07-16 23:12:43