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

为什么要使用ngOnInit,如果我们已经有一个构造函数?


当前回答

首先,ngOnInit是Angular生命周期的一部分,而constructor是ES6 JavaScript类的一部分,所以主要的区别就是从这里开始的!

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

在Angular2+中,我们使用构造函数为我们做DI(依赖注入),而在Angular 1中,它是通过调用String方法并检查注入了哪个依赖。

正如你在上面的图表中看到的,ngOnInit是在构造函数准备好之后发生的,ngOnChnages和在组件为我们准备好之后被触发。所有的初始化都可以在这个阶段进行,一个简单的示例是注入一个服务并在init上初始化它。

好的,我还分享了一个示例代码给你看,看看我们如何在下面的代码中使用ngOnInit和构造函数:

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

其他回答

为了测试这一点,我从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");  // 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;
  }

}

控制台输出

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

像许多其他语言一样,可以在类级、构造函数或方法上初始化变量。这取决于开发人员来决定在他们的特定情况下什么是最好的。但下面是在做决定时的最佳实践清单。

类级变量

通常,您将在这里声明将在组件的其余部分中使用的所有变量。如果值不依赖于其他任何东西,可以初始化它们,或者如果它们不会改变,则使用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框架,并且需要与特定的生命周期事件交互,请使用框架提供的方法来避免问题。

我将添加一个重要的东西,在上面的解释中被跳过,并解释什么时候你必须使用ngOnInit。

如果你通过ViewChildren, ContentChildren或ElementRef对组件的DOM进行任何操作,你的原生元素在构造函数阶段将不可用。

然而,由于ngOnInit发生在组件被创建和检查(ngOnChanges)被调用之后,你可以在这一点上访问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
  }
}

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

构造函数是ECMAScript的一部分。另一方面,ngOnInit()是angular的概念。 即使不使用Angular,我们也可以在所有类中调用构造函数 生命周期:构造函数在ngOnInt()之前调用 在构造函数中,我们不能调用HTML元素。然而,在ngOnInit()中我们可以。 通常,服务调用在ngOnInit()中,而不是在构造函数中 来源:http://www.angular-tuto.com/Angular/Component Diff

这里需要注意两点:

每当该类创建对象时,都会调用构造函数。 一旦组件创建,就调用ngOnInit。

两者都有不同的可用性。