Angular Uncaught (в обещании): ошибка загрузки: загрузка фрагмента XXXfailed

#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 часов, надеюсь, к тому времени браузер обновит страницу и получит новые файлы.