引言

本文分享一些讲解Angular Change Detection的文章,并指出其中有意思的内容,以及自己的一些总结和引申。

Angular Change Detection Explained by thoughtram

  • change detection的基本任务:用进程内的状态(Component中的数据)来更新view(DOM)的显示。
  • Angular Change Detection发生的时机:基本上所有的异步事件发生(并且回调函数已经执行完毕)以后,都需要触发change detection(因为此时进程的状态可能已经发生改变):

    • Events - click, submit, …
    • XHR - Fetching data from a remote server
    • Timers - setTimeout(), setInterval()
  • 单向数据流:沿着组件树进行变化检测,检查完父组件以后再检查子组件,在检查父组件的时候可能会更新子组件中的绑定,但是在检查子组件的时候(此时父组件已经检查完毕)不会更新父组件的数据。也就是说,在变化检测的过程中,数据可以从父组件流进子组件,但不会从子组件流进父组件。这是Angular与AngularJS之间的重大区别。Angular的这个特点能够保证:只需要执行一次Change Detection,就能使得view与组件中的数据一致("change detection gets stable after a single pass")。而在AngularJS中,由于在检查一个组件的时候可能会改动另一个组件中的数据,因此需要多次检查,直到数据“稳定”下来。
    从别的文章偷来一张图(很多文章有这张图,已经不知道来源):
  • 专用change detector:Angular的变化检测出了名的快,这是其中一个很重要的原因。每个组件都有一个自己的change detector(Angular compiler为每个component编译生成专门检测它的view的代码),这使得每个change detector的检测代码非常地简单高效(VM friendly)。而在AngularJS中,所有component输入同一个算法来进行变化检测,虽然代码的一般性(generic)、通用性很强,但是这种代码执行的效率相对较慢,因为动态性(dynamic)强意味着执行引擎难以做假设、做实时优化。

多态(polymorphic):通用性往往意味着多态的存在。多态的含义是,一段代码,多次执行,但是每次执行所操作的对象都不是同一个对象,甚至这些对象的“形状”(shape)相差巨大(属性名不一样,或者属性被添加的顺序不一样)。这种代码难以被优化。大量使用单态(monomorphic)是Angular速度爆炸的重要原因!除了Change Detection方面,Angular在其他地方也使用了单态的优化思想。
How JavaScript works: inside the V8 engine + 5 tips on how to write optimized code 和 v8 Design Elements 讲解了v8是如何优化代码的。

  • 这篇文章的后面部分讲的是如何通过changeDetection: ChangeDetectionStrategy.OnPush来对变化检测树进行“剪枝”,进一步降低变化检测的时间开销。使用到Immutable Objects和ChangeDetectorRef.markForCheck。

Change Detection in Angular

作者Victor Savkin以前是Angular核心团队的成员,现在似乎自己创建了一个Angular的企业咨询公司。

变化检测是有向的

“Change detectors propagate bindings from the root to leaves in the depth first order.”
“传播”(propagate)这个词比较生动地体现了变化检测的特点。Angular程序员通过绑定来定义哪些数据可以传播到view或者子组件中。数据从父组件传播到子组件,反之不行。
并且,对组件树的变化检测是深度优先的。

数据传播到view的绑定:

<span>todo: {{todo.text}}</span>

数据传播到子组件的绑定:

<todo-cmp [model]="myTodo"></todo-cmp>

变化检测将用本组件的myTodo属性来更新子组件的@Input() model属性。

变化检测默认检测所有组件的原因

"Angular has to be conservative and run all the checks every single time because the JavaScript language does not give us object mutation guarantees."
原因在Angular Change Detection Explained by thoughtram也介绍过了。即使@Input()对象的引用没有变,其中的属性可能已经发生变化(JavaScript对象的动态性),变化检测需要将这种变化也反映在view和子组件上。

OnPush

接下来就是介绍changeDetection:ChangeDetectionStrategy.OnPush了。这里我不再做过多解释。引用作者在另一篇文章的话:
The framework will check OnPush components only when their inputs change or components’ templates emit events.

值得注意的是,作者指出OnPush并不是所有属性都必须是immutable的,只要@Input是immutable的,并且其他mutable的属性能保证仅在【@Input更新】或【组件的template中有事件触发】时才更新:
It is worth noting that a component can still have private mutable state as long as it changes only due to inputs being updated or an event being fired from within the component’s template. The only thing the OnPush strategy disallows is depending on shared mutable state. Read more about it here.

Two Phases of Angular Applications

文章开头概括得非常精辟:

Angular 2 separates updating the application model(可以理解为更新Component的属性值) and reflecting the state of the model in the view into two distinct phases. The developer is responsible for updating the application model. Angular, by means of change detection, is responsible for reflecting the state of the model in the view. The framework does it automatically on every VM turn.

Event bindings, which can be added using the () syntax, can be used to capture a browser event execute some function on a component. So they trigger the first phase.

Property bindings, which can be added using the [] syntax, should be used only for reflecting the state of the model in the view.

Angular应用的变化(Component属性的变化和DOM的变化)分成2个阶段(按发生先后顺序排序):

  1. 异步事件发生(比如用户点击或者AJAX请求完成),注册的回调函数被执行(比如<button (click)="handler($event)"></button>),这些回调函数可以改变Component中的属性。
  2. 回调函数执行完毕以后(本轮事件循环的末尾),Angular此时才会执行变化检测。变化检测根据我们定义好的数据绑定([model]='prop'),将Component中的新数据“推”到view中(包括更新DOM和更新子组件的@Input属性)。对组件树执行变化检测的顺序:从根到叶,深度优先。

第一个阶段结束以后才会进入第二个阶段。
我们只能控制第一阶段,因为回调函数是我们定义的,我们可以随意在其中更新父组件属性、子组件属性、本组件属性……Angular完全不会插手。
第二个阶段由Angular来完成。这阶段发生的就是变化检测(change detection)。在变化检测的过程中,这些变化会在组件树上传播:从父组件到子组件单向传播,以及从组件传播到它的DOM。哪些数据传播给子组件、更新子组件的那些属性、更新DOM的哪些属性……这些是由数据绑定来决定的(因此从某种意义上来说,我们也能稍微控制第二个阶段,毕竟数据绑定也是我们来写的)。

可见,事件绑定和数据绑定的语法虽然看起来很相似((event)=[bindProp]=),但是它们是在不同的阶段产生作用的。

这样划分阶段的意义

在AngularJS时代,脏检查的执行过程中不仅会更新DOM,而且可能会更新其他application model,但application model被更新以后,可能有别的DOM又因此需要被更新(因为DOM展示的内容依赖于application model),因此AngularJS不得不做多次脏检查,直到application model不再更新。这会影响应用的性能,而且不利于Debug(因为你不知道application model是什么时候被谁更新的,是事件回调更新的?还是在某次脏检查执行过程中被更新的?)。
再看一次这张图:

正因为这个原因,Angular才如此划分阶段。在Angular中,application model的更新只能有2个原因:

  • 在第一个阶段,被事件回调更新。
  • 在第二个阶段,组件的@Input属性被数据绑定更新(父组件将新数据"推"进本组件)。

开发者不需要像AngularJS时代那样考虑脏检查的杂乱更新过程,现在只要稍微分析一下就能知道数据是如何流动的。这让应用的逻辑更加清晰,更容易调试和重构。

view的更新也更加简单高效了,因为只需要执行一轮变化检测(一轮变化检测执行完以后数据就会稳定下来,不再变化)。并且数据的流动方向也非常清晰,始终是从父组件流入子组件(单向数据流)。

另外,Angular开发者也不需要像AngularJS开发者那样害怕数据环路了(看本小节第一段的例子),因为这不再会发生。在Angular中,在第一阶段,我们可以更新任何父组件、子组件的数据,在第二阶段也不会造成数据环路(因为在第二阶段,数据的传播是单向的)。

更多相关文章

弄懂了这几篇文章以后,很多相关文章的内容其实大同小异。我整理了一张change detection文章列表,里面的文章都是讲得比较好的,不过只有一篇是中文。。。如果感觉还不是太懂的话可以在里面多找几篇阅读。
其中angularindepth的文章一般会深入到源码,想要更深入理解的话可以阅读其中文章,乃至自己研究Angular源码。

深入理解 Angular 变化检测(change detection)相关推荐

  1. Angular变化检测机制:改善的脏检查

    本文链接:https://blog.csdn.net/fen747042796/article/details/75152336 前端展示的页面是由视图和数据共同构成的,视图模板定义了页面的框架,而数 ...

  2. 论文阅读:SCDNET: A novel convolutional network for semantic change detection in high resolution optical

    SCDNET:一种用于高分辨率光学遥感图像变化检测网络 变化检测 新的网络架构 SCD的局限性 提出的SCDNET架构 网络结构 多尺度空洞卷积模块 注意力机制 LOSS函数 实验 结论 变化检测 变 ...

  3. Angular Change Detection 的学习笔记

    Angular 变化检测机制比 AngularJs 中的等效机制更透明且更易于推理.但是在某些情况下(例如在进行性能优化时),我们确实需要知道幕后发生了什么.因此,让我们通过以下主题深入了解变更检测: ...

  4. [译] 关于Angular的变更检测(Change Detection)你需要知道这些

    原文地址:Everything you need to know about change detection in Angular 如果你像我一样,想对Angular的变更检测机制有一个深入的理解, ...

  5. 2022年 change detection遥感图像变化检测 论文附代码

    1.Remote Sensing Change Detection using Denoising Diffusion Probabilistic Models 论文 代码 22-6 动机: 可用于训 ...

  6. 变化检测综述 Deep learning for change detection in remote sensing images: comprehensive review and me

    Deep learning for change detection in remote sensing images: comprehensive review and meta-analysis ...

  7. Angular 默认的Change detection策略及缺陷

    默认策略下,用户事件,计时器,XHR,promise等事件触发,所有的组件都会执行变更检测. 看一个实际例子: import { Component, OnInit, Input } from '@a ...

  8. Angular 变化检测详解

    作者:PingCode 产品研发部研发二组负责人王凯 前言 变化检测是前端框架中很有趣的一部分内容,各个前端的框架也都有自己的一套方案,一般情况下我们不太需要过多的了解变化检测,因为框架已经帮我们完成 ...

  9. SNUNet-CD: A Densely Connected Siamese Network for Change Detection of VHR Images论文介绍

    SNUNet-CD: A Densely Connected Siamese Network for Change Detection of VHR Images 该论文受DenseNet and N ...

最新文章

  1. ASP.Net中利用CSS实现多界面两法
  2. 影像组学视频学习笔记(5)-特征筛选之方差选择法、Li‘s have a solution and plan.
  3. 使用 git 下载linux 源码
  4. 华为ap配置_Win10频发蓝屏,深度Deepin系统,调试华为AC和AP
  5. Qt Creator管理项目层次结构
  6. python selenium webdriver方法封装(find_element_by)
  7. 小红伞和NOD32基于源码的免杀经验总结
  8. Java线程面试的前50个问题,面向初学者和经验丰富的程序员
  9. mybatis里oracle与MySQL的insert_update
  10. 辽宁412想学计算机科学技术,辽宁理工学院计算机科学与技术专业2016年在辽宁理科高考录取最低分数线...
  11. 面试精讲之面试考点及大厂真题 - 分布式专栏 04 谈谈你对分布式的理解,为什么引入分布式?
  12. 【bzoj4868】[Shoi2017]期末考试 前缀和+暴力
  13. 【Flink】Flink 1.10之改进的TaskManager内存模型与配置
  14. 第五十六题(最长公共子串)
  15. 【Spark】SparkSQL练习--出租车数据清洗
  16. Linux部署django项目最全,linux部署django项目流程(全)
  17. 幻云蜜网筑迷阵 春秋靶场信安大赛从攻击者角度看安全
  18. 6Attentive Convolutional Neural Network based Speech Emotion Recognition: A Study on the Impact of
  19. 脑电数据的实验范式及EEGLAB分析预处理
  20. 什么是内存泄露,如何避免内存泄露 C++

热门文章

  1. Android开发万能Utils(工具大全)
  2. VirtualAlloc和VirtualCopy----VirtualFree
  3. C/C++结构体struct 与结构体数组和枚举型enum的结合使用
  4. Java调用js方法
  5. ProGuard中keep到底有什么作用
  6. Hibernate关于父类子类的映射
  7. 团队作业5——测试与发布(Alpha版本)
  8. linux wget 进度条多行的解决办法
  9. 面试小记---外部脚本必须包含 script 标签吗?
  10. bzoj2005: [Noi2010]能量采集