NgZone
是 Zone.js 的包装器,Zone.js 是一个围绕异步函数创建上下文以使它们可跟踪的库。
Angular 的变化检测很大程度上依赖于Zones
, How?
Angular 需要一种方法来了解何时运行更改检测,这基本上只是更新DOM
代表最新模型 ( javascript ) 更改。
想象一下我们有以下示例:
<div id="content"></div>
在我们的 javascript 代码中,我们有
const element = document.getElementById('content');
function updateText(){
element.innerHtml = myText+ ": updated at time"+Date.now()
}
假设一开始我要更新content
打个招呼:
const myText = "Hello";
this.updateText();
这会将我的 HTML 内容更新为文本:“Hello Updated at time 19:30”
然后假设我想更新myText
用户单击后变量变为其他内容:
<button onClick="updateTheTextAgain()"></button>
updateTheTextAgain(){
this.myText = "Hi there";
}
如果我点击那个按钮会发生什么?
Nothing;
好吧,实际上,这不是“什么”,我设法更新了变量,但我没有更新视图(我没有检测到模型的变化),所以我需要调整我的updateTheTextAgain
成为 :
updateTheTextAgain(){
this.myText = "Hi there";
this.updateText(); /// Making sure I'm detecting the change ( I'm updating the `DOM`)
}
现在,单击按钮将更新我的视图(因为手动更改检测)。
这显然不是最好的主意,因为那样我就得写很多updateText
函数并在我更新模型后希望更新视图的任何地方调用它们,对吧(返回 Angular1 并记住$scope.apply()
)?
这是哪里ZoneJs
是惊人的。
想象一下如果我可以重写onClick
函数,我的意思是浏览器的原始 onClick 函数是:
const originalOnClick = window.onClick;
window.onClick = function(){
originalOnClick();
this. updateText();
}
这就是所谓的monkey patching
or open heart surgery
本机函数。
我这样做能得到什么?
当我把我的patched onClick
在页面中,所有onClick
将要在整个应用程序中编写的函数将通过我的patched onClick
,这意味着,我不必运行updateText()
每次 onclick 之后就不再起作用,因为它被烘焙到click
事件处理程序本身。
在 Angular 中,updateText
is the change detection
, Angular 在所有原生事件中都有钩子(通过使用区域)。
所以当你写:
setTimeout(()=>{
console.log('Do something');
},100);
你实际上写的是这样的:
setTimeout(()=>{
console.log('Do something');
runAngularsChangeDetection();
},100);
上面的内容与现实中发生的事情相去甚远,但它是变化检测和变化整个故事的核心。Zones
以及为什么我们需要它们/
** 更新:**
我们什么时候应该使用NgZone
.
当你想要使用时,会有很多情况NgZone
,我可以说出两个:
1-当你想要某些东西在 Angular 的更改检测之外运行时:
还记得我说过 Angular 在所有异步事件中都有钩子吗?window.onScroll
就是其中之一,现在假设我们想要在用户滚动时进行一些计算,您通常做的是:
window.onscroll = ()=>{
// do some heavy calculation :
}
现在,当滚动时,您的函数将按照您的预期正常调用,但您可能会注意到遇到了一些性能问题,这可能是因为 Angular 正在运行changeDetection
在每个滚动事件上(预期行为)。
如果您的组件中有大量绑定,那么您肯定会注意到滚动性能下降。
所以一种方法是说,嘿 Angular,忽略我的onscroll
事件,我知道我在做什么,我不希望你运行更改检测,在这种情况下,你会使用NgZone
constructor(private zone:NgZone){
this.zone.runOutsideOfAngular(()=>{
window.onscroll = ()=>{
// do some heavy calculation :
}
})
}
这将确保 Angular 不会在滚动时运行更改检测。
另一种情况与上面完全相反,你有一个函数在某种程度上位于 Angular 区域之外,而你希望它位于内部,就像当第三方库为你做一些事情时,你希望它绑定到你的角度周期。
this.zone.run(()=>{
$.get('someUrl').then(()=>{
this.myViewVariable = "updated";
})
});
如果不使用 Zone,您很可能需要执行以下操作:
$.get('someUrl').then(()=>{
this.myViewVariable = "updated";
this.changeDetectorRef.detectChanges();
})
但请注意,当您的函数位于区域内( run 方法)时,您不必调用detectChanges
自己手动操作,角度会完成这项工作