角度における繰延載荷技術



導入
角度は非常に強力なフレームワークです.それはあなたの製品の生活をはるかに簡単にするものがたくさんあります.しかし、大きな設備で、あなたは大きな責任を得ます.
私の現在のプロジェクトでは、我々は、角度が我々のUXとページ・スピード洞察指標に負に影響を及ぼすかなり大きいJavaScript束を生じるという事実に直面しました.
あなたはCodeGymでこれについてもっと学ぶことができます
私は、あなたが既にルータのloadchildrenを通してLazyLoadテクニックと共有モジュールのための1つのモジュールによるコード分割について知っていると思います.
この記事では、あなたにあなたのプロジェクトをより良くするのを助けるもう一つの技術を話したいです.
Web Vitals
レッツゴー!
私は、あなたがすでに@角/CLIをインストールしたと仮定します.
私たちは最初から出発します.最初に新しいプロジェクトを作成します
ng new example
cd example
SRC/APPフォルダーで、1つのコンポーネントを使用してlazyモジュールを作成します.
怠惰.モジュール
@NgModule({
  declarations: [LazyComponent],
})
export class LazyModule {}
怠惰.コンポーネント
@Component({
  selector: "app-lazy",
  template: `
    <div> Hello, I am lazy component!</div>
  `,
})
export class LazyComponent {}
次に、繰返されたローディングコンポーネントを作成する必要があります.それは怠惰なコンポーネントのラッパーになります.
@Component({
  selector: "app-deferred-loading",
  template: `<div #container></div>`,
})
export class DeferredLoadingComponent implements OnInit {
  @ViewChild("container", {read: ViewContainerRef}) container: ViewContainerRef;

  constructor(
    private compiler: Compiler,
    private injector: Injector,
  ) { }

  ngOnInit(): void {
    this.load();
  }

  async load(): Promise<void> {
    const { module, component } = await this.getContent();
    const moduleFactory = await this.compiler.compileModuleAsync(module);
    const moduleRef = moduleFactory.create(this.injector);
    const componentFactory = moduleRef.componentFactoryResolver.resolveComponentFactory(component);
    const { hostView, instance } = componentFactory.create(this.injector);
    this.container.insert(hostView);
  }

  private async getContent(): Promise<{ module: any, component: any }> {
    const [moduleChunk, componentChunk] = await Promise.all([
      import("./lazy/lazy.module"),
      import("./lazy/lazy.component")
    ]);
    return {
      module: moduleChunk["LazyModule"],
      component: componentChunk["LazyComponent"]
    };
  }
}
シングルコンポーネントではなく、独自のサービスと子コンポーネントを持つウィジェットを扱う方法を示したいので、モジュールとコンポーネントの両方をロードしなければなりません.
残念ながら、各アングルモジュールが独自のコンパイルコンテキストを持っているので、単にコードをロードして使用を開始できません.そういうわけで、我々は でこれを解決しなければなりません.
まず、モジュールをコンパイルし、プロバイダーを解決します.
第二に、コンポーネントを解決し、DOMに動的に注入する.
今我々は我々のアプリでそれを使用することができます.コンポーネント.TS
@Component({
  selector: 'app-root',
  template: `
    <app-deferred-loading *ngIf="isReadyForLazyComponent"></app-deferred-loading>
    <button (click)="load()">Load and bootstrap</button>
  `,
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  isReadyForLazyComponent: boolean;

  load(): void {
    this.isReadyForLazyComponent = true;
  }
}
ボタンをクリックするとJavaScriptのコードを読み込み、コンパイル、および角度のブランドの新しい遅延コンポーネントをレンダリングします.
jit compiler
チャレンジ- 1
我々はいくつかのデータを渡したり、アプリとの対話をしたい場合.コンポーネント.コンポーネント?
このような状況を扱うのが最善の方法かどうかは分かりませんが、
  • アプリを変更します.データを入力に送信し、出力をリッスンするコンポーネント
  • @Component({
      selector: 'app-root',
      template: `
        <button (click)="load()">Load and bootstrap</button>
        <app-deferred-loading *ngIf="isReadyForLazyComponent" [props]="props"></app-deferred-loading>
      `,
      styleUrls: ['./app.component.css']
    })
    export class AppComponent {
      isReadyForLazyComponent: boolean;
    
      props = {
        name: "Spike",
        onClick: this.handleLazyComponentClick.bind(this),
      };
    
      load(): void {
        this.isReadyForLazyComponent = true;
      }
    
      handleLazyComponentClick(val): void {
        console.log(`${val}: from lazy component!`)
      }
    }
    
    2 . lazyを変更するコンポーネントを受信し、データを放出する
    @Component({
      selector: "app-lazy",
      template: `
        <div>
          <hr>
          <div> Hello, I am lazy component!</div>
          <button (click)="handleClick()">Data from child</button>
          <hr>
        </div>
      `,
    })
    export class LazyComponent {
      @Output() onClick: EventEmitter<string> = new EventEmitter();
      @Input() name: string;
    
      handleClick(): void {
        this.onClick.emit(`My name is ${this.name}!`);
      }
    }
    
  • アプリを接続します.コンポーネントと怠惰.遅延読み込みを行うコンポーネント.コンポーネント
  • @Component({
      selector: "app-deferred-loading",
      template: `<div #container></div>`,
    })
    export class DeferredLoadingComponent implements OnInit, OnDestroy {
      ...
    
      @Input() props: any;
    
      private isDestroyed$: Subject<void> = new Subject();
    
      ...
    
      async load(): Promise<void> {
        ...
    
        Object.entries(this.props).forEach(([key, value]: [string, any]) => {
          if (instance[key] && instance[key].observers) {
            instance[key]
              .pipe(takeUntil(this.isDestroyed$))
              .subscribe((e) => value(e));
          } else {
            instance[key] = value;
          }
        });
    
        this.container.insert(hostView);
      }
    
      private async getContent(): Promise<{ module: any, component: any }> {
        ...
      }
    
      ngOnDestroy(): void {
        this.isDestroyed$.next();
        this.isDestroyed$.complete();
      }
    }
    
    今、我々は怠惰にデータを渡すことができます.コンポーネント入力と出力のためのリッスン
    すばらしい.

    チャレンジ- 2
    我々がクリックででなく我々のものをロードする必要があるならば、Viewportに入ることによって?
    この場合、 は救助に来る.
    まず、我々のアプリを準備する必要があります.コンポーネント
      @Component({
      selector: 'app-root',
      template: `
        <button (click)="load()">Load and bootstrap</button>
        <div class="first-content"></div>
        <app-deferred-loading [props]="props"></app-deferred-loading>
      `,
      styles: [`.first-content {
        background-color: cornflowerblue;
        width: 100%;
        height: 120vh;
      }`]
    })
    
    よりも、読み込み読み込みを編集します.コンポーネント
    ...
    export class DeferredLoadingComponent implements OnInit, OnDestroy {
      ....
    
      private intersectionObserver: IntersectionObserver;
      private isDestroyed$: Subject<void> = new Subject();
    
      constructor(
        private compiler: Compiler,
        private injector: Injector,
        private element: ElementRef,
        @Inject(PLATFORM_ID) private platformId: Object,
      ) { }
    
      ngOnInit(): void {
        if (isPlatformBrowser(this.platformId)) {
          if ("IntersectionObserver" in window) {
            this.intersectionObserver = this.createIntersectionObserver();
            this.intersectionObserver.observe(this.element.nativeElement);
          } else {
            this.load();
          }
        }
      }
    
      ...
    
      private createIntersectionObserver(): IntersectionObserver {
        return new IntersectionObserver(entries => this.checkForIntersection(entries));
      }
    
      private checkForIntersection(entries: IntersectionObserverEntry[]) {
        entries.forEach((entry: IntersectionObserverEntry) => {
          if (this.isIntersecting(entry)) {
            this.load();
            this.intersectionObserver.unobserve(this.element.nativeElement);
          }
        });
      }
    
      private isIntersecting(entry: IntersectionObserverEntry): boolean {
        return (<any>entry).isIntersecting && entry.target === this.element.nativeElement;
      } 
    
      ngOnDestroy(): void {
        ...
        if (this.intersectionObserver) {
          this.intersectionObserver.unobserve(this.element.nativeElement);
        }
      }
    }
    
    これは、スタンダールテクニック、Intersection Observerで紹介されています.
    さて、怠け者.コンポーネントはページ上でブートストラップされます.
    私の記事が誰かが製品をより良くするのを助けることを願っています.
    P . S .ソースコードはLazy Loading Images and Videoにあります.