Angular默认提供了生命周期钩子ngOnInit。
为什么要使用ngOnInit,如果我们已经有一个构造函数?
Angular默认提供了生命周期钩子ngOnInit。
为什么要使用ngOnInit,如果我们已经有一个构造函数?
当前回答
我找到了答案,试着把它翻译成英语: 即使在技术面试中,这个问题仍然会出现。事实上,两者之间有很大的相似之处,但也有一些不同。
构造函数是ECMAScript的一部分。另一方面,ngOnInit()是angular的概念。 即使不使用Angular,我们也可以在所有类中调用构造函数 生命周期:构造函数在ngOnInt()之前调用 在构造函数中,我们不能调用HTML元素。然而,在ngOnInit()中我们可以。 通常,服务调用在ngOnInit()中,而不是在构造函数中 来源:http://www.angular-tuto.com/Angular/Component Diff
其他回答
Constructor()用于进行依赖注入。
ngOnInit(), ngOnChanges()和ngOnDestroy()等都是生命周期方法。ngOnChanges()将是第一个被调用的,在ngOnInit()之前,当绑定属性的值发生变化时,如果没有变化,它将不会被调用。ngOnDestroy()在移除组件时被调用。要使用它,OnDestroy需要由类实现。
在Angular生命周期中
1) Angular注入器检测构造函数参数和实例化类。
2)下一个角调用生命周期
Angular生命周期钩子
调用指令参数绑定。
开始角渲染…
调用具有角生命周期状态的其他方法。
构造函数是类的默认方法,在类实例化时执行,并确保类及其子类中的字段正确初始化。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进行所有的初始化/声明,避免在构造函数中工作。构造函数应该只用于初始化类成员,而不应该做实际的“工作”。
因此,您应该使用构造函数()来设置依赖注入,而不是其他什么。ngOnInit()是更好的“开始”的地方——它是解析组件绑定的地方。
更多信息请参考这里:
https://angular.io/api/core/OnInit Angular组件构造函数Vs OnInit
需要注意的是,@Input值在构造函数中是不可访问的(感谢@tim在注释中提出的建议)
构造函数和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和其他生命周期钩子是更好的地方,特别是因为它们可以从异步语法中受益:
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可以完全stub,为其他组件单元(例如,一些模板逻辑)提供隔离。
继承
子类只能扩充构造函数,不能替换它们。
由于不能在super()之前引用this,这限制了初始化优先级。
考虑到Angular组件或指令使用ngOnInit进行时间不敏感的初始化逻辑,子类可以选择是否调用super.ngOnInit(),以及以下情况:
ngOnInit() {
this.someMethod();
super.ngOnInit();
}
仅使用构造函数是不可能实现的。
两种方法都有不同的目标/职责。构造函数(这是语言支持的特性)的任务是确保表示不变量成立。否则,通过给成员正确的值来确保实例是有效的。“正确”的含义由开发者决定。
onInit()方法(这是一个angular概念)的任务是允许在正确的对象上调用方法(表示不变)。每个方法都应该依次确保在方法终止时表示不变量保持不变。
构造函数应该用来创建“正确的”对象,onInit方法让你有机会在一个定义良好的实例中调用方法调用。