MST
星途 面试题库

面试题:Angular中ngIf指令的底层实现及扩展

深入探讨Angular中ngIf指令的底层实现逻辑,从源码角度分析它是如何管理视图的创建、销毁和更新的。如果需要为ngIf指令扩展一个新的功能,例如在视图销毁时执行一段自定义动画,你会如何实现?
15.5万 热度难度
前端开发Angular

知识考点

AI 面试

面试题答案

一键面试

1. Angular中ngIf指令底层实现逻辑

  • 视图创建
    • 在Angular的模板解析阶段,ngIf指令会被解析。当ngIf绑定的表达式值为true时,ngIf会触发视图的创建。
    • 从源码角度看,ngIf指令对应的类是NgIf,它继承自NgIfContextNgIf指令内部维护了一个ViewContainerRef,这是视图容器的引用,用于创建和管理视图。
    • 当表达式值为true时,NgIf通过ViewContainerRefcreateEmbeddedView方法来创建嵌入视图。这个方法会从模板中生成新的视图,并将其插入到DOM中。
  • 视图销毁
    • ngIf绑定的表达式值变为false时,ngIf会销毁当前的视图。
    • NgIf指令会调用ViewContainerRefclear方法,该方法会移除当前视图容器中的所有视图,从而实现视图的销毁。同时,相关的变更检测机制会停止对这些视图的检测,释放资源。
  • 视图更新
    • NgIf指令通过变更检测机制来监听绑定表达式的值的变化。当表达式的值发生变化时,Angular的变更检测机制会触发。
    • 如果表达式从false变为true,则重新创建视图;如果从true变为false,则销毁视图。NgIf指令依赖于Angular的默认变更检测策略(默认是DefaultChangeDetectionStrategy),该策略会在每个组件的变更检测周期内检查表达式的值。

2. 为ngIf指令扩展在视图销毁时执行自定义动画的实现

  • 步骤一:创建动画服务
    • 首先,创建一个动画服务,用于定义和执行动画逻辑。例如:
import { Injectable } from '@angular/core';
import { AnimationBuilder, AnimationPlayer } from '@angular/animations';

@Injectable({
  providedIn: 'root'
})
export class CustomAnimationService {
  constructor(private animationBuilder: AnimationBuilder) {}

  playDestroyAnimation(element: HTMLElement): AnimationPlayer {
    const animation = this.animationBuilder.build([
      // 定义动画逻辑,例如淡入淡出
      style({ opacity: 1 }),
      animate('500ms ease-out', style({ opacity: 0 }))
    ]);
    const player = animation.create(element);
    player.play();
    return player;
  }
}
  • 步骤二:扩展NgIf指令
    • 创建一个自定义指令,继承自NgIf指令,并在视图销毁时调用动画服务。
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
import { NgIf, NgIfContext } from '@angular/common';
import { CustomAnimationService } from './custom - animation.service';

@Directive({
  selector: '[appExtendedNgIf]',
  providers: [
    {
      provide: NgIf,
      useExisting: ExtendedNgIf
    }
  ]
})
export class ExtendedNgIf extends NgIf {
  constructor(
    templateRef: TemplateRef<NgIfContext>,
    viewContainer: ViewContainerRef,
    private customAnimationService: CustomAnimationService
  ) {
    super(templateRef, viewContainer);
  }

  override set ngIf(condition: boolean) {
    if (!condition && this._viewRef) {
      const element = this._viewRef.rootNodes[0] as HTMLElement;
      const player = this.customAnimationService.playDestroyAnimation(element);
      player.onDone(() => {
        super.ngIf = condition;
      });
    } else {
      super.ngIf = condition;
    }
  }
}
  • 步骤三:在模板中使用
    • 在模板中使用自定义的appExtendedNgIf指令代替原生的ngIf指令。
<div *appExtendedNgIf="showDiv">
  <p>This is a div that can be toggled with custom destroy animation.</p>
</div>
<button (click)="showDiv =!showDiv">Toggle Div</button>

在上述代码中,当appExtendedNgIf绑定的条件变为false时,会先执行自定义的销毁动画,动画完成后再真正销毁视图。