【问题标题】:Difference between Constructor and ngOnInit构造函数和 ngOnInit 的区别
【发布时间】:2016-06-16 06:43:51
【问题描述】:

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

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

【问题讨论】:

    标签: angular typescript ngoninit angular-lifecycle-hooks


    【解决方案1】:

    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() 是更好的“开始”位置 - 它是解决组件绑定的位置/时间。

    更多信息请参考这里:

    【讨论】:

    • 确切地说,大多数(甚至所有)基于类的语言都有构造函数来确保正确的初始化顺序,尤其是扩展其他类的类,这些类可能会出现一些非常困难的问题,比如最终字段(不知道如果 TS 有它们)和类似的。构造函数与 Angular2 无关,它们是 TypeScript 的特性。在某些初始化发生或发生某些事件时,Angular 会调用生命周期挂钩,以允许组件在某些情况下采取行动并使其有机会在适当的时间执行某些任务。
    • angular.io/docs/ts/latest/guide/server-communication.html 中有一个块引用也解释了这一点:“当它们的构造函数简单时,组件更容易测试和调试,并且所有实际工作(尤其是调用远程服务器)都在一个单独的方法。” - 在这种情况下,该方法是 ngOnInit()
    • 是 Angular2 调用的生命周期钩子,表示 Angular 已完成创建组件。 - 不完全是这样。它表示它已初始化绑定。该组件是较早创建的。见my answer
    • 与所有“最佳实践”一样,我认为最好也解释一下为什么你不应该在构造函数中做“工作”。 Angular 团队负责人的这篇文章很密集,但可能会有所帮助:misko.hevery.com/code-reviewers-guide/… 此外,应该不太重视实现 OnInit 所需的咒语(这很容易找到),而更多地关注数据绑定不是这一关键事实在构造函数中可用。
    • 如果严格模式在tsconfig.json 文件中为真,例如"strict": true,那么您必须在constructor 中初始化类成员,而不是在ngOnit 中,例如FormGroup
    【解决方案2】:

    文章The essential difference between Constructor and ngOnInit in Angular从多个角度探讨了不同之处。这个答案提供了与组件初始化过程相关的最重要的差异解释,这也显示了用法的不同。

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

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

    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 data
           ChildCompnentView
             - 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 展示了在构造函数中需要执行什么类型的初始化逻辑。

    这里有一些文章可以帮助您更好地理解该主题:

    【讨论】:

    • 这应该是公认的答案。它实际上解释了为什么,而不是重复口头禅和陈述the constructor should only be used to inject dependencies
    • @yannick1976,谢谢!查看参考文章
    • @flobacca,能否请您重新提出问题,很难理解您在问什么
    • 如果我错了,请纠正我。我理解组件树是首先构建然后更改检测过程。您编写的第一个 AppComponent 构造函数被调用(连同已解决的依赖项),然后 ChildComponent 构造函数被调用(连同依赖项),然后是 AppComponent 的输入绑定,然后调用 OnInit。但我担心的是,如果我向两个组件添加生命周期挂钩,则流程是 AppComponentConstructor — ->AppComponentOnInit — →ChildComponentConstructor — →ChildComponentOnInit 为什么在 ChildComponentConstructor 之前调用 AppComponentOnInit
    • @MaxKoretskyiakaWizard 你是对的。我在应用程序设置中犯了一些错误。它按照您的描述工作。 angular-c7zjsx.stackblitz.io
    【解决方案3】:

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

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

    它使它更容易查看,因此我只需在初始化时在组件中调用我的函数,而不必在其他地方挖掘它。实际上,它只是您可以用来使其在未来更易于阅读和使用的另一种工具。此外,我发现将函数调用放在构造函数中是非常糟糕的做法!

    【讨论】:

    • 如果您只是将 user_list 设置为 Observable,则可以简化您的示例。 Angular2 有异步管道,所以那里不会有任何问题。
    • @Morgan,只是我在这里学习一个小东西,为什么要先创建一个函数getUsers,然后插入到ngOnInit?仅仅在ngOnInit中编写代码不是更少吗?我只是想知道为什么人们会这样做?如果您愿意,您是否也可以重用代码?谢谢。
    • 如下面的答案所示,如果它在构造函数中,则没有区别。这不是目的的真正答案。
    • 我根本不明白这是如何回答这个问题的。为什么你不能把代码放在constructor 中?
    • @Morgan 你为什么不能做constructor(private _userService: UserService){ this.getUsers(); };
    【解决方案4】:

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

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

    在 Angular2+ 中,我们使用 constructor 为我们执行 DI(Dependency Injection),而在 Angular 1 中,它是通过调用 String 方法并检查注入的依赖项来实现的。

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

    好的,我也分享一个示例代码给你看,看看我们如何在下面的代码中使用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!');
      }
    }
    

    【讨论】:

    • 谢谢。这应该是最好的答案。
    • 感谢您提供这张出色的图表。对我们帮助很大!!
    • @Alireza 我们可以在同一个组件中定义的样式中添加数组吗:[]?
    【解决方案5】:

    我将只添加在上面的解释中跳过的一件重要的事情,并解释你何时必须使用ngOnInit

    如果您正在通过例如对组件的 DOM 进行任何操作。 ViewChildrenContentChildrenElementRef,您的原生元素在构造器阶段将不可用。

    但是,由于 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 方法。见这里:stackoverflow.com/questions/46314734/…
    • 感谢@AsGoodAsItGets 的指出。我现在改进了答案
    • 这是最美的回应。我们研究 Angular 生命周期钩子的原因主要归结为这些用例。
    • 很高兴你发现它有帮助,@N.Raj
    【解决方案6】:

    第一个(构造函数)与类实例化有关,与Angular2无关。我的意思是构造函数可以用于任何类。您可以在其中对新创建的实例进行一些初始化处理。

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

    引用自 Angular 官网:

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

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

    【讨论】:

      【解决方案7】:

      简短的回答是,

      Constructorconstructordefault method 在构建组件时运行(默认)。当您创建一个类的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() 
         }
      }
      

      资源:LifeCycle hook

      您可以查看此 small demo,它显示了这两种情况的实现。

      【讨论】:

      • 我认为“构造函数是在组件初始化时运行或调用的东西。”具有误导性。构造函数是类的特性而不是组件的特性。我会说这个类的实例只有在构造函数被调用之后才成为一个组件并且Angular做了它的初始化。
      • Yes 更改了您现在可以查看的声明。
      • 嗯,恕我直言,它仍然是相同的“构造函数(默认方法)是在构造组件时运行或调用的东西。”。它不仅在构建组件时被调用,而且在服务或像new MyClass() 这样的代码被执行时也被调用。我认为说构造函数是关于组件的,它们是关于类和初始化这些类的实例的,这是一种误导。一个组件恰好是这样一个类。否则我认为这是一个很好的答案。
      • 绝对是的。忘了提到,当你创建一个类的对象时,constructor 也会被调用。但是这个答案是在 angular2 上下文中编写的。要知道最佳答案,您必须了解 OOP 基础知识。我还是会更新答案。
      • @GünterZöchbauer,我不认为这是一个正确的断言是类的一个特性而不是组件的特性。从编程语言的角度来看,是的,这是正确的。但是我可以成功地使用组件而无需任何生命周期挂钩。但是如果我需要 DI,我就不能使用没有构造函数的组件,因为这是唯一可注入的地方。见my answer
      【解决方案8】:

      构造函数和ngOnInit 的主要区别在于ngOnInitlifecycle hook 并且在构造函数之后运行。组件插值模板和输入初始值在构造函数中不可用,但在 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 比构造函数更灵活,并为单元测试提供了一些好处,this answer 中有详细说明。

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

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

      继承

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

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

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

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

      单独使用构造函数是不可能实现的。

      【讨论】:

        【解决方案9】:

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

        类级变量

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

        【讨论】:

          【解决方案10】:

          为了测试这一点,我编写了这段代码,借鉴了NativeScript Tutorial

          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 ***  
          

          【讨论】:

            【解决方案11】:

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

            1) 假设您的组件是人。人类的生活包括许多生活阶段,然后我们就结束了。

            2) 我们的人类组件可能具有以下生命周期脚本:出生、婴儿、小学、青年、中年、老年、死亡、处置。

            3) 假设你想要一个函数来创建孩子。为了避免这变得复杂,而且相当幽默,你希望你的函数只在人类组件生命的年轻成人阶段被调用。因此,您开发的组件仅在父组件处于青年阶段时才处于活动状态。 Hooks 通过发出生命阶段的信号并让您的组件对其进行操作来帮助您做到这一点。

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

            【讨论】:

              【解决方案12】:

              constructor是JavaScript中的一种方法,在es6中被认为是类的一个特性。当类被实例化时,无论是否使用它都会立即运行构造函数是否在 Angular 框架中。所以它由 JavaScript 引擎调用,而 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 中有 ngOnInit 生命周期钩子。当 Angular 完成初始化组件时,ngOnInit 会渲染。

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

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

              let instance = new NGONINITTEST();
              

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

              instance.ngOnInit();
              

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

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

              【讨论】:

                【解决方案13】:

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

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

                即:

                Constructor() --> ngOnChanges() --> ngOnInit()

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

                【讨论】:

                  【解决方案14】:

                  这里要注意两点:

                  1. 只要创建该类的对象,就会调用构造函数。
                  2. ngOnInit 在组件创建后调用。

                  两者都有不同的可用性。

                  【讨论】:

                    【解决方案15】:

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

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

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

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

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

                    【讨论】:

                      【解决方案16】:

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

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

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

                      【讨论】:

                        【解决方案17】:

                        构造函数是第一个执行的,它有时会在@input 数据为空时发生! 所以我们使用 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 中的 InitialComponents() 。

                        【讨论】:

                          【解决方案18】:

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

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

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

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

                          【讨论】:

                            【解决方案19】:

                            在 Angular 生命周期中

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

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

                            Angular Lifecycle Hooks

                            ngOnChanges --> 调用指令参数绑定。

                            ngOnInit --> 开始角度渲染...

                            调用具有角度生命周期状态的其他方法。

                            【讨论】:

                              【解决方案20】:

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

                              constructor(private 
                                  service1: Service1,
                                  service2: Service2
                              ){};
                              

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

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

                              此外,来自父组件的涉及 输入属性 的操作无法在构造函数中完成。 它们应该放在ngOnInit 方法或其他钩子中。 与视图相关的元素(DOM)也是如此,例如viewchild elements

                              @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
                              };
                              

                              【讨论】:

                                【解决方案21】:

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

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

                                构造函数将在任何生命周期函数之前执行。

                                【讨论】:

                                  【解决方案22】:

                                  构造函数

                                  每个类都有构造函数,构造函数不是特定于 Angular 的,而是从面向对象设计中派生的概念。构造函数创建组件类的实例。

                                  OnInit

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

                                  【讨论】:

                                    【解决方案23】:

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

                                    虽然ngOnInIt 是 Angular 的生命周期钩子。它在您的组件初始化时被初始化。 (认为​​它是任何新生命诞生的状态)

                                    与构造函数相比,使用ngOnInIt 是明智的,因为您还有另一个生命周期钩子,例如ngOnDestory(将其视为任何生命的死亡)。在这里,您可以取消订阅任何可防止任何内存泄漏的 observable。

                                    如有任何问题,请随时对此答案发表评论。

                                    【讨论】:

                                      【解决方案24】:

                                      constructor()用来做依赖注入。

                                      ngOnInit()ngOnChanges()ngOnDestroy() 等是生命周期方法。 ngOnChanges() 将首先被调用,在ngOnInit() 之前,当绑定属性的值发生变化时,如果没有变化,则不会调用它。 ngOnDestroy() 在组件被移除时被调用。要使用它,OnDestroy 需要被班级 implemented。

                                      【讨论】:

                                      • 同意,这很简短。例如,constructor() 用于添加服务对象,ngOnInit() 用于通过必要的服务函数调用来操作组件。
                                      【解决方案25】:

                                      我找到了答案,并尝试将其翻译成英文: 即使在技术面试中,这个问题仍然存在。其实两者有很大的相似之处,但也有一些不同。

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

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

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

                                      • 在构造函数中我们不能调用 HTML 元素。但是,在 ngOnInit() 中我们可以。

                                      • 一般来说,服务的调用在 ngOnInit() 中而不是在构造函数中

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

                                      【讨论】:

                                        【解决方案26】:

                                        constructor()可以接受参数,可以用于依赖注入或者constructor()用于添加服务对象。

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

                                        ngOnInit() 用于通过必要的服务函数调用来操作组件,或者通常在 ngOnInit() 中而不是在构造函数中调用服务

                                        【讨论】:

                                          【解决方案27】:

                                          Constructor是Typescript类提供的默认方法,专门用于初始化类成员,一般用于依赖注入服务,如上面的示例代码,或者定时器初始化,socket连接初始化

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

                                          ngOnInit:是一个生命周期钩子,在组件初始化时调用,由 Angular 提供,专门用于业务逻辑、数据初始化、API 调用等,演示 API 调用的示例代码:

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

                                          【讨论】:

                                            猜你喜欢
                                            • 2019-08-30
                                            • 2017-03-30
                                            • 1970-01-01
                                            • 2011-06-01
                                            • 2015-01-05
                                            • 1970-01-01
                                            • 1970-01-01
                                            相关资源
                                            最近更新 更多