zl程序教程

您现在的位置是:首页 >  前端

当前栏目

Angular 复习与进阶系列 – Component 组件 の Lifecycle Hooks

Angular组件 系列 进阶 Component 复习 Hooks lifecycle
2023-09-27 14:23:55 时间

前言

我们在 这篇 和 这篇 中已经学习了几个基本的 Lifecycle Hooks. 分别是

constructor

OnInit

AfterContentInit

AfterViewInit

OnDestroy

OnChanges

这篇我们会把其余的 Lifecycle Hooks 都学完.

 

Init Group and Changes Group

Angular 的 Lifecycle 可以分为两组.

第一组有 constructor, OnInit, AfterContentInit, AfterViewInit, OnDestroy

第二组有 OnChanges, DoCheck, AfterContentChecked, AfterViewChecked

在 first time render 时, 上面两组都会跑. 但绝大部分情况下我们关心第一组就可以了.

而在 event 发生后, 我们更多的是会用到第二组.

所以我喜欢把它们分开两组看待.

 

Init Group Lifecycle Hooks (组件内视角)

在一个组件内部观察的话, Lifecycle Hooks 的顺序是这样的

constructor > OnInit > AfterContentInit > AfterViewInit > OnDestroy

下面我讲一下各个 hook 的特点

constructor 

1. 这个阶段 @Input 还没有被赋值, 都是 undefined. 

2. 这个阶段 inject parent 已经拿到组件实例了, 但这个实例的 @Input 也是 undefined. 

3. 这个阶段 query child 是连组件实例都没有的. 空空如也

4. 这个阶段, 组件 template 还没有 append 到 document

OnInit

1. 这个阶段 @Input 已经有值了.

2. 通常我们会在这个阶段去 ajax 什么的, 大部分代码都会写在这个阶段里.

3. 如果 @ViewChild / @ContentChild 配置 option static : true, 那么这个阶段已经可以拿到组件实例了, 但是这个组件是还没有 OnInit 的 (它的 @Input 都是 undefined)

4. 这个阶段组件 template 已经 append 到 document 了, 但是涉及 binding 的部分都没有 render 好 (empty)

AfterContentInit

1. 这个阶段 @ContentChild 已经可以拿到组件实例了, 而且这个组件已经 OnInit 好了 (但还没有 AfterViewInit 哦)

AfterViewInit

1. 这个阶段组件 template binding 的部分都 render 好了.

2. 通常这个阶段我们可以去 read DOM dimension, 比如 window.getComputedStyle 之类的

3. 这个阶段我们不应该再去修改 view model 了, 如果要修改那样配上 requestAnimationFrame, 让 Angular 进入下一个渲染周期 (这个是 Change Detection 概念, 这里不过多展开)

OnDestroy

1. 这个阶段 DOM 已经被移除了.

2. 通常我们在这个阶段释放资源, 比如 RxJS unsubscribe 等等.

 

Init Group Lifecycle Hooks (组件外视角)

 

上一 part 我们是站在一个组件内的视角去看 lifecycle hooks. 现在我们从外部看多个组件它们 lifecycle hooks 的顺序是怎样的.

首先我们有 5 个组件

App, Child, InsideChild, TranscludeToChild, InsideTranscludeToChild

all html

<!-- app.html -->
<app-child>
  <app-transclude-to-child></app-transclude-to-child>
</app-child>

<!-- child.html -->
<app-inside-child></app-inside-child>
<ng-content></ng-content>

<!-- inside-child.html -->
<p>inside-child works!</p>

<!-- transclude-to-child.html -->
<app-inside-transclude-to-child></app-inside-transclude-to-child>

<!-- inside-transclude-to-child.html -->
<p>inside-transclude-to-child works!</p>

source 在这里 Github – repository

先了解组件结构

app > child > inside child 这 3 个就是一层一层进, 

transclude-to-child 最特别, app.html 把 transclude-to-child 组件 transclude 到 child 组件.

最后 transclude-to-child > inside-transclude-to-child 就是进一层.

constructor, OnInit, AfterContentInit, AfterViewInit 的顺序.

constructor 环节

第一个环节是 constructor. Angular 会把所有组件都实例化出来先.

1. app constructor

然后进入 app.html

<app-child>
  <app-transclude-to-child></app-transclude-to-child>
</app-child>

接着

2. child constructor

3. transclude to child constructor

接着进入 child.html

<p>child works!</p>
<app-inside-child></app-inside-child>
<ng-content></ng-content>

4. inside child constructor

进入 inside-child.html

<p>inside-child works!</p>

这里没有组件需要实例化了. 返回最外面 app,html

<app-child>
  <app-transclude-to-child></app-transclude-to-child>
</app-child>

进入 transclude-to-child.html

<p>transclude-to-child works!</p>
<app-inside-transclude-to-child></app-inside-transclude-to-child>

5. inside transclude to child constructor

进入 inside-transclude-to-child.html

<p>inside-transclude-to-child works!</p>

没有组件需要实例化了. 

至此 constructor 环境结束. 以下是组件 constructor 的 console

OnInit > AfterContentInit > AfterViewInit 环节

和 constructor 不同, Angular 不会先把所有组件 OnInit 一遍才 AfterContentInit. OnInit 和 AfterContentInit 是前脚后脚一起的, 看下面例子理解.

1. app init

<body>
  <app-root></app-root>
</body>

由于 app-root 没有任何的 transclude, 所以 app 组件 AfterContentInit 紧跟着 OnInit 后就触发

2. app after content init

进入 app.html

<app-child>
  <app-transclude-to-child></app-transclude-to-child>
</app-child>

3. child init

由于 child 有 transclude 所以它的 AfterContentInit 不会马上触发.

4. transclude to child init

transclude to child 没有 transclude 所以 AfterContentInit 马上触发

5. transclude to child after content init

此时 child 的 transclude 完成了, 所以到它的 after content init 

6. child after content init

进入 child.html

<p>child works!</p>
<app-inside-child></app-inside-child>
<ng-content></ng-content>

7. inside child init

8. inside child after content init

进入 inside-child.html

<p>inside-child works!</p>

没有组件了, 所以开始 render view, 并且触发 AfterViewInit

9. inside child after view init

到目前为止的 console

回到 child.html

<p>child works!</p>
<app-inside-child></app-inside-child>
<ng-content></ng-content>

before render child component, 要先处理好 ng-content

 

这个 ng-content 内容是 transclude-to-child, 它已经 after content init 了.

进入 transclude-to-child.html

<p>transclude-to-child works!</p>
<app-inside-transclude-to-child></app-inside-transclude-to-child>

10. inside transclude to child init 

11. inside transclude to child after content init

进入 inside-transclude-to-child.html

<p>inside-transclude-to-child works!</p>

没组件了, render view

12. inside transclude to child after view init

回到 transclude-to-child.html

<p>transclude-to-child works!</p>
<app-inside-transclude-to-child></app-inside-transclude-to-child>

没有组件了, render view

13. transclude to child after view init

回到 child.html

<p>child works!</p>
<app-inside-child></app-inside-child>
<ng-content></ng-content>

ng-content 也完成了, 现在 render view

14. child after view init

回到 app.html

<app-child>
  <app-transclude-to-child></app-transclude-to-child>
</app-child>

15. app after view init 

最后的 console

 

 

 

 

Changes Group Lifecycle Hooks

我建议大家先学 Change Detection 在回来看这个部分. 

 

 

Changes group lifecycle hooks 和 change detection 比较有关联. 但我不想过度关注 change detection.

因为 Angular 要推出 Signal 了. change detection 会有重大改变. 某些相关的 lifecycle hooks 也会改. 所以这里大概讲一下就好了.

虽然 Changes Group Lifecycle Hooks 在 first time render 也会触发, 但很少会要用到, 

所以这里我 focus 的是, 因为后续 event 而触发的 lifecycle.

DoCheck

1. 每当有异步事件发生, 比如 setTimeout, ajax, event dispatch, DoCheck 都会触发.

2. DoCheck 阶段是让我们告诉 Angular 组件是否要 detech change 的.

3. 这个阶段我们可以去修改 view model.

 

OnChanges

1. 每当 @Input 改变, 它就会触发, 包括 first time render (input from undefined to value) 也会.

2. 这个阶段我们任然可以去修改 view model