Angular16从Webpack转Vite后Service Worker哈希不匹配问题
场景
把项目从Angular 15升级到16,同时把构建工具从Webpack换成esbuild(用@angular-devkit/build-angular:browser-esbuild构建器),通过Firebase Hosting部署后,Service Worker触发「Version_Installation_Failed」错误,所有资产都出现哈希不匹配问题。
错误日志
"Hash mismatch (cacheBustedFetchFromNetwork): https://myUrl.com/favicon.ico: expected 27d22f56faabc6ede22b52aad474a2806263ea9e, got a1b8ccd7e91b162f833472126c73929012a05ca7 (after cache busting) Error: Hash mismatch (cacheBustedFetchFromNetwork): https://myUrl.com/favicon.ico: expected 27d22f56faabc6ede22b52aad474a2806263ea9e, got a1b8ccd7e91b162f833472126c73929012a05ca7 (after cache busting) at PrefetchAssetGroup.cacheBustedFetchFromNetwork (https://myUrl.com/ngsw-worker.js:474:21)
当前ngsw-config.json配置
{ "$schema": "./node_modules/@angular/service-worker/config/schema.json", "index": "/index.html", "assetGroups": [ { "name": "app", "installMode": "prefetch", "resources": { "files": ["/favicon.ico", "/index.html", "/manifest.json", "/*.css", "/*.js"] } }, { "name": "assets", "installMode": "lazy", "updateMode": "prefetch", "resources": { "files": ["/assets/**", "/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)"] } } ] }
已尝试操作
- 移除ngsw-config后重新部署
- 删除所有资产后重新部署
问题还是没解决。
需求
让所有用户自动重新下载最新资产,不用手动清除浏览器缓存。
当前版本检查代码
if (this.swUpdate.isEnabled) { this.subs.sink = this.swUpdate.versionUpdates.subscribe((update) => { console.log(update); if (!this.snackBar._openedSnackBarRef && update.type == "VERSION_READY") this.openSnackBar(); }); }
1. 改资产组名称强制刷新缓存
esbuild和Webpack生成哈希的逻辑不一样,旧Service Worker存的还是Webpack的哈希值,自然和新的对不上。直接改ngsw-config.json里的资产组名字就行——把"name": "app"改成"name": "app-v2","name": "assets"改成"name": "assets-v2"。Angular Service Worker会把名字变化当成全新的资产组,直接让客户端重新下载所有资源,旧缓存直接作废。
2. 规范构建和部署步骤
部署前一定要清干净再构建:
ng build --prod --output-hashing=allesbuild默认的哈希规则和Webpack不同,加上
--output-hashing=all能确保所有静态文件(包括favicon)都带哈希后缀,避免Firebase的CDN缓存旧文件。给Firebase Hosting加缓存规则,让
ngsw-worker.js和ngsw.json绝不缓存:
编辑firebase.json,在hosting里加这段:{ "hosting": { "headers": [ { "source": "/ngsw-worker.js", "headers": [ { "key": "Cache-Control", "value": "no-cache, no-store, must-revalidate" } ] }, { "source": "/ngsw.json", "headers": [ { "key": "Cache-Control", "value": "no-cache, no-store, must-revalidate" } ] } ] } }这样客户端每次都会拉最新的SW配置,不会用本地存的旧版本。
3. 主动触发SW更新检查
在应用启动时主动查更新,不用等SW自己轮询:
修改app.component.ts的ngOnInit:
import { SwUpdate } from '@angular/service-worker'; async ngOnInit() { if (this.swUpdate.isEnabled) { // 启动就主动查更新 try { const hasUpdate = await this.swUpdate.checkForUpdate(); if (hasUpdate) { // 有更新就直接激活,然后刷新页面 await this.swUpdate.activateUpdate(); window.location.reload(); } } catch (err) { console.error('SW更新检查失败:', err); } // 监听版本就绪事件,自动激活刷新 this.subs.sink = this.swUpdate.versionUpdates.subscribe((update) => { if (update.type === 'VERSION_READY') { this.swUpdate.activateUpdate().then(() => { window.location.reload(); }); } }); } }
这样用户打开应用就会自动检查更新,有新版本直接刷新,不用手动操作。
4. 清Firebase服务器端缓存
登录Firebase控制台,进Hosting的存储页面,点清除缓存,把CDN上存的旧资源清掉,避免返回旧文件导致哈希不匹配。
内容的提问来源于stack exchange,提问作者Stef

