typescript - 'Uventet token <' ved hver ny bygging av angular produksjon PWA til siden oppdateres
Jeg vet at det finnes noen lignende spørsmål som 'så uventet token etter Angular 2 produksjonsbygg', men det svarer egentlig ikke på spørsmålet mitt.
I utgangspunktet har jeg en Angular-app og det er en PWA, nå hver gang jeg gjør en ny produksjonsbygging, får jeg en feil første gang jeg besøker nettstedet slik
og så snart jeg oppdaterer siden, fungerer nettstedet som normalt.
Dette skjer på hvilken som helst nettleser/enhet
Nå mistenker jeg at hver gang appen oppdateres, endres den hovedsamlede js-filen, og PWA-en har bufret den gamle, og appen prøver fortsatt å bruke den nye.
Min prosjektstruktur er som følger
Jeg har en rotmodul som laster de andre modulene forsinket, nå er den første modulen som blir lastet inn kontomodulen min, slik at brukeren kan logge inn.
dette er min root-routing.module.ts
import { NgModule } from '@angular/core';
import { Routes, RouterModule, PreloadAllModules } from '@angular/router';
const routes: Routes = [
{ path: '', redirectTo: '/account/login', pathMatch: 'full' },
{
path: 'account',
loadChildren: 'account/account.module#AccountModule',
data: { preload: true }
}
];
@NgModule({
imports: [RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules })],
exports: [RouterModule],
providers: []
})
export class RootRoutingModule {
}
så teknisk sett er den første modulen brukeren kommer til kontomodulen, men åpenbart må den omdirigeres fra rotmodulen.
Så det jeg gjorde var at i rotkomponentmodulen min sjekket jeg om Service Worker trenger oppdatering, slik som dette...
root.component.ts
@import { SwUpdate } from '@angular/service-worker'
...
export class...
constructor(
private _sw: SwUpdate
) {
if (this._sw.isEnabled) {
this._sw.available
.subscribe(() => {
this._sw.activateUpdate()
.then(() => {
window.location.reload(true);
});
});
}
}
nå skal dette sjekke om det er en oppdatering og deretter bare oppdatere siden.. men det fungerer ikke.
dette er min ngsw-config.json
{
"index": "/index.html",
"assetGroups": [
{
"name": "App",
"installMode": "prefetch",
"resources": {
"files": [
"/favicon.ico",
"/index.html",
"/*.css",
"/*.js",
"!/main*.js"
]
}
}, {
"name": "assets",
"installMode": "lazy",
"updateMode": "prefetch",
"resources": {
"files": [
"/assets/**",
"/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)"
]
}
}
]
}
som du kan se, ekskluderer jeg main.js-chunkfilen, noe som burde fjerne dette problemet... men det gjør det ikke
Nå når jeg sjekker nettverksfanen etter en ny bygging, er dette hva jeg får
slik ser mine main.js-kall ut etter at jeg har ekskludert det fra ngsw.config
Jeg tenkte deretter å bare prøve å fange denne feilen ved å bruke min Sentry-feilhåndterer slik som dette..
export class RavenErrorHandler implements ErrorHandler {
handleError(err: any): void {
console.log('error', err);
if (err.toLowerCase().includes('token <')) {
window.location.reload(true);
} else {
Raven.captureException(err);
}
}
}
men dette fungerer ikke, jeg får feilen i Sentry, men appen laster ikke på nytt??
Nå har jeg gjort litt testing. Hvis du besøker nettstedet etter en ny bygging i privatmodus, vil du aldri få feilen; det ser ut til å skje bare hvis du har besøkt nettstedet tidligere, noe som får meg til å tro at det er et cache-problem.
Applikasjonen min er hostet på Azure og applikasjonen min bruker Angular 7.27
All hjelp ville blitt satt pris på!
Svar #1
Problemet skyldes den installerte ServiceWorker, som er den som forstyrrer og cacher alle svarene, inkludert de du nettopp har oppdatert.
Mer informasjon om servicearbeidere: MDN - Bruke servicearbeidere
https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers
Det er derfor window.location.reload() ikke fungerer, fordi det faktisk lager en ny forespørsel men denne forespørselen blir kapret av den installerte ServiceWorker og i stedet for å la den ekte forespørselen gå gjennom til den ekte serveren og hente det ekte svaret (oppdatert main.js og ressurser), returnerer den bare en lagret respons tilbake til appen din, som er den gamle main.js og ikke den oppdaterte, og derfor feiler det.
Mer spesifikt cacher ServiceWorker main.js og andre ressurser, som genereres hver gang du endrer koden din. Main.js når den lastes, laster den på sin side andre deler av applikasjonen din på forespørsel (gjør en HTTP-forespørsel til andre chunks, f.eks. 5.xxxxx.js).
Når du faktisk gjør en endring i koden din, og selv om alle delene dine blir oppdatert, inkludert main.js og 5.xxxxxx.js-delen, kjører fortsatt nettleseren den eldre main.js. Denne eldre main.js har en referanse til det eldre paret av 5.xxxxxx.js som ikke lenger eksisterer. På dette punktet, hvis du gjør en reload() programmessig, svarer den installerte ServiceWorker med den eldre bufrede main.js som i sin tur prøver å lazy-loade den eldre versjonen av 5.xxxxx.js fra serveren som ikke eksisterer, og derfor får man et 404 html-feilrespons. Dermed oppstår '<'-token unntaket (det er egentlig det første tegnet i
Dette er tydelig fra skjermbildet av nettverkspanelet. Hvis du undersøker skjermbildet, vil du se at main.js og alle andre ressurser leveres fra (fra service worker), noe som betyr at disse er de bufrede eldre versjonene og er forskjellige fra de faktiske oppdaterte filene som nå finnes på webserveren din.
For å løse dette problemet må ServiceWorker konfigureres til å ikke cache main.js og i stedet alltid la denne forespørselen gå gjennom til den ekte serveren for å hente en oppdatert versjon av main.js som igjen vil laste den oppdaterte 5.xxxx.js-delen som nå faktisk finnes på webserveren.
Her er hvordan du kan konfigurere ngsw-config.json for ServiceWorker for spesifikt å ikke cache main.js-filen (forutsatt at den har mønsteret main.xxxxxxx.js) for å løse dette problemet:
{
"index": "/index.html",
"assetGroups": [
{
"name": "App",
"installMode": "prefetch",
"resources": {
"files": [
"/favicon.ico",
"/index.html",
"/*.css",
"/*.js",
"/!main*.js"
]
}
}, {
"name": "assets",
"installMode": "lazy",
"updateMode": "prefetch",
"resources": {
"files": [
"/assets/**",
"/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)"
]
}
}
]
}
kjør ng build --prod for å bygge appen din på nytt og teste den.
OPPDATERING:
For å forsikre deg om at dette ikke er et problem med nettleserens hurtigbuffer, send true som det første argumentet til window.location.reload()-metoden. Dette instruerer at omlastingen skal utføres uten hurtigbufrede ressurser fra nettleseren:
rot.komponent.ts
.then(() => {
window.location.reload(true);
});
OPPDATERING 2:
Ifølge dokumentasjonen til Angular er Service Worker som allerede er installert på klienters nettlesere, bufret. Hvis du oppdaterer Service Worker-koden (slik du gjorde nylig), må den nye Service Worker-koden oppdateres i alle nettlesere til klientene som allerede har den installert. Angular-dokumentasjonen sier at det finnes en periodisk sjekk, men den spesifiserer det ikke, som vil sjekke om Service Worker selv trenger en oppdatering og vil gjøre det. https://angular.io/guide/service-worker-devops#service-worker-updates
Kanskje manifestasjonen av problemet bare forekommer i produksjon og ikke i utvikling eller med en Privat nettlesermodus (ren nettleser med ingen SW installert), ser det ut til at dette er tilfelle.
For å bekrefte dette bør du sammenligne den eksisterende nettleserens SW som kjører på siden ved hjelp av utviklerverktøyene i Chrome, med den oppdaterte SW-koden som skal installeres. Hvis de er forskjellige, betyr det at den ennå ikke er oppdatert, og det er det som forårsaker problemet.
Du kan gjøre dette ved å bruke en Chrome-nettleser som viser dette problemet, og ved å gå til Chrome Dev-verktøy -> Applikasjonsfanen -> Service Workers (øverst i venstre panel), og der vil det være SW som er registrert i nettleseren. Klikk på Kilde-lenken, og det vil åpne den faktiske koden til SW. Sammenlign koden linje for linje (eller bruk et diff-verktøy) med den oppdaterte SW-koden som skal kjøre.
Svar #2
Dobbeltsjekk cache-headerne dine. Chrome følger ikke alltid no-cache. Hvis du har besøkt nettstedet nylig, vil Chrome laste index.html fra cache, noe som forårsaker feilen.
Vi har endret vår index.html til no-store, og det så ut til å løse problemet. https://gertjans.home.xs4all.nl/javascript/cache-control.html


