Service Worker作为浏览器背后的独立线程,能够拦截页面发出的fetch请求。在需要登录态的系统中,令牌往往有时效限制,如果直接在页面里处理刷新,多标签页并发会导致重复刷新和请求失败。把令牌处理和请求同步放到Service Worker中统一调度,是更稳妥的做法。

为什么在Service Worker中处理令牌
将令牌逻辑放在Service Worker有以下好处:
- 所有页面的请求都经过同一处拦截,避免各页面各自刷新令牌。
- 可以利用worker作用域内的变量缓存令牌,减少反复读取存储的开销。
- 在离线或弱网时,可先返回缓存响应,待网络恢复后再同步数据。
令牌存储与刷新设计
在worker中使用变量保存access_token和refresh_token,并维护一个刷新中的标记。当发现请求返回401时,进入刷新流程。
// Service Worker内部令牌管理
let tokenData = {
access_token: null,
refresh_token: null,
refreshing: false,
waiters: []
};
function setTokens(access, refresh) {
tokenData.access_token = access;
tokenData.refresh_token = refresh;
}
// 统一刷新令牌,多个请求复用同一次刷新
async function refreshToken() {
if (tokenData.refreshing) {
return new Promise(resolve => tokenData.waiters.push(resolve));
}
tokenData.refreshing = true;
try {
const res = await fetch('/api/refresh', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ refresh_token: tokenData.refresh_token })
});
const json = await res.json();
setTokens(json.access_token, json.refresh_token);
tokenData.waiters.forEach(fn => fn(json.access_token));
tokenData.waiters = [];
return json.access_token;
} finally {
tokenData.refreshing = false;
}
}
网络请求同步实现
在fetch事件中,先给请求附加令牌。若收到401,则刷新令牌后重发原请求,从而保证并发请求不会各自触发刷新。
self.addEventListener('fetch', event => {
const url = new URL(event.request.url);
if (url.pathname.startsWith('/api/')) {
event.respondWith(handleAuthRequest(event.request));
}
});
async function handleAuthRequest(request) {
const req1 = addToken(request, tokenData.access_token);
let response = await fetch(req1);
if (response.status === 401) {
const newToken = await refreshToken();
const req2 = addToken(request, newToken);
response = await fetch(req2);
}
return response;
}
function addToken(request, token) {
const headers = new Headers(request.headers);
if (token) {
headers.set('Authorization', 'Bearer ' + token);
}
return new Request(request, { headers: headers });
}
缓存与同步策略
对于非敏感数据,可使用cache-first策略;对于写请求,先发往网络,失败则进入后台同步队列。
| 请求类型 | 策略 | 说明 |
|---|---|---|
| GET静态资源 | cache-first | 优先读缓存,提升加载速度 |
| GET接口数据 | network-first | 保证数据新鲜,失败回退缓存 |
| POST写操作 | network-only加同步 | 失败存入SyncManager待重发 |
后台同步示例
self.addEventListener('sync', event => {
if (event.tag === 'sync-post') {
event.waitUntil(replayPostQueue());
}
});
async function replayPostQueue() {
const cache = await caches.open('post-queue');
const requests = await cache.keys();
for (const req of requests) {
const body = await cache.match(req);
await fetch(req, { body: await body.blob() });
await cache.delete(req);
}
}
小结
通过把令牌刷新收敛到Service Worker、用等待队列消除并发冲突,再配合合理的缓存与后台同步,就能在架构层面实现高效的令牌处理与网络请求同步。这种方案对多标签页应用尤其友好,也更容易扩展到离线优先场景。
Service_Worker令牌处理网络请求同步前端缓存离线优先修改时间:2026-07-24 21:36:29