angular如何解决无限递归?
How does angular resolve infinite recursion?
我正在学习这个 Udemy 课程:
https://www.udemy.com/the-complete-guide-to-angular-2/
我正在学习关于字符串插值的第 2.13 部分。
作为课程的一部分,您在组件中定义了一个简单的函数来显示 class:
的属性之一
export class ServerComponent{
serverId = 10;
serverStatus = 'offline';
getServerStatus(){
return this.serverStatus;
}
}
然后在您的组件 HTML 中将该函数绑定到模板:
<P>Server with ID {{ serverId }} is {{ getServerStatus() }}</P>
我想测试的是,当您将该字符串插值标记绑定到一个自引用函数时会发生什么...例如:
getServerStatus(){
this.serverStatus = this.serverStatus + this.serverStatus;
return this.serverStatus;
}
当我运行这段代码时,我注意到页面显示如下:
server with ID 10 is offlineofflineofflineofflineofflineofflineofflineoffline
serverStatus 变量正好重复 8 次。
我想知道为什么这个数量恰好是 8 次重复? angular 使用什么逻辑来决定 'real time' 模板指令在重复 8 次后从 class 属性 中切断。
Angular 在应用程序开始 时启动 两个更改检测周期。
也就是说,它调用了两次Application.tick()
方法
private _loadComponent(componentRef: ComponentRef<any>): void {
this.attachView(componentRef.hostView);
this.tick();
2) 并且在第一个 VM 轮到(当 zonejs 中没有微任务时)(https://github.com/angular/angular/blob/aaaa34021c2d56f798d20e5a1f31b23972055170/packages/core/src/application_ref.ts#L385-L386)
this._zone.onMicrotaskEmpty.subscribe(
{next: () => { this._zone.run(() => { this.tick(); }); }});
考虑到这一点,让我们回到我们的 Application.tick() 方法。它在视图树(组件视图或嵌入式视图)上运行变化检测。
tick(): void {
...
try {
...
this._views.forEach((view) => view.detectChanges());
if (this._enforceNoNewChanges) {
this._views.forEach((view) => view.checkNoChanges());
}
} catch (e) {
...
} finally {
...
}
}
这里我们能注意到什么?
我们可以注意到 在开发模式下(因为 this._enforceNoNewChanges = isDevMode();
https://github.com/angular/angular/blob/aaaa34021c2d56f798d20e5a1f31b23972055170/packages/core/src/application_ref.ts#L383) Angular 运行变化检测周期两次。
这里还有一点就是tick
方法是在try catch
块中执行的。
那么,到目前为止我们有什么?
2 сd cycles * 2 view.detectChanges() on the tree = 4
同样在每个 view.detectChanges()
Angular 上检查模板绑定是否已更改。为此 Angular 执行模板中的每个表达式 (因此你的 getServerStatus()
方法将在每次树遍历时执行)。如果在使用 tick 方法的第二次 cd 期间,绑定发生了一些变化,那么 Angular 会抛出错误 Expression has changed after it was checked
。你可以猜到它不会停止后续的 cd 循环谢谢你 try catch
block.
为简单起见,假设您有以下模板:
{{ getServerStatus() }}
那么这里发生了什么?
Start app serverStatus
loadComponent => tick
|
|__ view.detectChanges()
||
\/
call getServerStatus() 'offlineoffline'
|__ view.checkNoChanges()
||
\/
call getServerStatus() 'offlineofflineofflineoffline'
'offlineoffline' !== 'offlineofflineofflineoffline'
||
\/
ExpressionChangedAfterItHasBeenCheckedError (template is not updated!!)
onMicrotaskEmpty => tick
|
|__ view.detectChanges()
||
\/
call getServerStatus() 'offline'.repeat(8)
|__ view.checkNoChanges()
||
\/
call getServerStatus() 'offline'.repeat(16)
'offline'.repeat(8) !== 'offline'.repeat(16)
||
\/
ExpressionChangedAfterItHasBeenCheckedError (template is not updated!!)
因此,您正好重复了 8 次 serverStatus
我正在学习这个 Udemy 课程: https://www.udemy.com/the-complete-guide-to-angular-2/
我正在学习关于字符串插值的第 2.13 部分。
作为课程的一部分,您在组件中定义了一个简单的函数来显示 class:
的属性之一export class ServerComponent{
serverId = 10;
serverStatus = 'offline';
getServerStatus(){
return this.serverStatus;
}
}
然后在您的组件 HTML 中将该函数绑定到模板:
<P>Server with ID {{ serverId }} is {{ getServerStatus() }}</P>
我想测试的是,当您将该字符串插值标记绑定到一个自引用函数时会发生什么...例如:
getServerStatus(){
this.serverStatus = this.serverStatus + this.serverStatus;
return this.serverStatus;
}
当我运行这段代码时,我注意到页面显示如下:
server with ID 10 is offlineofflineofflineofflineofflineofflineofflineoffline
serverStatus 变量正好重复 8 次。
我想知道为什么这个数量恰好是 8 次重复? angular 使用什么逻辑来决定 'real time' 模板指令在重复 8 次后从 class 属性 中切断。
Angular 在应用程序开始 时启动 两个更改检测周期。
也就是说,它调用了两次Application.tick()
方法
private _loadComponent(componentRef: ComponentRef<any>): void {
this.attachView(componentRef.hostView);
this.tick();
2) 并且在第一个 VM 轮到(当 zonejs 中没有微任务时)(https://github.com/angular/angular/blob/aaaa34021c2d56f798d20e5a1f31b23972055170/packages/core/src/application_ref.ts#L385-L386)
this._zone.onMicrotaskEmpty.subscribe(
{next: () => { this._zone.run(() => { this.tick(); }); }});
考虑到这一点,让我们回到我们的 Application.tick() 方法。它在视图树(组件视图或嵌入式视图)上运行变化检测。
tick(): void {
...
try {
...
this._views.forEach((view) => view.detectChanges());
if (this._enforceNoNewChanges) {
this._views.forEach((view) => view.checkNoChanges());
}
} catch (e) {
...
} finally {
...
}
}
这里我们能注意到什么?
我们可以注意到 在开发模式下(因为 this._enforceNoNewChanges = isDevMode();
https://github.com/angular/angular/blob/aaaa34021c2d56f798d20e5a1f31b23972055170/packages/core/src/application_ref.ts#L383) Angular 运行变化检测周期两次。
这里还有一点就是tick
方法是在try catch
块中执行的。
那么,到目前为止我们有什么?
2 сd cycles * 2 view.detectChanges() on the tree = 4
同样在每个 view.detectChanges()
Angular 上检查模板绑定是否已更改。为此 Angular 执行模板中的每个表达式 (因此你的 getServerStatus()
方法将在每次树遍历时执行)。如果在使用 tick 方法的第二次 cd 期间,绑定发生了一些变化,那么 Angular 会抛出错误 Expression has changed after it was checked
。你可以猜到它不会停止后续的 cd 循环谢谢你 try catch
block.
为简单起见,假设您有以下模板:
{{ getServerStatus() }}
那么这里发生了什么?
Start app serverStatus
loadComponent => tick
|
|__ view.detectChanges()
||
\/
call getServerStatus() 'offlineoffline'
|__ view.checkNoChanges()
||
\/
call getServerStatus() 'offlineofflineofflineoffline'
'offlineoffline' !== 'offlineofflineofflineoffline'
||
\/
ExpressionChangedAfterItHasBeenCheckedError (template is not updated!!)
onMicrotaskEmpty => tick
|
|__ view.detectChanges()
||
\/
call getServerStatus() 'offline'.repeat(8)
|__ view.checkNoChanges()
||
\/
call getServerStatus() 'offline'.repeat(16)
'offline'.repeat(8) !== 'offline'.repeat(16)
||
\/
ExpressionChangedAfterItHasBeenCheckedError (template is not updated!!)
因此,您正好重复了 8 次 serverStatus