构造函数和ngOnInit的区别

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

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

747969 次浏览

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

new MyClass(someArg);

ngOnInit是Angular调用的生命周期钩子,表示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()是“开始”的更好地方-它是组件绑定被解决的地方/时间。

有关更多信息,请参阅此处:

重要的是要注意,@Input值在构造函数中是不可访问的(感谢@Tim在评论中的建议)

简单明了的答案是,

#0constructor是在构建组件时default method运行(默认)。当你创建一个类的an instance时,也会调用constructor(default method)。换句话说,当组件constructed or/and an instance is created constructor(default method)被调用并在其中编写相关代码时,会被调用。基本上和一般在Angular2中,它用于在构建组件以供进一步使用时注入像services这样的东西。

#0: 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()}}

资源:生命周期钩子

您可以检查这个小演示,它显示了这两件事的实现。

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

在构造函数上使用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();};

}

它使它更容易看到,所以当我初始化时,我只是在我的组件中调用我的函数,而不必在其他地方挖掘它。真的,这只是你可以使用的另一个工具,使它在未来更容易阅读和使用。此外,我发现将函数调用放在构造函数中真的很糟糕!

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

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

引用自官方网站:

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

因此,如果初始化处理依赖于组件的绑定(例如用@Input定义的组件参数),则应该使用ngOnInit,否则构造函数就足够了…

为了测试这一点,我写了这段代码,借用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() {}}

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");  // ONEisLoggingIn:boolean;
constructor() {this.user = new User("constructor");   // TWOconsole.log("*** Login Component Constructor ***");}
ngOnInit() {this.user = new User("ngOnInit");   // THREEthis.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;}
}

控制台输出

JS: *** User class constructor property ***JS: *** User class constructor constructor ***JS: *** Login Component Constructor ***JS: *** User class constructor ngOnInit ***JS: *** Login Component ngOnInit ***

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

1)假设你的组件是一个人。人类的生命包括许多生命阶段,然后我们就过期了。

2)我们的人类组成部分可能有以下生命周期脚本:出生,婴儿,小学,年轻人,中年成年人,老年人,死亡,处置。

3)假设你想有一个创建孩子的函数。为了避免变得复杂和幽默,你希望你的函数只在人类组件生命的年轻人阶段被调用。所以你开发了一个只有当父组件处于年轻人阶段时才活跃的组件。钩子通过发出生命阶段的信号并让你的组件对其采取行动来帮助你做到这一点。

有趣的东西。如果你让你的想象力去实际编码这样的东西,它会变得复杂和有趣。

这两种方法都有不同的目标/职责。构造函数(这是一种语言支持的特性)的任务是确保表示不变量成立。否则,通过向成员提供正确的值来确保实例是有效的。由开发人员决定“正确”的含义。

onInit()方法(这是一个角度概念)的任务是允许在正确的对象上调用方法(表示不变量)。每个方法都应该确保在方法终止时表示不变量保持不变。

构造函数应该用于创建“正确”的对象,onInit方法让您有机会在定义良好的实例上调用方法调用。

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

类级变量

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

export class TestClass{let varA: string = "hello";}

构造函数

通常,最好不要在构造函数中做任何事情,而是将其用于将被注入的类。大多数时候,你的构造函数应该如下所示:

   constructor(private http: Http, private customService: CustomService) {}

这将自动创建类级变量,因此您将可以访问customService.myMethod()而无需手动执行。

NgOnInit

NgOnit是Angular 2框架提供的生命周期钩子。你的组件必须实现OnInit才能使用它。这个生命周期钩子在调用构造函数并初始化所有变量后被调用。你的大部分初始化应该在这里。你将确信Angular已经正确初始化了你的组件,你可以开始在OnInit中做任何你需要的逻辑,而不是在组件没有正确加载时做事情。

这是一张图片,详细说明了调用的顺序:

在此处输入图片描述

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

TLDR

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

构造函数是JavaScript中的一个方法,被认为是es6中类的一个特性。当该类被实例化时,无论是否在Angular框架中使用,它都会立即运行构造函数。

import {Component} from '@angular/core';@Component({})class CONSTRUCTORTEST {
//This is called by Javascript not the Angular.constructor(){console.log("view constructor initialised");}}

“ConstructorTest”类在下面实例化;所以它在内部调用构造函数(所有这些都由JavaScript(es6)没有Angular发生)。

new CONSTRUCTORTEST();

这就是为什么当Angular完成初始化组件时,OnInit渲染Angular.ng中有ngOnInit生命周期钩子。

import {Component} from '@angular/core';@Component({})class NGONINITTEST implements onInit{constructor(){}//ngOnInit calls by AngularngOnInit(){console.log("Testing ngOnInit");}}

首先,我们实例化类,如下所示,它发生在构造函数方法的立即运行中。

let instance = new NGONINITTEST();

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

instance.ngOnInit();

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

答案是依赖注入。正如前面提到的,当类实例化时(在Angular调用ngOnInit之前),JavaScript引擎会立即调用构造函数,所以typeScript帮助我们获取构造函数中定义的依赖关系的类型,并最终告诉Angular我们想要在该特定组件中使用什么类型的依赖关系。

OK,首先ngOnInit角生命周期的一部分,而constructorES6 JavaScript类的一部分,所以主要区别从这里开始!…

看看下面我创建的图表,它显示了Angular的生命周期。

ngOnInit vs构造函数

在Angular2+中,我们使用constructor为我们做DI(Dependency Injection),而在Angular 1中,它是通过调用String方法并检查注入了哪个依赖项来发生的。

如上图所示,ngOnInit发生在构造函数准备好之后,ngOnChnages发生在组件为我们准备好之后。所有初始化都可以在这个阶段发生,一个简单的示例是注入服务并在init上缩写它。

好的,我还分享了一个示例代码供您查看,看看我们如何在下面的代码中使用ngOnInitconstructor

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!');}}

这里要注意两件事:

  1. 每当创建该类的对象时,都会调用构造函数。
  2. 创建组件后调用ngOnInit。

两者具有不同的可用性。

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

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

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

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

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

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

让我们看一个快速示例。假设您有以下模板:

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

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

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

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

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

这里有一些文章可以帮助你更好地理解这个话题:

constructor()用于进行依赖注入。

ngOnInit()ngOnChanges()ngOnDestroy()等是生命周期方法。ngOnChanges()将是第一个被调用的,在ngOnInit()之前,当绑定属性的值发生变化时,如果没有变化,它将不被调用。ngOnDestroy()在组件被删除时被调用。要使用它,OnDestroy需要由类implement ed。

在Angular生命周期中

1)Angular注入器检测构造函数参数并实例化类。

2)下一个角度调用生命周期

角的生命周期钩子

调用指令参数绑定。

ngOnInit-->开始角度渲染…

用角生命周期状态调用其他方法。

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

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

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

这个阶段包括对我们可能绑定到组件本身的属性进行更改检测的第一次传递-例如使用@Input()装饰器。

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

我将只添加一个在上面的解释中跳过的重要内容,并解释当你必须使用ngOnInit时。

如果您通过查看儿童内容儿童元素参考对组件的DOM进行任何操作,您的本机元素在构造器阶段将不可用。

但是,由于一旦创建了组件并调用了检查(ngOnChanges),就会发生ngOnInit,因此您可以在此时访问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}}

构造函数和ngOnInit之间的主要区别在于ngOnInit生命周期钩子并在构造函数之后运行。组件插值模板和输入初始值在构造函数中不可用,但它们在ngOnInit中可用。

实际的区别是ngOnInit如何影响代码的结构。大多数初始化代码可以移动到ngOnInit-只要这不会造成种族歧视

构造器反模式

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

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

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

ngOnInit可以在组件和指令中用于此目的:

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

依赖注入

类构造函数在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);}}

如果存在竞争条件(包括组件不应该出现在初始化错误上的竞争条件),异步初始化例程应该在组件实例化之前进行,并移动到父组件、路由器保护等。

单元测试

ngOnInit比构造函数更灵活,并为单元测试提供了一些好处,这些好处将在这个答案中详细解释。

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

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

继承

子类只能增强构造函数,而不能替换它们。

由于this不能在super()之前引用,这限制了初始化优先级。

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

ngOnInit() {this.someMethod();super.ngOnInit();}

这将是不可能实现与构造函数单独。

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

ngOnInit()方法名称在构造函数之后调用,ngOnInit在第一个ngOnChange之后调用。

即:

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

如上所述,当输入或输出绑定值更改时调用ngOnChanges()

构造函数是第一个执行的,有时当@输入数据为空时会发生!所以我们使用Constructor注入服务,然后发生ngOnInit。构造函数示例:

 constructor(translate: TranslateService, private oauthService: OAuthService) {translate.setDefaultLang('En');translate.use('En');}

ngOnInit示例:

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

我认为ngOnInit就像winForm中的初始化组件()。

当Angular“实例化/构造”组件时调用constructorngOnInit方法是一个钩子,表示组件生命周期的初始化部分。一个好的做法是仅将它用于服务注入

constructor(privateservice1: Service1,service2: Service2){};

即使有可能,你也不应该在里面做一些“工作”。如果您想启动一些必须在组件“初始化”时发生的操作,请使用ngOnInit

ngOnInit(){service1.someWork();};

此外,来自父组件的涉及输入属性的操作不能在构造器中完成。它们应该放在ngOnInit方法或另一个钩子中。对于与视图(DOM)相关的元素也是如此,例如视子元素

@Input itemFromParent: string;@ViewChild('childView') childView;
constructor(){console.log(itemFromParent); // KO// childView is undefined here};
ngOnInit(){console.log(itemFromParent); // OK// childView is undefined here, you can manipulate here};

我找到了答案,我试着把它翻译成英语:这个问题甚至在技术面试中仍然出现。事实上,两者之间有很大的相似之处,但也有一些差异。

  • 构造函数是ECMAScript的一部分。另一方面,ngOnInit()是角的概念。

  • 我们可以调用所有类中的构造函数,即使我们不使用Angular

  • LifeCycle:构造函数在ngOnInt()之前被调用

  • 在构造函数中,我们不能调用超文本标记语言元素。但是,在ngOnInit()中,我们可以。

  • 通常,在ngOnInit()中而不是在构造函数中调用服务

    来源:http://www.angular-tuto.com/Angular/Component#Diff

构造函数是构建组件(或其他类)时执行的函数。

ngOnInit是一个属于组件生命周期方法组的函数,它们在我们组件的不同时刻执行(这就是命名生命周期的原因)。这是所有这些的列表:

在此处输入图片描述构造函数将在任何生命周期函数之前执行。

构造函数

构造函数随每个类一起提供,构造函数不是特定于Angular的,而是源自面向对象设计的概念。构造函数创建组件类的实例。

OnInit

ngOnInit函数是Angular组件的生命周期方法之一。Angular组件中的生命周期方法(或钩子)允许您在组件生命周期的不同阶段运行一段代码。与构造函数方法不同,ngOnInit方法来自Angular接口(OnInit),组件需要实现该接口才能使用此方法。ngOnInit方法在组件创建后不久被调用。

构造函数在类实例化时执行。它与角无关。它是Javascript的特性,Angular无法控制它

ngOnInit是Angular特定的,当Angular使用其所有输入属性初始化组件时调用

@Input属性在ngOnInit生命周期钩子下可用。这将帮助您执行一些初始化操作,例如从后端服务器获取数据等以显示在视图中

@Input属性在构造函数中显示为未定义

Constructor是ES6的一部分,打字稿也使用es6语法,现在也使用es7,因此您可以利用打字稿将编译为es5/es4(根据您的定义)的高级功能来为旧浏览器提供支持。

ngOnInIt是角的生命周期钩子。它在您的组件初始化时初始化。(将它的状态视为任何新生命的诞生)

ngOnInIt与构造函数进行比较是明智的,因为你有另一个像ngOnDestory这样的生命周期钩子(将其视为任何生命的死亡)。在这里,你可以取消订阅任何observable,这有助于防止任何内存泄漏。

如果有任何问题,请随时评论这个答案。

构造函数可以接受参数并用于依赖注入或构造函数()用于添加服务对象。

在ngOnint()之前调用构造函数;

ngOnInit()方法名称用于操作具有必要服务函数调用的组件,或者通常,在ngOnInit()中而不是在构造函数中调用服务

构造函数是TypeScript类提供的默认方法,专门用于初始化类成员,一般用于依赖注入服务,如上面的示例代码,或定时器初始化、套接字连接初始化

export class AppComponent {title = 'angular-fork-join';constructor(private http: HttpClient) {}

ngOnInit:是Angular提供的组件初始化时调用的生命周期钩子,专用于例如业务逻辑、数据初始化、API调用等。

export class HomeComponent implements OnInit {
products = [];
constructor(private dataService: DataService) { }
ngOnInit() {
this.dataService.sendGetRequest().subscribe((data: any[])=>{console.log(data);this.products = data;})}
}