Angular 17 构造函数与 OnInit

Bja*_*ted 3 constructor typescript angular angular-signals

与 Constructor 和 ngOnInit 之间的差异相关但不相同。

在 Angular 16 和 17 中,我们现在可以使用takeUntilDestroyed运算符和信号。这两种方法似乎在注入器上下文中效果最好,或者至少具有不需要传递其中一种的优点。

问题

所以问题是(再次)我们应该将初始化放在构造函数(或成员字段)中还是仍然使用OnInit?其次,使用构造函数而不是有任何陷阱吗OnInit

注意:对于初始化,我的意思是使用httpClient来获取数据以显示在页面上。使用数据映射等设置 RxJS 管道。读取路由参数等。

搁置以下内容:

  • 如果我们想使用变量,我们需要使用OnInitorOnChanges@Input()
  • 个人喜好

附加信息

根据旧的 Angular.io 站点的组件生命周期文档

组件的构造应该便宜且安全。例如,您不应该在组件构造函数中获取数据。您不必担心新组件在测试中创建时或在您决定显示它之前会尝试联系远程服务器。ngOnInit() 是组件获取其初始数据的好地方。

但新的 Angular.dev 站点中不存在此文档。

他们的新教程之一也在构造函数中进行了数据调用:

  constructor() {
    this.housingService.getAllHousingLocations().then((housingLocationList: HousingLocation[]) => {
      this.housingLocationList = housingLocationList;
      this.filteredLocationList = housingLocationList;
    });
  }
Run Code Online (Sandbox Code Playgroud)

概括

Angular 16/17 似乎正朝着在注入上下文(成员字段或构造函数)中完成更多初始化的方向发展。这对性能、稳定性、未来发展有影响吗?

OZ_*_*OZ_ 9

最好在成员字段声明中初始化事物。通过这种方式,您可以将字段声明为readonly,并在同一行中声明它们的类型,并且通常可以推断出它。

class ExampleComponent {
  private readonly userService = inject(UserService);
  private readonly users = this.userService.getUsers();
}
Run Code Online (Sandbox Code Playgroud)

如果某些字段在构造函数中初始化(而不是在参数列表中),那么您将需要 2 行 - 一行用于声明字段(及其类型),另一行用于初始化它:

class ExampleComponent {
  private readonly users: Observable<User[]>;

  constructor(private readonly userService: UserService) {
    this.users = this.userService.getUsers();
  }
}
Run Code Online (Sandbox Code Playgroud)

另外,如果某些字段是在构造函数中初始化的,则在字段初始化之前使用字段时可能会遇到以下情况:

class ExampleComponent {
  private readonly users: Observable<User[]>;

  constructor(private readonly userService: UserService) {
    this.users = this.userService.getUsers();
  }

  private readonly emails = this.users.pipe(
    map((users) => users.map(user => user.email))
  );

}
Run Code Online (Sandbox Code Playgroud)

无论emails位于何处,它都会在 之前初始化constructor(),并且您会收到错误。而实际上代码并不是那么短和简单,所以很容易出现这种情况。

如果字段在 中初始化ngOnInit(),您将无法将它们声明为readonly,即使它们可以是只读的(这是一个非常有用的保护措施)。

ngOnInit()ngOnChanges()仅当您需要一次读取多个Input()属性并基于其中多个属性执行逻辑时才需要和。不过,我建议使用 setter 和computed()

class ExampleComponent {
   private readonly $isReadonly = signal<boolean>(false);
   private readonly $hasEditPermissions = signal<boolean>(true);
   // 
   protected readonly $isEditButtonEnabled = computed(() => {
     return !this.$isReadonly() && this.$hasEditPermissions();
   });


   @Input() set isReadonly(isReadonly: boolean) {
     this.$isReadonly.set(isReadonly);
   }

   @Input() set hasEditPermissions(hasEditPermissions: boolean) {
     this.$hasEditPermissions.set(hasEditPermissions);
   }
}
Run Code Online (Sandbox Code Playgroud)

如果使用信号输入,该代码将缩短 6 行。

您不必担心新组件在测试下创建时或决定显示它之前会尝试联系远程服务器

在测试中,通过模拟解决。|async管道并@defer解决第二个问题。另外,如果您想将某些可观察量转换为信号,并且不想在模板的该部分可见之前进行订阅,则 NG 扩展库中有一个辅助函数:toLazySignal()