Подтвердить что ты не робот

Как обрабатывать angular2 ленивый отказ маршрута загрузки

Я использую angular 2 ленивую маршрутизацию. Клиент поставляется с webpack2 с использованием AOT и angular -router-loader для пользователей с ленивой загрузкой. Все работает так, как ожидается, когда браузер подключен, то есть я могу успешно перемещаться по маршрутизатору к ленивому загруженному модулю, кусок загружен успешно, и я могу просмотреть компонент и т.д.

Однако, если я эмулирую отсоединение (например, используя опцию "Негативные инструменты разработчика Chrome" ), маршрутизация прерывается, как и ожидалось, потому что не может загрузить соответствующий фрагмент. Ошибка: "Ошибка загрузки блока [номер канала]"

После этого команда маршрутизации не работает, она похожа на маршрутизатор.

Я попытался обработать ошибку, используя глобальный ErrorHandler. Идея заключалась в том, что, возможно, я мог поймать ошибку, прежде чем она сломает маршрутизатор, но похоже, что к тому времени уже слишком поздно. В то время, когда я поймаю ошибку, маршрутизатор не работает.

import { Injectable, ErrorHandler, Injector } from '@angular/core';

import { Router } from '@angular/router';

@Injectable()
export class CustomErrorHandler extends ErrorHandler {
    constructor(private injector: Injector) {
        super(false);
    }

    private get router(): Router {
        return this.injector.get(Router, null);
    }

    public handleError(error: Error) {
        if (/Loading chunk [\d]+ failed/.test(error.message)) {
            console.info('Offline mode');
            let router = this.router;
            if (router) {
                router.navigateByUrl('/offline').then(t => console.debug(t+'')).catch(t=> console.debug(t));
            } else {
                window.location.href = '/';
            }
        }
    }
}

Пользовательский обработчик ошибок работает, потому что печатается сообщение "Offline mode". Инъекция также работает, маршрутизатор не является нулевым, однако навигация маршрутизатора не работает, обещание маршрутизатора не разрешено и не отклонено.

То, что я пытаюсь выполнить, - это обработать ошибку (например, показать информационное сообщение пользователю) и в то же время иметь маршрутизатор в рабочем состоянии, чтобы пользователь мог перемещаться позже (при подключении к Интернету восстанавливается) без перезагрузки всей страницы.

Обновление 1: попытка воспроизведения без использования aot и webpack

Чтобы узнать, является ли это проблемой маршрутизатора angular, я попытался понять, что происходит при попытке работать в автономном режиме с компиляцией jit. Я использовал: angular rounk plunker, перешел на вкладку входа в систему, переключился в автономный режим и нажал логин. При загрузке модуля администрирования был зарегистрирован ряд ошибок "Ошибка: XHR error load". В конечном итоге навигация не удалась, однако после этого маршрутизация не нарушилась. Я смог перейти на другие вкладки, и после переключения обратно я даже смог перейти к администратору. Возможно, проблема заключается в том, как angular -router-loader плагин webpack пытается загрузить модуль.

Обновление 2: Кажется, это известная проблема

Функция: более надежный загрузчик модулей для маршрутизатора

4b9b3361

Ответ 1

Я получал эту ошибку каждый раз, когда выгуливал собаку и пытался проверить мой сайт в лифте. Удивило, что нет лучшей документации для обработки ошибок - и особенно всеми не решениями в проблеме github.

С учетом сказанного, есть событие NavigationError от маршрутизатора. Таким образом, вы можете перехватить это, предотвратить запуск обычного обработчика ошибок и показать всплывающее окно. Хорошая вещь об этом событии состоит в том, что он содержит полный предпринятый URL - вместо бесполезной ошибки Error: Uncaught (in promise): Error: Loading chunk common failed..

Итак, вот что я придумала.

В app.component.ts (необходимо ввести private router: Router)

Вызовите функцию initErrorHandler() из вашего конструктора.

// filter router events of type NavigationError
navigationError$ = this.router.events.pipe
                   (filter((e): e is NavigationError => e instanceof NavigationError));

// Keep a reference to the error handled, so we can ignore it later
_errorHandled: any;
initErrorHandler()
{
   this.navigationError$.subscribe((evt) => 
   {
       let showPopup = false;

       // handle known error messages that can be retried
       if (evt.error.message.startsWith('Loading chunk')) 
       {
           showPopup = true;
       }
       else 
       {
          debugger;   // consider handling other messages
       }

       if (showPopup) {

          // display a popup however you want...
          // I use Angular Material, and popup is MY OWN dialog box service
          this.popup.showChoicesDialog({

              title: 'Network Error',
              desc: 'Sorry we were unable to load that page',
              fullscreen: true,

              choices: [
                  {
                      action: () => 
                      {
                          // this is the important part - retry URL
                          this.router.navigateByUrl(evt.url)
                      },
                      buttonText: 'Retry'
                  }
              ]
          });

          // mark the error as handled to avoid global handler
          this._errorHandled = evt.error;
      }

      // normal global error handler
      this.zone.onError.subscribe((error: any) => 
      {
          // ignore errors that were already handled but couldn't be cancelled
          if (error.rejection === this._errorHandled)
          {
              this._errorHandled = undefined;
              return;
          }

          // Whatever you want to do here for true unhandled errors
          console.warn(error); 
      });
  }

В зависимости от вашей пользовательской базы вам нужно изменить сообщение и поведение, но общий подход, похоже, работает.

Я еще не знаю всего возможного NavigationError, поэтому сейчас он просто обрабатывает ошибки Loading chunk. Пожалуйста, прокомментируйте, если вы обнаружите альтернативные сообщения об ошибках.

При тестировании обязательно используйте режим "Оффлайн". В быстром тестировании, если я включаю режим "Автономно", чтобы вызвать ошибку, то попробуйте снова выполнить навигацию, она действительно успешно перемещается после отключения "Автономно".

enter image description here

Примечание. Многие темы для обсуждения о "чанках" связаны с проблемами кэширования после нового развертывания. Я еще не рассмотрел эти проблемы, но, скорее всего, сообщение об ошибке будет отличаться, так что вы могли бы показать другое всплывающее окно.

Ответ 2

Предотвратите сбой маршрутизации, а не обрабатывайте его.

Вы можете предварительно загрузить ваши лениво загруженные модули, чтобы они загружались на клиент после того, как браузер обнаружит неактивность, таким образом, они будут доступны в автономном режиме.


Отметьте здесь, чтобы предварительно загрузить все модули: PreloadAllModules


Вы также можете создать собственную стратегию загрузки и предварительно загружать только выбранные модули:

таможенно-preloading.ts
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/mergeMap';
import { PreloadingStrategy, Route } from '@angular/router';

export class CustomPreloading implements PreloadingStrategy {

   public preload(route: Route, fn: () => Observable<boolean>): Observable<boolean> {
     if (route.data && route.data.preload) {
       return Observable.of(true).flatMap((_: boolean) => fn());
     } else {
       return Observable.of(false);
     }
   }
}
приложение-routing.module.ts
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { CustomPreloading } from './custom-preloading';

const routes: Routes = [
  { ..other paths.. },
  { path: 'lazy', loadChildren: 'app/lazy/lazy.module#LazyModule', data: { preload: true }  }
];

export const appRoutingProviders = [
   CustomPreloading
];

@NgModule({
   imports: [RouterModule.forRoot(routes, { preloadingStrategy: CustomPreloading })],
   exports: [RouterModule]
})
export class AppRoutingModule { }