首次加载Service Worker注册前SwUpdate无法正常工作问题
问题分析与解决方案
核心问题
首次加载时你的NewAppVersionTrackerService初始化过早——此时Service Worker还没完成注册(因为用了registerWhenStable:30000策略),this.swUpdate.isEnabled返回false,直接跳过了所有监听逻辑;而SW注册完成后,你的服务没有重新触发这些逻辑,导致SwUpdate与SW完全脱节。刷新页面时SW已提前注册,isEnabled为true,所以逻辑正常运行。
修复方案
方案1:监听SwUpdate.isEnabled变化,动态启动监听逻辑
修改服务代码,在isEnabled状态变为true时自动初始化更新监听,避免因SW未就绪导致逻辑跳过:
// new-app-version-tracker.service.ts @Injectable({ providedIn: 'root' }) export class NewAppVersionTrackerService { private checkIntervalSeconds = 300; private updateSubscription?: Subscription; private intervalSubscription?: Subscription; constructor( private dialogService: DialogService, private swUpdate: SwUpdate, private zone: NgZone, ) {} public init() { // 监听isEnabled状态变化,动态启停监听逻辑 this.swUpdate.isEnabled.subscribe((enabled) => { enabled ? this.startUpdateWatch() : this.stopUpdateWatch(); }); // 初始检查一次状态 if (this.swUpdate.isEnabled.value) { this.startUpdateWatch(); } } private startUpdateWatch(): void { if (this.updateSubscription) return; // 订阅版本更新事件 this.updateSubscription = this.swUpdate.versionUpdates.subscribe((versionEvent) => { switch (versionEvent.type) { case 'VERSION_DETECTED': console.info(`发现新版本,开始下载: ${versionEvent.version.hash}`); break; case 'VERSION_READY': console.info(`新版本已就绪: ${versionEvent.latestVersion.hash}`); this.dialogService.open(DynamicNewApplicationVersionReadyDialogComponent); break; case 'VERSION_INSTALLATION_FAILED': console.warn(`新版本下载失败 (hash: ${versionEvent.version.hash}): ${versionEvent.error}`); break; } }); // 启动定时更新检查 this.zone.runOutsideAngular(() => { this.intervalSubscription = interval(this.checkIntervalSeconds * 1000).subscribe(() => { console.log('检查SW更新'); this.swUpdate.checkForUpdate() .then((versionFound) => console.log('更新检查结果:', versionFound)) .catch(err => console.error('更新检查失败:', err)); // 新增错误捕获,避免Promise挂起 }); }); } private stopUpdateWatch(): void { this.updateSubscription?.unsubscribe(); this.intervalSubscription?.unsubscribe(); this.updateSubscription = undefined; this.intervalSubscription = undefined; } }
方案2:在APP初始化阶段等待SW注册完成
通过APP_INITIALIZER让应用等待SW注册完成后再初始化服务,确保服务启动时SW已就绪:
// app.config.ts export const appConfig: ApplicationConfig = { providers: [ provideServiceWorker('ngsw-worker.js', { enabled: !isDevMode(), registrationStrategy: 'registerWhenStable:30000', }), { provide: APP_INITIALIZER, useFactory: (swUpdate: SwUpdate) => () => new Promise(resolve => { // 轮询检查SW是否就绪 const checkTimer = setInterval(() => { if (swUpdate.isEnabled.value) { clearInterval(checkTimer); resolve(true); } }, 1000); // 超时兜底,避免阻塞应用启动 setTimeout(() => { clearInterval(checkTimer); resolve(true); }, 35000); }), deps: [SwUpdate], multi: true, }, NewAppVersionTrackerService, ], };
方案3:直接监听浏览器SW注册事件
通过浏览器原生API监听SW注册状态,确保SW就绪后再启动更新逻辑:
// new-app-version-tracker.service.ts private waitForSwRegistration(): Promise<void> { return new Promise(resolve => { // 若SW已激活,直接resolve if (navigator.serviceWorker.controller) { resolve(); } else { // 监听SW控制器变更事件(注册完成触发) navigator.serviceWorker.addEventListener('controllerchange', () => { resolve(); }); // 超时兜底 setTimeout(resolve, 35000); } }); } public async init() { await this.waitForSwRegistration(); if (this.swUpdate.isEnabled) { this.watchForUpdates(); } }
额外注意事项
- 生产环境测试时,确保每次部署都更新资源哈希(Angular CLI会自动处理,若手动部署需同步
ngsw-config.json),否则SW无法检测到新版本。 - 必须为
checkForUpdate()添加错误捕获,避免Promise因异常挂起无响应。
内容的提问来源于stack exchange,提问作者JWrightII
相关产品推荐
相关产品推荐

