构造函数和ngOnInit之间的区别

Has*_*P A 946 typescript ngoninit angular

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

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

Par*_*ain 977

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

new MyClass(someArg);
Run Code Online (Sandbox Code Playgroud)

ngOnInit 是一个生命周期钩子,由Angular2调用,表示Angular完成了创建组件.

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

import {Component, OnInit} from '@angular/core';
Run Code Online (Sandbox Code Playgroud)

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

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

  ngOnInit(){
     //called after the constructor and called  after the first ngOnChanges() 
  }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

有关更多信息,请参阅:

  • 没错,最基础的(甚至全部)类的语言有contructors以确保正确初始化顺序尤其是扩展其他类的类,其中一些相当棘手的问题可以想出,就像最后的字段(不知道是否有TS他们)和类似的.Contstructors与Angular2无关,它们是TypeScript特性.在进行一些初始化或某些事件发生以允许组件在某些情况下起作用并使其有机会在适当的时间执行某些任务时,Angular会调用生命周期钩子. (53认同)
  • 与所有"最佳实践"一样,我认为最好也解释一下*为什么*你不应该在构造函数中"工作".Angular团队领导的这篇文章很密集,但可能会有所帮助:http://misko.hevery.com/code-reviewers-guide/flaw-constructor-does-real-work/此外,不太重视所需的咒语实现OnInit(这很容易找到)以及更多关于数据绑定在构造函数中不可用的关键事实. (16认同)
  • 需要注意的是,@Input 值在构造函数中不可访问。 (11认同)
  • https://angular.io/docs/ts/latest/guide/server-communication.html中有一个块引用,它也解释了这一点:"当构造函数很简单,所有实际工作时,组件更容易测试和调试(特别是调用远程服务器)是在一个单独的方法中处理的." - 在这种情况下,该方法是ngOnInit() (8认同)
  • _是Angular2调用的一个生命周期钩子,表示Angular已经完成了创建组件._ - 这不是那个.它表示它已初始化绑定.该组件是先前创建的.见[我的回答](/sf/answers/3180112701/) (3认同)
  • 如果严格模式在“ tsconfig.json”文件中为true(例如“ strict”:true),则必须在“构造函数”中而不是在“ ngOnit”中初始化类成员,例如“ FormGroup”。 (2认同)

Max*_*kyi 135

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

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

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

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

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

当Angular开始更改检测时,构造组件树并调用树中所有组件的构造函数.此外,每个组件的模板节点都会添加到DOM中.该@Input所以你不能指望在构造函数提供的属性的通信机制是改变检测过程中处理.它将在之后提供ngOnInit.

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

<my-app>
   <child-comp [i]='prop'>
Run Code Online (Sandbox Code Playgroud)

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

MyAppView
  - MyApp component instance
  - my-app host element data
       ChildCompnentView
         - ChildComponent component instance
         - child-comp host element data  
Run Code Online (Sandbox Code Playgroud)

然后才运行更改检测并更新MyAppComponent类my-app和调用的绑定ngOnInit.然后继续更新child-comp和调用ngOnInitChildComponent类的绑定.

您可以在构造函数中执行初始化逻辑,也可以ngOnInit根据需要执行初始化逻辑.例如,文章以下是在评估@ViewChild查询之前如何获取ViewContainerRef,以显示在构造函数中可能需要执行的初始化逻辑类型.

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

  • 这应该是公认的答案.它实际上解释了为什么而不是重复mantras并声明`构造函数应该只用于注入依赖项`. (29认同)

Mor*_*n G 88

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

使用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);
    };


}
Run Code Online (Sandbox Code Playgroud)

使用我的构造函数,我可以调用我的_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();
    };


}
Run Code Online (Sandbox Code Playgroud)

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

  • 如下面的答案所示,如果它在构造函数中,则没有区别.这不是目的的真正答案. (30认同)
  • 我根本不知道这是怎么回答这个问题的.为什么你不能把代码放在`构造函数`中? (8认同)
  • @Morgan 为什么你不能只做`constructor(private _userService: UserService){ this.getUsers(); }; ` (4认同)

Ali*_*eza 70

好的,首先ngOnInitAngular生命周期的一部分,虽然constructorES6 JavaScript类的一部分,所以主要区别从这里开始!...

请看下面我创建的图表,其中显示了Angular的生命周期.

ngOnInit vs构造函数

在Angular2 +中我们用来为我们constructorDI(Dependency Injection),而在Angular 1中,它是通过调用String方法并检查注入了哪个依赖项来实现的.

正如您在上图中看到的那样,ngOnInit在构造函数准备就绪后发生,ngOnChnages并在组件为我们准备好后被触发.所有初始化都可以在此阶段进行,一个简单的示例是注入服务并在init上初始化它.

OK,我也分享给你看看,看看我们如何得到使用示例代码ngOnInit,并constructor在下面的代码:

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


@Component({
 selector: 'my-app',
 template: `<h1>App is running!</h1>
  <my-app-main [data]=data></<my-app-main>`,
  styles: ['h1 { font-weight: normal; }']
})
class ExampleComponent implements OnInit {
  constructor(private router: Router) {} //Dependency injection in the constructor

  // ngOnInit, get called after Component initialised! 
  ngOnInit() {
    console.log('Component initialised!');
  }
}
Run Code Online (Sandbox Code Playgroud)


Thi*_*ier 50

第一个(构造函数)与类实例化有关,与Angular2无关.我的意思是构造函数可以在任何类上使用.您可以为新创建的实例添加一些初始化处理.

第二个对应于Angular2组件的生命周期钩子:

引自官方角度的网站:

  • ngOnChanges 在输入或输出绑定值更改时调用
  • ngOnInit 在第一个之后调用 ngOnChanges

所以你应该使用ngOnInitif初始化处理依赖于组件的绑定(例如定义的组件参数@Input),否则构造函数就足够了......


Mir*_*nas 38

我将在上面的解释中添加一个重要的内容,并解释您必须使用的时间ngOnInit.

如果您通过ViewChildren,ContentChildrenElementRef对组件的DOM进行任何操作,则在构造函数阶段,您的本机元素将不可用.

但是,因为ngOnInit一旦创建了组件并且ngOnChanges调用了checks(),就可以在此时访问DOM.

export class App implements OnInit, AfterViewInit, AfterContentInit {
  @Input() myInput: string;
  @ViewChild() myTemplate: TemplateRef<any>;
  @ContentChild(ChildComponent) myComponent: ChildComponent; 

  constructor(private elementRef: ElementRef) {
     // this.elementRef.nativeElement is undefined here
     // this.myInput is undefined here
     // this.myTemplate is undefined here
     // this.myComponent is undefine here
  }

  ngOnInit() {
     // this.elementRef.nativeElement can be used from here on
     // value of this.myInput is passed from parent scope
     // this.myTemplate and this.myComponent are still undefined
  }
  ngAfterContentInit() {
     // this.myComponent now gets projected in and can be accessed
     // this.myTemplate is still undefined
  }

  ngAfterViewInit() {
     // this.myTemplate can be used now as well
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 不。特别是对于@ViewChildren,您需要使用ngAfterViewInit方法。看到这里:/sf/ask/3242031411/?rq=1 (3认同)

mic*_*yks 32

简短的回答是,

Constructor:constructor是构造组件时的default method运行(通过deafult).当你创建an instance一个类时,constructor(default method)也会被调用.换句话说,当constructed or/and an instance is created constructor(default method)调用组件时,调用内部编写的相关代码.基本上并且通常在Angular2其中用于注入诸如services正在构造组件以供进一步使用的东西.

OnInit:ngOnInit是组件的生命周期钩子,它constructor(default method)在组件初始化之后首先运行.

因此,首先调用构造函数,然后在构造函数方法之后调用Oninit.

boot.ts

import {Cmomponent, OnInit} from 'angular2/core';
import {ExternalService} from '../externalService';

export class app implements OnInit{
   constructor(myService:ExternalService)
   {
           this.myService=myService;
   }

   ngOnInit(){
     // this.myService.someMethod() 
   }
}
Run Code Online (Sandbox Code Playgroud)

资源:LifeCycle钩子

您可以查看这个小型演示,其中显示了两者的实现.

  • 我认为"构造函数是在组件初始化时运行或调用的东西." 是误导.构造函数是类的特征,而不是组件的特征.我会说类的实例只在构造函数被调用后才成为一个组件**并且**Angular做了它的初始化. (5认同)
  • 是的,一点没错.忘了提一下,当你创建一个类的对象时,也会调用`constructor`.但这个答案是用angular2语言编写的.要了解最佳答案,您必须了解OOP的基础知识.我还是会更新答案. (2认同)

abb*_*33f 17

为了测试这个,我编写了这段代码,借鉴了NativeScript教程:

user.ts

export class User {
    email: string;
    password: string;
    lastLogin: Date;

    constructor(msg:string) {        
        this.email = "";
        this.password = "";
        this.lastLogin = new Date();
        console.log("*** User class constructor " + msg + " ***");
    }

    Login() {
    }
}
Run Code Online (Sandbox Code Playgroud)

login.component.ts

import {Component} from "@angular/core";
import {User} from "./../../shared/user/user"

@Component({
  selector: "login-component",
  templateUrl: "pages/login/login.html",
  styleUrls: ["pages/login/login-common.css", "pages/login/login.css"]
})
export class LoginComponent {

  user: User = new User("property");  // ONE
  isLoggingIn:boolean;

  constructor() {    
    this.user = new User("constructor");   // TWO
    console.log("*** Login Component Constructor ***");
  }

  ngOnInit() {
    this.user = new User("ngOnInit");   // THREE
    this.user.Login();
    this.isLoggingIn = true;
    console.log("*** Login Component ngOnInit ***");
  }

  submit() {
    alert("You’re using: " + this.user.email + " " + this.user.lastLogin);
  }

  toggleDisplay() {
    this.isLoggingIn = !this.isLoggingIn;
  }

}
Run Code Online (Sandbox Code Playgroud)

控制台输出

JS: *** User class constructor property ***  
JS: *** User class constructor constructor ***  
JS: *** Login Component Constructor ***  
JS: *** User class constructor ngOnInit ***  
JS: *** Login Component ngOnInit ***  
Run Code Online (Sandbox Code Playgroud)


Edu*_*nis 17

与许多其他语言一样,您可以在类级别,构造函数或方法中初始化变量.由开发人员决定在他们的特定情况下最好的是什么.但下面列出了决策时的最佳实践.

类级变量

通常,您将在此处声明将在其余组件中使用的所有变量.如果值不依赖于任何其他值,则可以初始化它们,或者如果它们不会更改,则使用const关键字创建常量.

export class TestClass{
    let varA: string = "hello";
}
Run Code Online (Sandbox Code Playgroud)

构造函数

通常,最佳做法是不在构造函数中执行任何操作,只将其用于将要注入的类.大多数情况下,构造函数应如下所示:

   constructor(private http: Http, private customService: CustomService) {}
Run Code Online (Sandbox Code Playgroud)

这将自动创建类级变量,因此您customService.myMethod()无需手动操作即可访问.

NgOnInit

NgOnit是Angular 2框架提供的生命周期钩子.您的组件必须实现OnInit才能使用它.在调用构造函数并初始化所有变量之后调用此生命周期钩子.你的大部分初始化应该放在这里.您将确信Angular已正确初始化您的组件,并且您可以开始执行您需要的任何逻辑,OnInit而不是在组件未正确加载时执行操作.

这是一张详细说明被调用顺序的图像:

在此输入图像描述

https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html

TLDR

如果您正在使用Angular 2框架并且需要与某些生命周期事件进行交互,请使用框架提供的方法来避免问题.


Est*_*ask 16

构造函数之间的主要差异,ngOnInit被认为ngOnInit生命周期的钩和构造后运行.组件插值模板和输入初始值在构造函数中不可用,但它们可用于ngOnInit.

实际差异在于如何ngOnInit影响代码的结构.大多数初始化代码都可以移动到ngOnInit- 只要这不会创建竞争条件.

构造函数反模式

大量的初始化代码使得构造函数方法难以扩展,读取和测试.

将初始化逻辑与类构造函数分离的常用方法是将其移动到另一个方法,如init:

class Some {
  constructor() {
    this.init();
  }

  init() {...}
}
Run Code Online (Sandbox Code Playgroud)

ngOnInit 可以在组件和指令中实现此目的:

constructor(
  public foo: Foo,
  /* verbose list of dependencies */
) {
  // time-sensitive initialization code
  this.bar = foo.getBar();
}

ngOnInit() {
  // rest of initialization code
}
Run Code Online (Sandbox Code Playgroud)

依赖注入

Angular中类构造函数的主要作用是依赖注入.构造函数也用于TypeScript中的DI注释.几乎所有依赖项都被指定为类实例的属性.

平均组件/指令构造函数已经足够大,因为它可以由于依赖性而具有多行签名,将不必要的初始化逻辑放置到构造函数体对反模式的贡献.

异步初始化

异步初始化构造往往被认为是反模式,并有异味,因为类的实例完成异步例程不前,这可以创造竞争条件.如果不是这样,那么ngOnInit其他生命周期钩子就是更好的地方,特别是因为它们可以从async语法中受益:

constructor(
  public foo: Foo,
  public errorHandler: ErrorHandler
) {}

async ngOnInit() {
  try {
    await this.foo.getBar();
    await this.foo.getBazThatDependsOnBar();
  } catch (err) {
    this.errorHandler.handleError(err);
  }
}
Run Code Online (Sandbox Code Playgroud)

如果存在竞争条件(包括组件不应该在初始化错误出现的一个),异步初始化程序应该发生组件实例之前被移动到父组件,路由器后卫等

单元测试

ngOnInit比构造函数更灵活,并为单元测试提供了一些好处,本答案将对此进行详细说明.

考虑到ngOnInit在单元测试中没有自动调用组件编译,ngOnInit可以在组件实例化后监视或模拟调用的方法.

在特殊情况下,ngOnInit可以完全存根以提供其他组件单元的隔离(例如,某些模板逻辑).

遗产

子类只能扩充构造函数,而不能替换它们.

由于this之前不能引用super(),因此限制了初始化优先级.

考虑到Angular组件或指令ngOnInit用于时间不敏感的初始化逻辑,子类可以选择是否super.ngOnInit()调用以及何时:

ngOnInit() {
  this.someMethod();
  super.ngOnInit();
}
Run Code Online (Sandbox Code Playgroud)

单独使用构造函数无法实现这一点.


Pre*_*ton 12

上面的答案并没有真正回答原问题的这一方面:什么是生命周期钩子?我花了一段时间才明白这意味着什么,直到我这样想.

1)说你的组件是人.人类的生活包括许多生活阶段,然后我们就会过期.

2)我们的人体成分可以具有以下生命周期脚本:出生,婴儿,小学,青年成人,中年成人,高级成人,死亡,处置.

3)假设你想要一个创造孩子的功能.为了防止这种情况变得复杂,而且相当幽默,你希望你的功能只能在人体成分生命的青年成人阶段被调用.因此,您开发的组件仅在父组件处于Young Adult阶段时才处于活动状态.钩子通过发出生命阶段的信号并让你的组件对其起作用来帮助你做到这一点.

好玩的东西.如果你让你的想象力去实际编写这样的东西,它会变得复杂,有趣.


Neg*_*gin 7

构造函数是在JavaScript的方法,被认为是在ES6类的功能.当类实例化它立即运行构造无论是在角框架中使用或not.So它是由JavaScript引擎叫和角度没有对此的控制.

import {Component} from '@angular/core';
@Component({})
class CONSTRUCTORTEST {

//This is called by Javascript not the Angular.
     constructor(){
        console.log("view constructor initialised");
     }
}
Run Code Online (Sandbox Code Playgroud)

"ConstructorTest"类在下面实例化;因此它在内部调用构造函数(所有这些都发生在JavaScript(es6)没有Angular).

new CONSTRUCTORTEST();
Run Code Online (Sandbox Code Playgroud)

这就是当Angular完成组件初始化时,Angular.ngOnInit中存在ngOnInit生命周期钩子的原因.

import {Component} from '@angular/core';
@Component({})
class NGONINITTEST implements onInit{
   constructor(){}
   //ngOnInit calls by Angular
   ngOnInit(){
     console.log("Testing ngOnInit");
   }
}
Run Code Online (Sandbox Code Playgroud)

首先,我们实例化下面的类,这恰好是构造函数方法的立即运行.

let instance = new NGONINITTEST();
Run Code Online (Sandbox Code Playgroud)

必要时,Angular会调用ngOnInit,如下所示:

instance.ngOnInit();
Run Code Online (Sandbox Code Playgroud)

但你可能会问为什么我们在Angular中使用构造函数?

答案是依赖注入.如前所述,构造函数在实例化类时(在通过Angular调用ngOnInit之前)立即通过JavaScript引擎调用,因此typescript帮助我们获取构造函数中定义的依赖项的类型,最后告诉Angular我们想要在该特定组件中使用哪种类型的依赖项.


Uni*_*der 7

这里要注意两件事:

  1. 只要为该类创建了一个对象,就会调用构造函数.
  2. 创建组件后调用ngOnInit.

两者都有不同的可用性.


Vis*_*ati 7

构造函数: ES6 类(在本例中为 TypeScript)的构造函数方法是类本身的特性,而不是 Angular 特性。当构造函数被调用时,它不受 Angular 的控制,这意味着它不是一个合适的钩子来让你知道 Angular 何时完成了组件的初始化。JavaScript 引擎调用构造函数,而不是直接调用 Angular。这就是创建 ngOnInit(以及 AngularJS 中的 $onInit)生命周期钩子的原因。记住这一点,有一个适合使用构造函数的场景。这就是我们想要利用依赖注入的时候——本质上是为了将依赖“连接”到组件中。

由于构造函数由 JavaScript 引擎初始化,TypeScript 允许我们告诉 Angular 我们需要将哪些依赖项映射到特定属性。

ngOnInit纯粹是为了给我们一个信号,告诉我们 Angular 已经完成了组件的初始化。

此阶段包括针对我们可能绑定到组件本身的属性的更改检测的第一遍 - 例如使用 @Input() 装饰器。

因此,@Input() 属性在 ngOnInit 中可用,但在构造函数中未定义,按照设计


Sha*_*ran 5

Constructor()是组件生命周期中的默认方法,用于依赖项注入。构造函数是一个打字稿功能。

ngOnInit()在构造函数之后调用,ngOnInit在第一个ngOnChanges之后调用。

即:

构造函数()-->ngOnChanges()-->ngOnInit()

ngOnChanges()当输入或输出绑定值更改时,将调用上述方法。


use*_*506 5

构造函数是最先执行的,有时@input data 为null 时会发生这种情况!所以我们使用构造函数来注入服务,然后 ngOnInit 发生。构造函数示例:

 constructor(translate: TranslateService, private oauthService: OAuthService) {
    translate.setDefaultLang('En');
        translate.use('En');}
Run Code Online (Sandbox Code Playgroud)

ngOnInit 的示例:

ngOnInit() {
    this.items = [
      { label: 'A', icon: 'fa fa-home', routerLink: ['/'] },
      { label: 'B', icon: 'fa fa-home', routerLink: ['/'] }]
}
Run Code Online (Sandbox Code Playgroud)

我认为 ngOnInit 就像 winForm 中的 InitialComponents() 。