#javascript #angular #deployment #service-worker
Вопрос:
Я использую angular 12 и использую Service Worker для развертывания новых версий.
Похоже, что каждый раз, когда я развертываю новую версию для производства (и по какой-то причине не на этапе подготовки). Некоторые пользователи получают кучу ошибок, таких как
Не перехвачен (в обещании): ошибка загрузки фрагмента XXX не удалась.
Существует довольно много сообщений об этой проблеме, но мне кажется, что решение зависит от конкретного случая.
Разница, которую я имею в производстве, по сравнению с stg, просто.
Я вызываю enableProdMode();
У меня есть настройка service worker следующим образом :
ServiceWorkerModule.register('ngsw-worker.js', {
enabled:
environment.env === Environment.PRODUCTION || environment.env === Environment.STAGING,
registrationStrategy: 'registerWhenStable:30000',
}),
И моя конфигурация выглядит так
{
"$schema": "../../node_modules/@angular/service-worker/config/schema.json",
"index": "/index.html",
"assetGroups": [
{
"name": "app",
"installMode": "prefetch",
"resources": {
"files": ["/favicon.ico", "/index.html", "/manifest.webmanifest", "/*.css", "/*.js"]
}
},
{
"name": "assets",
"installMode": "lazy",
"updateMode": "lazy",
"resources": {
"files": ["/assets/**", "/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)"]
}
}
]
}
И вот как я показываю обновления (в основном баннер, который можно игнорировать) :
@Component({
selector: 'app-live-update-notifier',
templateUrl: './live-update-notifier.component.html',
styleUrls: ['./live-update-notifier.component.scss'],
})
export class LiveUpdateNotifierComponent implements OnDestroy {
hasUpdate = false;
isIgnored = false;
private subscription = new SubSink();
constructor(
@Inject(DOCUMENT) private document: Document,
private swUpdate: SwUpdate,
private appRef: ApplicationRef,
private changeDetectorRef: ChangeDetectorRef
) {
const appIsStable$ = this.appRef.isStable.pipe(first((isStable) => isStable === true));
const everySixHours$ = interval(6 * 60 * 60 * 1000);
const everySixHoursOnceAppIsStable$ = concat(appIsStable$, everySixHours$);
if (swUpdate.isEnabled) {
this.subscription.add(
everySixHoursOnceAppIsStable$.subscribe(() => swUpdate.checkForUpdate()),
this.swUpdate.available.subscribe((evt) => {
if (evt.type === 'UPDATE_AVAILABLE') {
this.hasUpdate = true;
this.changeDetectorRef.detectChanges();
}
})
);
}
}
ngOnDestroy(): void {
this.subscription.unsubscribe();
}
updateApp() {
this.document.location.reload();
}
skipNotification() {
this.isIgnored = true;
}
}
Если у вас есть какие-либо подсказки, которые могли бы помочь в решении, спасибо.
Редактировать :
У меня есть куча лениво загруженных модулей по всему моему приложению. Я думал, причина в том, что какой-то модуль не был загружен до новой версии, и когда пользователь переходит к ним после развертывания, у них возникает проблема с этим фрагментом. Но я попытался эмулировать эту проблему с помощью
- редактирование модулей A и B
- развернуть
- перейдите к модулю A
- редактирование модулей A и B
- развернуть
- перейдите к старому модулю B из старого A, поэтому без обновления версии приложения RES: старый B по-прежнему загружается нормально
так что я не думаю, что это проблема
Комментарии:
1. Можете ли вы добавить более подробную ошибку?
2. @ApoorvaChikara sentry.io/share/issue/32f52037029f43d8b355456ffbfe56de
3. или другой sentry.io/share/issue/87e6a59fdc4845f3b31b2abf91aebff4
4. Я знаю, что это из-за несоответствия хэш-файла, который обновляется при новом развертывании, но я не понимаю, почему это происходит, поскольку он должен просто «installMode»: «предварительная выборка», но не жестко удалять кэшированные файлы
Ответ №1:
Это может быть вызвано разными причинами. Но есть обходной путь, который я использовал несколько месяцев назад благодаря spock123.
Я цитирую его:
Мы, конечно, используем службу Service Worker
swUpdate
для получения уведомлений о доступности новой версии. Однако иногда кажется, что не все лениво загруженные фрагменты обновляются при появлении новой версии SW.
В основном обходным путем является перезагрузка приложения, когда вы сталкиваетесь с ошибкой bundle not found
, которая вызывает ошибку ChunkLoadError
.
Вы должны прослушивать ошибку глобально, и если приложение столкнулось с этой ошибкой, вы просто перезагружаете страницу.
Инструкции по настройке глобального обработчика ошибок
Обновить:
Я спрашиваю из angular.io:
Обработка невосстановимого состояния
В некоторых случаях версия приложения, используемая сервисным работником для обслуживания клиента, может находиться в нерабочем состоянии, из которого невозможно восстановить без полной перезагрузки страницы.
Например, представьте следующий сценарий:
- Пользователь открывает приложение в первый раз, и работник службы кэширует последнюю версию приложения. Предположим, что кэшированные ресурсы приложения включают
index.html
в себя ,main.<main-hash-1>.js
иlazy-chunk.<lazy-hash-1>.js
. - Пользователь закрывает приложение и некоторое время не открывает его.
- Через некоторое время на сервере развертывается новая версия приложения. Эта более новая версия включает файлы
index.html
main.<main-hash-2>.js
иlazy-chunk.<lazy-hash-2>.js
(обратите внимание, что хэши теперь другие, потому что содержимое файлов изменилось). Старая версия больше не доступна на сервере. - Тем временем браузер пользователя решает удалить
lazy-chunk.<lazy-hash-1>.js
его из своего кэша. Браузеры могут решить удалить определенные (или все) ресурсы из кэша, чтобы освободить место на диске. - Пользователь снова открывает приложение. Сервисный работник обслуживает последнюю версию, известную ему на данный момент, а именно старую версию (
index.html
иmain.<main-hash-1>.js
). - В какой-то более поздний момент приложение запрашивает отложенный пакет,
lazy-chunk.<lazy-hash-1>.js
. - Работник службы не может найти ресурс в кэше (помните, что браузер удалил его). Он также не может получить его с сервера (поскольку сервер теперь имеет только
lazy-chunk.<lazy-hash-2>.js
более новую версию).
В предыдущем сценарии работник службы не может обслуживать ресурс, который обычно кэшируется. Эта конкретная версия приложения повреждена, и нет способа исправить состояние клиента без перезагрузки страницы. В таких случаях работник службы уведомляет клиента, отправляя событие UnrecoverableStateEvent . Подпишитесь на SwUpdate#unrecoverable, чтобы получать уведомления и обрабатывать эти ошибки.
handle-невосстановимый-state.service.ts:
@Injectable()
export class HandleUnrecoverableStateService {
constructor(updates: SwUpdate) {
updates.unrecoverable.subscribe(event => {
notifyUser(
'An error occurred that we cannot recover from:n'
event.reason
'nnPlease reload the page.'
);
});
}
}
Комментарии:
1. спасибо, да, я тоже нашел это решение, но оно больше похоже на обходной путь, чем на правильное решение. Я делаю это в данный момент в процессе производства, чтобы избежать такой проблемы. Но знание того, почему все фрагменты загружаются неправильно из SW, является моей главной заботой. Является ли это проблемой непосредственно для angular SW?
2. Я не могу быть уверен, но я так думаю
3. это может быть вызвано буквально МНОЖЕСТВОМ причин. сам angular, управление версиями браузеров, конфигурации сборки, …
4. хорошо, я думаю, нет простого способа узнать, в чем проблема в моем случае ^^
5. Я обновил ответ @Bobby
Ответ №2:
Конфигурация для сервера nginx:
location ~* .(?:css|js)$ {
add_header Cache-Control "public, max-age=7200";
}
Это подскажет браузеру кэшировать файлы в течение 2 часов, надеюсь, к тому времени браузер обновит страницу и получит новые файлы.