Angular默认提供生命周期钩子ngOnInit
。
如果我们已经有了constructor
,为什么还应该使用ngOnInit
?
Angular默认提供生命周期钩子ngOnInit
。
如果我们已经有了constructor
,为什么还应该使用ngOnInit
?
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
进行所有的初始化/声明,并避免在构造函数中进行操作。构造函数仅应用于初始化类成员,但不应执行实际的“工作”。constructor()
,不要做太多其他事情。ngOnInit()更适合“开始”——这是组件绑定解析的位置和时间。<my-app>
<child-comp [i]='prop'>
MyAppComponent
构造函数。它还创建了一个DOM节点,该节点是my-app
组件的宿主元素。然后,它继续为child-comp
创建一个宿主元素,并调用ChildComponent
构造函数。在这个阶段,它并不真正关心i
输入绑定和任何生命周期钩子。因此,当这个过程完成时,Angular得到了以下组件视图树:MyAppView
- MyApp component instance
- my-app host element data
ChildComponentView
- ChildComponent component instance
- child-comp host element data
my-app
的绑定,并在MyAppComponent类上调用ngOnInit
。然后,它继续更新child-comp
的绑定,并在ChildComponent类上调用ngOnInit
。ngOnInit
中执行初始化逻辑。例如,文章Here is how to get ViewContainerRef before @ViewChild query is evaluated展示了在构造函数中可能需要执行的初始化逻辑类型。好的,首先ngOnInit
是Angular生命周期的一部分,而constructor
是ES6JavaScript类的一部分,因此主要区别从这里开始!...
看一下我创建的下面的图表,它展示了Angular的生命周期。
在Angular2+中,我们使用constructor
来为我们执行DI(Dependency Injection)
,而在Angular 1中,通过调用String方法并检查注入了哪个依赖项来完成。
如您在上面的图表中看到的,ngOnInit
发生在构造函数准备就绪之后,ngOnChnages
在组件准备就绪后触发。所有初始化都可以在此阶段进行,一个简单的示例是注入服务并在init上对其进行初始化。
好的,我还为您分享了一个样本代码,看看我们如何在下面的代码中使用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!');
}
}
我认为最好的例子就是使用服务。比如说,当我的组件被“激活”时,我想从服务器上获取数据。假设我还希望在从服务器获取数据后对其进行一些其他操作,比如遇到错误时以不同方式记录日志。
使用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);
};
}
使用构造函数,我可以调用我的用户服务并填充用户列表,但也许我想对其进行一些额外操作。就像确保所有内容都是大写的,我不确定我的数据究竟是怎样的。
因此,使用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();
};
}
这使得查看变得更加容易,因此我在初始化组件时仅调用我的函数,而不必到其他地方进行查找。这只是另一个工具,可以让您将来更轻松地阅读和使用。此外,我发现在构造函数中放置函数调用实在是一个糟糕的做法!
getUsers
然后再将其插入到 ngOnInit
中呢?直接在 ngOnInit 中编写不是更少代码吗?我只是想知道人们为什么这样做?是为了以后可以重复使用代码吗?谢谢。 - Alfa Bravoconstructor(private _userService: UserService){ this.getUsers(); };
- Ashley在上面的解释中有一件重要的事情被省略了,它解释了什么时候你必须使用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
}
}
@ViewChildren
,你需要使用 ngAfterViewInit
方法。请参见:
https://dev59.com/ZFYO5IYBdhLWcg3wHuIA?rq=1 - AsGoodAsItGetselementRef
зҡ„APIжңүж”№еҸҳеҗ—пјҹжҲ‘е·Із»ҸжөӢиҜ•иҝҮпјҢеңЁжһ„йҖ еҮҪж•°дёӯthis.elementRef.nativeElement
дёҚжҳҜжңӘе®ҡд№үзҡ„гҖӮ - bytrangle第一个(构造函数)与类实例化有关,与Angular2无关。我的意思是,构造函数可以用于任何类。您可以在其中放置一些新创建实例的初始化处理。
第二个对应于Angular2组件的生命周期钩子:
引自官方Angular网站:
ngOnChanges
在输入或输出绑定值更改时调用ngOnInit
在第一个ngOnChanges
之后调用
因此,如果初始化处理依赖于组件绑定(例如使用@Input
定义的组件参数),则应使用ngOnInit
,否则构造函数就足够了...
简短而简单的答案是:
构造函数
: 构造函数
是一个默认方法
,在组件被构造时(默认情况下)运行。当您创建一个类的实例
时,也会调用构造函数(默认方法)。因此,换句话说,在组件被构建或/和实例化
时,将调用构造函数(默认方法),并且写入其中的相关代码被称为。基本上,在Angular2
中,它用于注入像服务
这样的东西,以便在组件被构造时进行进一步使用。
OnInit
: OnInit是组件的生命周期钩子,它在构造函数(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()
}
}
资源: 生命周期钩子
您可以查看这个小演示,它展示了两者的实现。
new MyClass()
的代码时被调用。我认为说构造函数与组件有关是具有误导性的,它们与类和初始化这些类的实例有关。组件只是这样一个类。否则,我认为这是一个很好的答案。 - Günter Zöchbauerconstructor
也会被调用。但是这个答案是在angular2上下文中编写的。要知道最好的答案,你必须了解面向对象编程的基础知识。我仍然会更新答案。 - micronyks构造函数和ngOnInit
的主要区别在于,ngOnInit
是一个生命周期钩子,并且在构造函数之后运行。组件插值模板和输入初始值在构造函数中不可用,但它们在ngOnInit
中是可用的。
实际上,ngOnInit
如何影响代码结构是最���的区别。大多数初始化代码可以移动到ngOnInit
中 - 只要这不会创建竞争条件。
大量的初始化代码会使构造函数方法难以扩展、阅读和测试。
将初始化逻辑与类构造函数分离的通常做法是将其移动到另一个方法中,例如init
:
class Some {
constructor() {
this.init();
}
init() {...}
}
ngOnInit
可以在组件和指令中用来实现此目的:
constructor(
public foo: Foo,
/* verbose list of dependencies */
) {
// time-sensitive initialization code
this.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
可以完全被桩替代,以为其他组件单位(例如某些模板逻辑)提供隔离。
子类只能增强构造函数,而不能替换构造函数。
由于在super()
之前无法引用this
,这会对初始化顺序产生限制。
考虑到Angular组件或指令使用ngOnInit
进行与时间无关的初始化逻辑,子类可以选择何时调用super.ngOnInit()
:
ngOnInit() {
this.someMethod();
super.ngOnInit();
}
仅使用构造函数是不可能实现这一点的。
与许多其他编程语言一样,您可以在类级别、构造函数或方法中初始化变量。这取决于开发人员决定在其特定情况下什么最好。但以下是在做出决策时的最佳实践清单。
通常,在此处声明将在组件的其余部分中使用的所有变量。如果值不依赖于任何其他因素,则可以初始化它们,否则可以使用“const”关键字创建常数(如果它们不会更改)。
export class TestClass{
let varA: string = "hello";
}
通常最佳实践是不在构造函数中执行任何操作,而只是将其用于将要被注入的类。大多数情况下,您的构造函数应该如下所示:
constructor(private http: Http, private customService: CustomService) {}
这将自动创建类级变量,因此您将可以访问customService.myMethod()
而无需手动执行。
NgOnit是Angular 2框架提供的生命周期钩子。您的组件必须实现OnInit
才能使用它。这个生命周期钩子在构造函数调用后和所有变量初始化后被调用。大部分初始化应该放在这里。您可以确信Angular已正确初始化您的组件,可以在OnInit
中开始执行任何需要的逻辑,而不是在组件加载完全之前执行。
下面是一个详细说明哪些内容会被调用的图像:
https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html
如果您正在使用Angular 2框架并且需要与某些生命周期事件交互,请使用框架提供的方法来避免问题。
为了测试这个功能,我编写了以下代码,借鉴了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 ***
tsconfig.json
文件中设置了"strict": true
严格模式,那么你必须在构造函数中初始化类成员,而不是在ngOnit
(如FormGroup
)中。 - Rohit Sharma