您现在的位置是:首页 > 正文

Angular编译及变化检测相关详解

2024-04-01 04:18:01阅读 2

一、编译

1、为什么 Angular 需要编译

Angular 是基于 TypeScript,编译打包的时候会用 tsc 将 TypeScript 编译成 es5 文件,这样在浏览器 JavaScript Virtual Machines (VM) 可以直接运行 es5 代码。那么 Angular 为什么还需要编译呢?在 Angular 中除了 TypeScript 之外,还有 HTML 模板文件,在这些模板文件里有 Angular 自带的组件、指令、管道等等,为了让浏览器可以识别和运行这些东西,那么就需要用 Angular 编译器把这些编译成浏览器可识别和运行的 es5 代码。

2、Angular 编译机制:JiT vs AoT

ng build和ng serve是 JiT 的编译方式,
ng build --prod ng build --aot 或者ng serve --aot是 AoT 的编译方式。

一句话概括两者的区别: Angular 编译器(ngc)执行的时机不一样

JiT 是浏览器在渲染页面的时候先把 Angular 编译器下载到本地,然后把 HTML 模板编译成浏览器可识别运行的 es5 代码;

AoT 是项目在打包的时候就把 HTML 模板编译成浏览器可识别运行的 es5 代码,在浏览器渲染时不需要下载 Angular 编译器也不需要编译,直接运行这些代码就可以了

3、总结一下 JiT 和 AoT 的主要区别:

  • 打包之后的文件大小不一样,最大的区别是 JiT 需要把 compiler 源码打包进 bundle 文件,而 AoT 不需要。

  • bundle 文件的内容也有很大区别:JiT 把 HTML 模板直接内联进 bundle 文件不做任何处理,AoT 会把 HTML 模板文件编译 es5 文件。

  • 编译执行的时机不一样,JiT 是在浏览器里执行,需要内联的 HTML 文件编译成 es5 代码;AoT 是在编译打包的时候就把 HTML 文件编译成 es5 代码。

  • 不管是 JiT 还是 AoT,最终的结果文件是一样的。JiT 编译的 bundle 文件,在浏览器渲染之前需要下载 compiler 源码,然后 compiler 源码对 JiT 的 bundle 文件进行编译,也会在本地生成*.ngfactory.js文件,最后进行渲染。在浏览器里先编译再渲染肯定没有 AoT 直接在浏览器里渲染性能高。

  • 除此之外,还有一点好处是,AoT 把模板文件编译成 es6 文件,可以做 Tree-Shaking,也会提高性能。

二、变化检测

1、单向数据流

比如在 child A component 从 http response 拿到最新 model 的值,并且需要把变化后的值渲染到页面,这个过程会触发 child A component 的变化检测(change detection)。这个变化检测不仅仅会在 child A component 中执行,它会从 root component 开始沿着 component 关系树结构从上到下执行,直到最后一个 child component 完成变化检测达到稳定状态。这个过程就是 Angular 的单向数据流

注:在ngOnInit 、ngDoCheck、ngAfterContentInit、ngAfterContentChecked、ngOnChanges,里面 通过 @Output 向 父层 发送 message 都能正常显示也不会报错。 但是在 ngAfterViewInit、gAfterViewChecked,会有出现错误。
在这里插入图片描述
出现这种错误的原因是: 在 Angular 中强制了单向数据流,当有变化的时候,变化检测机制是沿着 component 关系树结构从上到下执行,直到最后一个 child component 变化检测完成,这个完整的变化检测才算结束。在这个过程中,parent component 的变化检测完成以后,任何更低一层级去改上一层级的属性,都不允许。如果是在生产环境里,也就是启用了 enableProdMode() 会直接忽略这样的操作,页面也不会显示变化以后的值,也不会报错。但是在开发模式下,在每一次变换检测(change detection)以后,Angular 会从上到下再多跑一个变化检测,确保每次改动之后所有的状态是 stable 的,这个时候发现有低层级改动上一层级的值,就会出现上面那个错误。

为什么呢?AfterViewChecked和AfterViewInit是在它自己的变化检测(change detetion)之后再执行的,也就是它状态 stable 之后再执行的,这时候在去触发它上一层级属性的改动,被认为是违反 Angular 的单向数据流。

2、变化检测机制

我们都知道在Angular 里,每个 component 都有一个 html 模板,在 Angular 内部,编译器在 component 和模板之间会生成一个 component view。数据绑定、脏数据检查和更新 DOM 都是由这个 component view 实现的。变化检测机制也可以说就是沿着 component view 的树状结构从上到下执行的。

Component View 到底是什么?

提到过 ngc 会生成.ngfactory.js,其实 ngfactory 就是 Component View。

在*.ngfactory.js过程中,ngc 会把所有可能发生变化的DOM Nodes/Elements都找出来,然后给这些DOM Nodes/Elements生成Bindings,这些Bindings里会记录Element Name/Expression/OldValue,一旦有异步事件发生(Click事件或者是HttpRequest)就会被ngZone捕获到,然后触发Change Detection,也就是会从Root Component开始,从上到下检查所有组件的Bindings也就是前面提到的Component View,对比NewVaule和OldValue,如果不一致就会把新值更新到页面,同时把新值更新为旧值(这也就是我们经常提到的脏检查机制Dirty Checking)

angular通常有如下三种方式会导致组件数据变化:

  • 事件:页面 click、submit、mouse down……
  • XHR:从后端服务器拿到数据
  • Timers:setTimeout()、setInterval()

Angular 又怎么通知各个组件做变化检测?

NgZone 可以简单的理解为是一个异步事件拦截器,它能够 hook 到异步任务的执行上下文,然后就可以来处理一些操作,比如每个异步任务 callback 以后就会去通知 Angular 做变化检测。

Angular 源码中有一个ApplicationRef,可以监听 NgZones onTurnDone事件,每当onTurnDone被触发后,它会立马执行tick()方法,tick()会从上到下沿着组件树触发变化检测,每个 component 都有自己的变化检测器,负责检查它们各自的绑定。

3、变化检测策略

Angular 默认的变化检测机制是ChangeDetectionStrategy.Default:异步事件 callback 结束后,NgZone 会触发整个组件树至上而下做变化检测,如下所示

在这里插入图片描述
但是在实际应用里,并不是每个异步操作需要变化检测,某些组件也可以完全不用做变化检测,应用越大页面越复杂,过多的变化检测会影响整个应用的性能。Angular 除了默认的变化检测机制,也提供了ChangeDetectionStrategy.OnPush,用 OnPush 可以跳过某个组件或者某个父组件以及它下面所有子组件的变化检测
在这里插入图片描述
在组件里加了 OnPush 策略,以下四种情况还是可以触发该组件的变化检测:

  • 组件的@Input引用发生变化。

  • 组件的 DOM 事件,包括它子组件的 DOM 事件,比如 click、submit、mouse down。

  • Observable 订阅事件,同时设置 Async pipe。

  • 利用以下方式手动触发变化检测:
    ChangeDetectorRef.detectChanges
    ChangeDetectorRef.markForCheck()
    ApplicationRef.tick()

在这里插入图片描述
在这里插入图片描述

注:

  • 除了 Object 以外的所有类型,是通过值传递的,每次对它们的改动都会在内存里生成一个新的值。而 Object 是通过引用传递的,每次对 Object 改动,引用不会改变。

  • 如果是默认的变化检测策略,setInterval 会触发组件变化检测,页面的 counter 会每过一秒就自动更新一次。现在 CDChildComponent 设置了 OnPush,setInterval 不会触发变化检测,页面上的 counter 不会有任何变化

  • detectChanges 会立马触发当前组件和它子组件变化检测。markForCheck 并不会立马触发变化检测,而是标记需要被变化检测,在当前或下一轮的变化检测中被触发。ApplicationRef.tick() 触发整个应用的组件树从上到下执行变化检测。

网站文章