angularJS中,并没有单向数据流的概念,子组件是可以修改父组件的值的 所以angularJS的脏检测会一遍又一遍地检测,直到angular的数据模型都“稳定下来”,如果超过10次都不同就会报错
而angular中是有单向数据流的,因为有了这个保证,所以自上而下地做一遍检测,就是足够的,就是可信任的。也因此,angular的变更检测还会触发第二遍,如果第二遍发现数据和第一遍不同,那就马上报错。直接告诉你不可以这样。
result : 10 vs 2
另外,从这篇文章可以看到更多关于变更检测的东西 zhuanlan.zhihu.com/p/60569895
比如子组件的ngOnInt中可以修改父组件,而在 AfterViewChecked 则不行。但这个报错似乎可以关闭,生产环境也不会报这个错。
其中还有中很好的图,说明了变更侦测的内容
这说明了子组件的变更侦测Run change detection的精确时间点,即在父组件的render渲染结束和父组件的AfterViewXXX钩子之间。所以,这意味着,父组件到了afterViewXXX这一步,子组件就必然已经全部做完侦测了。所以此时再改数据,很危险。而onint时,子组件的变更侦测Run change detection还未开始,所以真的是没关系,改就改,不怕。而为了说明顺序,可以自行尝试在一个child组件和一个parent组件的ngAfterViewChecked生命周期中都console.log('parent / child checked') 可以发现child checked先打印出来。这就好理解了——只有子组件标记完侦测,父组件才会被标记完侦测。
另外需要注意的是 ngOnint、ngdocheck和ngonchange钩子是如果有需要才会在变更检测周期中
使得angular报变更检测的错误,有几个简单的方式可以看看
方式一:在子组件的ngAfterViewChecked方法中,修改父组件的属性,报错
方式二:子组件使用@input接收了父组件的属性,无论这个是标量属性,还是对象属性,即使是在onInit方法中,修改父组件这个属性,会强制报错
总结:对比vue和react,可以发现,angular对单向数据流是多么的严格,不给有任何侥幸和幻想,引用里的属性,你偷偷改,不行(如果prop是对象属性的话,vue和react是可以改的,虽然都不建议这样做)。这里的例子其实只是在危险的边缘试探,而在实际项目中,无论任何框架,都应该避免在子组件中通过引用来直接修改父组件的值,而应该通过暴露方法去修改,因为你不想让你手上的项目出现一些奇奇怪怪的bug。
代码可以在这里看看demo
这段代码可以不触发变更检测
this.zone.runOutsideAngular(_=>{
setInterval(_=>{
this.time = Date.now();
},1000)
})