Как лениво и динамически загрузить компонент в Angular

Как лениво и динамически загрузить компонент в Angular

Существует три способа использования компонента в приложении Angular.

  • Загрузка при изменении маршрута
  • Использование в качестве дочернего компонента
  • Динамическая загрузка по требованию

Однако эта статья посвящена динамической и ленивой загрузке компонента. Основным преимуществом ленивой загрузки компонента является уменьшение размера начального пакета и загрузка компонента в браузер только при необходимости.

Допустим, у вас есть компонент под названием GreetComponent, как показано в следующем блоке кода,

import { Component, EventEmitter, Input, Output } from '@angular/core';
import { CommonModule } from '@angular/common';
 
const template = `
   <h2>{{message}} </h2>
   <button (click)='sendMessage()'>Send Message </button>
`
@Component({
  selector: 'app-greet',
  standalone: true,
  imports: [CommonModule],
  template: template
})
export class GreetComponent {
  @Input({ required: true }) message?: string;
  @Output() messageEvent = new EventEmitter<boolean>();
  sendMessage(): void {
    this.messageEvent.emit(true);
  }
} 

Компонент GreetComponent имеет украшенное свойство @Input и украшенный @Output() EvenEmmiter. Компонент FooComponent использует его в качестве дочернего компонента, как показано в следующем блоке кода.

const template = `
   <app-greet [message]='message' (messageEvent)='sendMessage($event)'></app-greet>
`
@Component({
  selector: 'app-foo',
  standalone: true,
  imports: [CommonModule,
    GreetComponent],
  template : template
})
export class FooComponent {
   message = "data from parent"
 
   sendMessage(m:boolean){
      console.log(m);
   }
}

Здесь стоит обратить внимание на несколько моментов,

  • GreetComponent является частью массива imports.
  • GreetComponent используется в шаблоне.

Из-за этих двух моментов, каждый раз, когда Angular компилирует FooComponent, он также включает GreetComponent, увеличивая размер пакета, содержащего FooComponent.

_Одним из основных преимуществ динамической (ленивой) загрузки компонента является уменьшение размера пакета, поскольку он загружается в браузер только тогда, когда это необходимо. _

Допустим, компонент GreetComponent должен загружаться динамически и лениво при нажатии кнопки в FooComponent. Для этого, 

  • Добавьте кнопку
  • Удалите из шаблона FooComponent
  • Удалить GreetComponent из массива импортов. [Важно]
const template = `
   <button (click)='loadComponent()'>Load Greet Component </button>
`
@Component({
  selector: 'app-foo',
  standalone: true,
  imports: [CommonModule],
  template: template
})
export class FooComponent {
  message = "data from parent"
  greetcomp: any;

Чтобы динамически загрузить компонент, инжектируйте ViewContainerRef с помощью функции inject или инжекта конструктора.  

vcr = inject(ViewContainerRef);

После этого импортируйте файл, содержащий GreetComponent, с помощью оператора import.  Оператор import используется в JavaScript для динамической загрузки файла.

const { GreetComponent } = await import(’../greet/greet.component’);

После импорта файла используйте метод CreateComponent в ViewContainerRef.

this.greetcomp = this.vcr.createComponent(GreetComponent);

Вы можете получить доступ ко всем свойствам и событиям динамически загруженных компонентов, используя метод экземпляра. Так, свойству message можно передать значение a, как показано в следующем блоке кода,

this.greetcomp.instance.message = “Hello dynamic Component”;

Вы можете подписаться на EventEmitter, украшенный @Output, как показано далее,

this.greetcomp.instance.messageEvent.subscribe((data:any)=>{
        console.log(data);
      })

Собрав все вместе, вы можете лениво загрузить GreetComponent нажатием кнопки в FooComponent, как показано в следующем листинге кода,

const template = `
   <button (click)='loadComponent()'>Load Greet Component </button>
`
@Component({
  selector: 'app-foo',
  standalone: true,
  imports: [CommonModule],
  template: template
})
export class FooComponent {
  message = "data from parent"
  greetcomp: any;
  vcr = inject(ViewContainerRef);
  
  async loadComponent() {
    this.vcr.clear();
    const { GreetComponent } = await import('../greet/greet.component');
    this.greetcomp = this.vcr.createComponent(GreetComponent);
    if (this.greetcomp) {
      this.greetcomp.instance.message = "Hello dynamic Component";
 
      this.greetcomp.instance.messageEvent.subscribe((data:any)=>{
        console.log(data);
      })
    }
 
  }
}

В настоящее время Angular загружает GreetComponent в конце после всех DOM-элементов FooComponent. Чтобы загрузить его в определенный div-блок, считайте div-блок как ViewChild и ViewConatinerRef.  В другом посте я расскажу об этом подробнее.

Надеюсь, эта статья будет вам полезна. Спасибо за прочтение.