MyException - 我的异常网
当前位置:我的异常网» Go » Angular中Constructor 跟 ngOnInit 的本质区别

Angular中Constructor 跟 ngOnInit 的本质区别

www.MyException.Cn  网友分享于:2013-09-28  浏览:0次
Angular中Constructor 和 ngOnInit 的本质区别

在Medium看到一篇Angular的文章,深入对比了 Constructor 和 ngOnInit 的不同,受益匪浅,于是翻译过来让更多的前端小伙伴看到,译的不得当之处还请斧正。
原文:The essential difference between Constructor and ngOnInit in Angular
难以译出原意的术语都在圆括号里给出原词了。下面开始正文!

在stackoverflow上被问得很多的一个关于Angular的问题就是Difference between Constructor and ngOnInit,阅读量超过10万。我回答了这个问题,但还是决定在这篇文章展开讲一下。这上面的很多答案和网上的一些文章都把关注点放在这两者用法的区别上,在这里我想给出一个深入到组件初始化进程的更全面的答案。

有关JS和TS语言的区别

让我们从语言本身最明显的区别开始。在一个类中,ngOnInit只是一个在结构上与其他方法不一样的方法。Angular团队只是这样命名它,但它也可以有其他任何名字:

    class MyComponent {
        ngOnInit() { }
        otherNameForNgOnInit() { }
    }

在一个组件类中引不引入这个方法完全取决于你。编译过程中Angular编译器会检查组件是否引入了这个方法,然后用合适的标记去标记这个类:

    export const enum NodeFlags {
    ...
    OnInit = 1 << 16,

在变更检测过程中,在组件实例内,这个标记会被用来决定是否调用ngOnInit方法:

    if (def.flags & NodeFlags.OnInit && ...) {
        componentClassInstance.ngOnInit();
    }

相反,constructor是个不同的东西。在一个TypeScript类实例化过程中,无论写不写constructor,它都会被调用。这就是为什么一个TypeScript类的constructor会被转译成一个 JavaScript constructor function

    class MyComponent {
        constructor() {
            console.log('Hello');
        }
    }

转译成

    function MyComponent() {
        console.log('Hello');
    }

在创建类实例时这个函数会被用new操作符调用:

    const componentInstance = new MyComponent(

所以,如果你在类中省略constructor,这个类会被转译成一个空函数:

    function MyComponent() {}

这就是为什么我说在类中无论写不写constructor,它都会被调用。

有关组件初始化进程的区别

从组件初始化阶段的角度看,两者存在巨大差别。Angular bootstrap process(译注:这个比较微妙,不知道怎么翻译,暂且译作引导进程吧)包含两个主要阶段:

  • 构造组件树
  • 运行变更检测

而且,组件的constructor会在Angular构造组件树的时候被调用。所有生命周期钩子包括ngOnInit会被作为接下来的变更检测阶段的一部分被调用。通常,组件初始化逻辑需要一些依赖注入提供商(DI providers),或者可用的输入绑定,或者已渲染的DOM,在Angular 引导进程的不同阶段,这些都是可用的。

Angular构造组件树的时候,根模块注入器就已经配置好,所以你可以注入任何全局依赖。而且,当Angular实例化一个子组件类的时候,父组件的注入器也已经配置好,所以你可以注入父组件中定义的提供商(providers),包括父组件自身。组件的constructor是在注入器的上下文中被调用的唯一方法,所以如果你需要任何依赖,constructor是唯一获得这些依赖的地方。@Input的通信机制(communication mechanism)是作为接下来的变更检测阶段的一部分处理的,所以输入绑定在constructor中不可用。

Angular开始变更检测的时候组件树已经构造完毕,在组件树中的所有组件的constructor都会被调用。而且这时候所有组件的模板节点(template nodes)也已经添加到DOM中,这时,初始化组件的所有数据都已齐全——依赖注入提供商、DOM和输入绑定( DI providers, DOM and input bindings)。

你可以在Everything you need to know about change detection in Angular学习关于变更检测的知识,在The mechanics of property bindings update in Angular学习Angular进程如何输入。

我们用个简单例子证明这些阶段。假设有如下模板:

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

Angular开始引导应用程序。如上所述,它首先创建每个组件的类,因此调用MyAppComponent的constructor。当执行组件的constructor时,Angular resolves(译注:这个词不知道怎么翻译比较准确,就直接用原文了) 所有注入到MyAppComponentconstructor的依赖,并把他们作为参数提供出来(译注:这里翻译的比较拗口,原文是When executing a component constructor Angular resolves all dependencies that are injected into MyAppComponent constructor and provides them as parameters)。并且它会创建一个作为my-app宿主元素的DOM节点,然后它继续创建child-comp的宿主元素,并且调用ChildComponent的constructor。在这个阶段,Angular不关心i输入绑定和任何生命周期钩子。所以当这个过程完成的时候,Angular就创建出了如下组件视图树:

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

直到那时Angular才会运行变更检测、更新my-app的绑定、调用MyAppComponent实例的ngOnInit。然后它继续更新child-comp的绑定和调用ChildComponent类的ngOnInit

你可以在Here is why you will not find components inside Angular了解更多知识。

用法上的区别

现在从用法的角度看看两者的区别。

Constructor

在Angular中,一个类的constructor主要用来注入依赖。Angular调用constructor injection pattern这里已经解释得很详细,更深入的见解你可以读Miško Hevery的文章Constructor Injection vs. Setter Injection

然而,constructor的使用不仅限于依赖注入(DI)。举个例子,@angular/router模块的router-outlet指令在路由生态系统内用constructor来注册自己和自己的位置(viewContainerRef)。我在Here is how to get ViewContainerRef before @ViewChild query is evaluated把它描述了一遍。

惯例就是,在constructor中,逻辑应尽可能少。

NgOnInit

前文我们看到,当Angular调用ngOnInit的时候,它已经通过constructor完成创建组件DOM、注入所有必要的依赖,也已经完成输入绑定。这时所有必需信息已经齐全,这些信息使得ngOnInit成为执行初始化逻辑的好地方。

习惯上用ngOnInit来执行初始化逻辑,即使这些逻辑不依赖于依赖注入(DI)、DOM或者输入绑定。

文章评论

中美印日四国程序员比较
中美印日四国程序员比较
什么才是优秀的用户界面设计
什么才是优秀的用户界面设计
60个开发者不容错过的免费资源库
60个开发者不容错过的免费资源库
写给自己也写给你 自己到底该何去何从
写给自己也写给你 自己到底该何去何从
“懒”出效率是程序员的美德
“懒”出效率是程序员的美德
科技史上最臭名昭著的13大罪犯
科技史上最臭名昭著的13大罪犯
团队中“技术大拿”并非越多越好
团队中“技术大拿”并非越多越好
代码女神横空出世
代码女神横空出世
亲爱的项目经理,我恨你
亲爱的项目经理,我恨你
我的丈夫是个程序员
我的丈夫是个程序员
Google伦敦新总部 犹如星级庄园
Google伦敦新总部 犹如星级庄园
如何成为一名黑客
如何成为一名黑客
10个调试和排错的小建议
10个调试和排错的小建议
2013年美国开发者薪资调查报告
2013年美国开发者薪资调查报告
程序员都该阅读的书
程序员都该阅读的书
我跳槽是因为他们的显示器更大
我跳槽是因为他们的显示器更大
那些性感的让人尖叫的程序员
那些性感的让人尖叫的程序员
聊聊HTTPS和SSL/TLS协议
聊聊HTTPS和SSL/TLS协议
为什么程序员都是夜猫子
为什么程序员都是夜猫子
程序猿的崛起——Growth Hacker
程序猿的崛起——Growth Hacker
要嫁就嫁程序猿—钱多话少死的早
要嫁就嫁程序猿—钱多话少死的早
2013年中国软件开发者薪资调查报告
2013年中国软件开发者薪资调查报告
我是如何打败拖延症的
我是如何打败拖延症的
漫画:程序员的工作
漫画:程序员的工作
5款最佳正则表达式编辑调试器
5款最佳正则表达式编辑调试器
Java 与 .NET 的平台发展之争
Java 与 .NET 的平台发展之争
当下全球最炙手可热的八位少年创业者
当下全球最炙手可热的八位少年创业者
 程序员的样子
程序员的样子
程序员的一天:一寸光阴一寸金
程序员的一天:一寸光阴一寸金
如何区分一个程序员是“老手“还是“新手“?
如何区分一个程序员是“老手“还是“新手“?
10个帮程序员减压放松的网站
10个帮程序员减压放松的网站
为啥Android手机总会越用越慢?
为啥Android手机总会越用越慢?
Web开发者需具备的8个好习惯
Web开发者需具备的8个好习惯
程序员和编码员之间的区别
程序员和编码员之间的区别
那些争议最大的编程观点
那些争议最大的编程观点
Java程序员必看电影
Java程序员必看电影
程序员必看的十大电影
程序员必看的十大电影
老程序员的下场
老程序员的下场
老美怎么看待阿里赴美上市
老美怎么看待阿里赴美上市
旅行,写作,编程
旅行,写作,编程
初级 vs 高级开发者 哪个性价比更高?
初级 vs 高级开发者 哪个性价比更高?
十大编程算法助程序员走上高手之路
十大编程算法助程序员走上高手之路
程序员最害怕的5件事 你中招了吗?
程序员最害怕的5件事 你中招了吗?
编程语言是女人
编程语言是女人
程序员周末都喜欢做什么?
程序员周末都喜欢做什么?
一个程序员的时间管理
一个程序员的时间管理
看13位CEO、创始人和高管如何提高工作效率
看13位CEO、创始人和高管如何提高工作效率
不懂技术不要对懂技术的人说这很容易实现
不懂技术不要对懂技术的人说这很容易实现
程序员眼里IE浏览器是什么样的
程序员眼里IE浏览器是什么样的
软件开发程序错误异常ExceptionCopyright © 2009-2015 MyException 版权所有