协慌网

登录 贡献 社区

构造函数和 ngOnInit 之间的区别

Angular 默认提供生命周期钩子ngOnInit

如果我们已经有一个constructor ,为什么要使用ngOnInit

答案

Constructor是在实例化类时执行的类的默认方法,并确保在类及其子类中正确初始化字段。 Angular 或更好的 Dependency Injector(DI)分析构造函数参数,当它通过调用new MyClass()创建新实例时,它会尝试查找与构造函数参数类型匹配的提供程序,解析它们并将它们传递给构造函数,如

new MyClass(someArg);

ngOnInitngOnInit调用的生命周期钩子,表示 Angular 已完成创建组件。

我们必须导入OnInit以便像这样使用(实际上实现OnInit不是强制性的,但被认为是良好的做法):

import {Component, OnInit} from '@angular/core';

然后使用OnInit的方法我们必须在类中实现。

export class App implements OnInit{
  constructor(){
     //called first time before the ngOnInit()
  }

  ngOnInit(){
     //called after the constructor and called  after the first ngOnChanges() 
  }
}

在初始化指令的数据绑定属性后,实现此接口以执行自定义初始化逻辑。在第一次检查指令的数据绑定属性之后,以及在检查其任何子项之前,立即调用 ngOnInit。实例化指令时仅调用一次。

大多数情况下,我们使用ngOnInit进行所有初始化 / 声明,并避免在构造函数中工作。构造函数应仅用于初始化类成员,但不应该执行实际的 “工作”。

所以你应该使用constructor()来设置依赖注入,而不是其他。 ngOnInit()是 “开始” 的最佳位置 - 它是解析组件绑定的地方 / 时间。

有关更多信息,请参阅:

文章Consularctor 和 Angular 中的 ngOnInit 之间的本质区别探讨了多个视角的差异。这个答案提供了与组件初始化过程相关的最重要的差异解释,它也显示了不同的用法。

Angular bootstrap 过程包括两个主要阶段:

  • 构建组件树
  • 运行变化检测

当 Angular 构造组件树时,将调用组件的构造函数。所有生命周期挂钩都被称为运行更改检测的一部分。

当 Angular 构造组件树时,已经配置了根模块注入器,因此您可以注入任何全局依赖项。此外,当 Angular 实例化子组件类时,父组件的注入器也已设置,因此您可以注入在父组件上定义的提供程序,包括父组件本身。组件构造函数是在注入器上下文中调用的唯一方法,因此如果您需要任何依赖项,那么这是获取这些依赖项的唯一位置。

当 Angular 开始更改检测时,构造组件树并调用树中所有组件的构造函数。此外,每个组件的模板节点都会添加到 DOM 中。在更改检测期间处理@Input通信机制,因此您不能期望在构造函数中具有可用的属性。它将在ngOnInit之后ngOnInit

让我们看一个简单的例子。假设您有以下模板:

<my-app>
   <child-comp [i]='prop'>

所以 Angular 开始引导应用程序。正如我所说,它首先为每个组件创建类。所以它调用MyAppComponent构造函数。它还创建了一个 DOM 节点,它是my-app组件的主机元素。然后继续为child-comp创建一个 host 元素并调用ChildComponent构造函数。在这个阶段,它并不真正关心i输入绑定和任何生命周期钩子。因此,当此过程完成时,Angular 最终会得到以下组件视图树:

MyAppView
  - MyApp component instance
  - my-app host element data
       ChildCompnentView
         - ChildComponent component instance
         - child-comp host element data

然后才运行更改检测并更新my-app绑定,并在 MyAppComponent 类上调用ngOnInit 。然后继续更新child-comp的绑定并在 ChildComponent 类上调用ngOnInit

您可以在构造函数或ngOnInit执行初始化逻辑,具体取决于您需要的内容。例如,文章以下是在评估 @ViewChild 查询之前如何获取 ViewContainerRef,以显示在构造函数中可能需要执行的初始化逻辑类型。

以下是一些有助于您更好地理解该主题的文章:

我认为最好的例子是使用服务。假设当我的组件被 “激活” 时,我想从我的服务器获取数据。假设我从服务器获取数据之后还想对数据做一些额外的事情,也许我得到一个错误并希望以不同的方式记录它。

使用 ngOnInit 对构造函数来说非常简单,它还限制了我需要向应用程序添加多少个回调层。

例如:

export class Users implements OnInit{

    user_list: Array<any>;

    constructor(private _userService: UserService){
    };

    ngOnInit(){
        this.getUsers();
    };

    getUsers(){
        this._userService.getUsersFromService().subscribe(users =>  this.user_list = users);
    };


}

使用我的构造函数,我可以调用我的_userService 并填充我的 user_list,但也许我想用它做一些额外的事情。就像确保一切都是大写的一样,我不完全确定我的数据是如何通过的。

因此,使用 ngOnInit 变得更加容易。

export class Users implements OnInit{

    user_list: Array<any>;

    constructor(private _userService: UserService){
    };

    ngOnInit(){
        this.getUsers();
    };

    getUsers(){
        this._userService.getUsersFromService().subscribe(users =>  this.user_list = users);
        this.user_list.toUpperCase();
    };


}

这使得它更容易看到,因此我在初始化时只需在我的组件中调用我的函数,而不是在其他地方挖掘它。实际上它只是您可以使用的另一种工具,以便将来更容易阅读和使用。另外我发现将函数调用放在构造函数中真的很糟糕!