Angular ngZone 源码解析
ngZone 源码中有几个常用的方法,属性,这里做一个整理与总结
Zone.js简介
ZoneJs 职责
- 拦截异步任务的调度
- 封装回调函数用于异常处理以及异步操作中zone的跟踪
- 提供往zone中添加数据的方法
- 提供上下文特定的最后一帧错误处理
- 拦截阻塞方法
Zone 的底层异步Task模型
- MicroTask 微任务,这个是在当前task 之后立刻执行的,不可取消.肯定是在当前的VM turn。
- MacroTask 宏任务,这个肯定是不在当前的VM turn内的。一个很好定义合理的延迟执行的任务e.g.
setTimeout,setInternal,requestAnimationFrame
- EventTask 事件任务。
Zone 的属性方法。下面是配置项的解构,可以感知到zone 内部会是啥样的
/**
* Provides a way to configure the interception of zone events.
*
* Only the `name` property is required (all other are optional).
*/
interface ZoneSpec {
/**
* The name of the zone. Useful when debugging Zones.
*/
name: string;
/**
* A set of properties to be associated with Zone. Use [Zone.get] to retrieve them.
*/
properties?: {[key: string]: any};
/**
* Allows the interception of zone forking.
*
* When the zone is being forked, the request is forwarded to this method for interception.
*
* @param parentZoneDelegate Delegate which performs the parent [ZoneSpec] operation.
* @param currentZone The current [Zone] where the current interceptor has been declared.
* @param targetZone The [Zone] which originally received the request.
* @param zoneSpec The argument passed into the `fork` method.
*/
onFork?:
(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone,
zoneSpec: ZoneSpec) => Zone;
/**
* Allows interception of the wrapping of the callback.
*
* @param parentZoneDelegate Delegate which performs the parent [ZoneSpec] operation.
* @param currentZone The current [Zone] where the current interceptor has been declared.
* @param targetZone The [Zone] which originally received the request.
* @param delegate The argument passed into the `wrap` method.
* @param source The argument passed into the `wrap` method.
*/
onIntercept?:
(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, delegate: Function,
source: string) => Function;
/**
* Allows interception of the callback invocation.
*
* @param parentZoneDelegate Delegate which performs the parent [ZoneSpec] operation.
* @param currentZone The current [Zone] where the current interceptor has been declared.
* @param targetZone The [Zone] which originally received the request.
* @param delegate The argument passed into the `run` method.
* @param applyThis The argument passed into the `run` method.
* @param applyArgs The argument passed into the `run` method.
* @param source The argument passed into the `run` method.
*/
onInvoke?:
(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, delegate: Function,
applyThis: any, applyArgs?: any[], source?: string) => any;
/**
* Allows interception of the error handling.
*
* @param parentZoneDelegate Delegate which performs the parent [ZoneSpec] operation.
* @param currentZone The current [Zone] where the current interceptor has been declared.
* @param targetZone The [Zone] which originally received the request.
* @param error The argument passed into the `handleError` method.
*/
onHandleError?:
(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone,
error: any) => boolean;
/**
* Allows interception of task scheduling.
*
* @param parentZoneDelegate Delegate which performs the parent [ZoneSpec] operation.
* @param currentZone The current [Zone] where the current interceptor has been declared.
* @param targetZone The [Zone] which originally received the request.
* @param task The argument passed into the `scheduleTask` method.
*/
onScheduleTask?:
(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, task: Task) => Task;
onInvokeTask?:
(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, task: Task,
applyThis: any, applyArgs?: any[]) => any;
/**
* Allows interception of task cancellation.
*
* @param parentZoneDelegate Delegate which performs the parent [ZoneSpec] operation.
* @param currentZone The current [Zone] where the current interceptor has been declared.
* @param targetZone The [Zone] which originally received the request.
* @param task The argument passed into the `cancelTask` method.
*/
onCancelTask?:
(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, task: Task) => any;
/**
* Notifies of changes to the task queue empty status.
*
* @param parentZoneDelegate Delegate which performs the parent [ZoneSpec] operation.
* @param currentZone The current [Zone] where the current interceptor has been declared.
* @param targetZone The [Zone] which originally received the request.
* @param hasTaskState
*/
onHasTask?:
(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone,
hasTaskState: HasTaskState) => void;
}
get(key:string):any
这个是从properties里面取值wrap<F extends Function>(callback: F, source: string): F;
在当前zone 中封装一个函数,当这个函数被执行时,会在当前的zone 中执行。跟apply/bind神似。run<T>(callback: Function, applyThis?: any, applyArgs?: any[], source?: string): T;
在当前的zone中执行函数runGuarded<T>(callback: Function, applyThis?: any, applyArgs?: any[], source?: string): T;
与上面的方法类似,区别在于异常处理会在当前的zone 中接管,如果没有处理会rethrow.runTask<T>(task: Task, applyThis?: any, applyArgs?: any): T;
执行当前zone 中执行一个Task(上面指定的三个task,micro/macro/eventtask)。这个方法属于底层方法.scheduleMicroTask(source: string, callback: Function, data?: TaskData,customSchedule?: (task: Task) => void): MicroTask;
scheduleMacroTask(source: string, callback: Function, data?: TaskData, customSchedule?: (task: Task) => void,customCancel?: (task: Task) => void): MacroTask;
scheduleEventTask(source: string, callback: Function, data?: TaskData, customSchedule?: (task: Task) => void, customCancel?: (task: Task) => void): EventTask;
scheduleTask<T extends Task>(task: T): T;
这个方法也是内部方法,上面三种schedule其实是调用的它。定义/安排一个taskcancelTask(task: Task): any;
fork(zoneSpec: ZoneSpec): Zone;
创建一个子Zone,
class 继承关系
interface NgZonePrivate extends NgZone
class NoopNgZone implements NgZone
这个是一个空类,啥也不干
直接介绍ngZone
方法
runOutsideAngular<T>(fn: (...args: any[]) => T): T
,这个经常用,将某个方法的执行置于ngZone 之外。run<T>(fn: (...args: any[]) => T, applyThis?: any, applyArgs?: any[]): T
,将某个方法置于ngZone中执行。runTask<T>(fn: (...args: any[]) => T, applyThis?: any, applyArgs?: any[], name?: string): T
以eventtask的方式来执行某个方法。这个方法好像是给angular 用的,会发布一些事件NgZoneEvent: xxxstatic isInAngularZone(): boolean
判断当前是否在angular zone 里面
属性
isStable: boolean
用于标记当前的zone 中没有未执行的Micro/Macro Task
事件
onUnstable: EventEmitter<any> = new EventEmitter(false)
当代码进入angularZone的时候。或者说有代码被ngZone拦截的时候。这个时候一般是发生在VM turn刚开始的时候,也就是一个宏任务开始的时候,例如setTimeout
里面的callback函数开始执行的时候。onMicrotaskEmpty: EventEmitter<any>
这个标记当前VM Turn里面所有的微任务都执行结束了。这个会指示Angular,你可以做ChangeDetect了。当然Angular的框架在CD的过程中也会参数更多的微任务。所以这个事件会很频繁的被发布。onStable: EventEmitter<any> = new EventEmitter(false)
。当最后一个onMicrotaskEmpty被发布后。再也没有微任务了。这个时候会发布这个事件,这个事件一般是在终止VM Turn 的时候发布。意味着Angular 完成了CD,没有未执行的js 代码了。页面完全处于idle 状态。onError: EventEmitter<any>
注意点
- 宏任务/微任务 跟js事件模型里面的是一致的。ngZone,通过对宏任务/微任务进行拦截。这些任务在ngZone里面统一被称为Task。只是类型不同。这些Task 的执行是在ngZone 里面的,所以ngZone 能够感知到这些事件。其实当宏任务被执行的时候,它在ngZone里面以及被当成了js 的微任务。
- Angular 框架在onMicrotaskEmpty发出的时候,可能会触发CD.所以这个方法可能会触发多次。