Угловой универсальный стиль FOUC при переходе с сервера на клиент

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

Стили для заголовка и нижнего колонтитула моего сайта все время выглядят хорошо, но компоненты, которые загружаются внутри <router-outlet> Элементами являются те, которые не имеют правильного стиля.

Я использую Preboot для управления переходом между сервером и клиентом и ничего не делаю за пределами стандартной конфигурации. Я экспериментировал с использованием @ngx-universal/state-transfer а также @ngx-cache библиотеки, но я не думаю, что это то, что мне нужно.

Я использую ленивые загруженные маршруты, но я экспериментировал с их удалением, и ошибка та же. Я тоже пробовал настройку { initialNavigation: 'enabled' } в моем конфиге маршрутизации.

Я использую webpack для создания приложения на стороне сервера и Angular CLI для приложения на стороне клиента, в основном на основе этого проекта, и использую компилятор AOT. Любые идеи будут очень признательны, спасибо!

1 ответ

Решение

Таким образом, решение, которое я закончил, заключалось в том, чтобы создать StyleStateTransferService который использует библиотеку @ngx-universal/state-Transfer.

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

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

Обслуживание:

@Injectable()
export class StyleStateTransferService {
  document: any;
  window: Window;
  renderer: Renderer2;
  ngStyleId = 'ng_styles';

  constructor(private stateTransferService: StateTransferService,
              @Inject(DOCUMENT) document: any,
              private winRef: WindowRef,
              private rendererFactory: RendererFactory2) {
    this.window = winRef.nativeWindow;
    this.document = document;
    this.renderer = rendererFactory.createRenderer(this.document, null);
  }

  addStylesToState() {
    const styles: string[] = this.document.head.children
      // elements have a weird structure on the server
      // this filters to style tags with the ng-transition attribute that have content
      .filter(el =>
        el.name === 'style' && el.attribs['ng-transition'] && el.firstChild && el.firstChild.data)
      // extract the css content of the style tags
      .map(el => el.firstChild.data.replace(/\n/g, ' '));

    this.stateTransferService.set(this.ngStyleId, styles);
    this.stateTransferService.inject();
  }

  injectStylesFromState() {
    const styles = _.get(this.window, `${DEFAULT_STATE_ID}.${this.ngStyleId}`, []);
    styles.forEach(content => {
      const styleEl = this.renderer.createElement('style');
      // set this attribute so we can remove them later
      this.renderer.setAttribute(styleEl, 'ng-state-transfer', null);
      this.renderer.setProperty(styleEl, 'innerHTML', content);
      this.renderer.appendChild(this.document.head, styleEl);
    });
  }

  cleanupInjectedStyles() {
    Array.from(<HTMLElement[]>this.document.head.children)
      .filter(htmlEl => htmlEl.tagName === 'STYLE' && htmlEl.hasAttribute('ng-state-transfer'))
      .forEach(styleEl => this.renderer.removeChild(this.document.head, styleEl));
}

Использование его в модулях сервера и браузера (очистка происходит в компоненте приложения, но, похоже, не стоит показывать):

export class AppServerModule {
  constructor(private appRef: ApplicationRef,
              private styleStateTransferService: StyleStateTransferService) {
    // waits until the app has fully initialised before adding the styles to the state
    this.appRef.isStable
      .filter(isStable => isStable)
      .first()
      .subscribe(() => {
        this.styleStateTransferService.addStylesToState();
      });
  }
}

export class AppBrowserModule {
  constructor(private styleStateTransferService: StyleStateTransferService) {
    this.styleStateTransferService.injectStylesFromState();
  }
}
Другие вопросы по тегам