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

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

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

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

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

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


person Heather Roberts    schedule 24.07.2017    source источник
comment
Как вы в итоге это исправили?   -  person Michael    schedule 23.08.2017
comment
ты исправил это?   -  person Rakeschand    schedule 28.09.2017


Ответы (1)


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

В серверном приложении я извлекаю из головы стили angular и добавляю их в службу передачи состояния. В клиентском приложении я получаю стили из переданного состояния и добавляю их в голову. Как только 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();
  }
}
person Heather Roberts    schedule 16.11.2017