This article will take you through the change detection inAngular. We will start with a small example, and then gradually discuss the change detection in depth. I hope it will be helpful to everyone!
#Change detection in Angular is a mechanism used to synchronize the state of the application UI with the state of the data. When application logic changes component data, the values bound to DOM properties in the view also change. The change detector is responsible for updating the view to reflect the current data model. [Recommended related tutorials: "angular tutorial"]
It is easy to learn from the paper, but I know that I have to do it in detail. In order to make it easier for readers to understand, this article starts with a small example and then expands step by step. The example is as follows:
// app.component.ts import { Component } from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { title = 'aa'; handleClick() { this.title = 'bb'; }} // app.componnet.html{{title}}
The example is relatively simple, that is, a click event is bound to thediv
element. Clicking on the element will change the value of the variabletitle
. The interface The display will also update. How does the framework know when it needs to update a view, and how does it update the view? Let’s find out.
When we click on thediv
element, thehandleClick
function will be executed. So how is this function triggered in Angular applications? If you have read my previous article about the introduction ofzone.js
, you will know that the click event in the Angular application has been taken over byzone.js
. Based on this answer, it is obvious that the execution must be triggered byzone.js
at the beginning, but here we have to further analyze the direct calling relationship and expand it layer by layer. The code closest to thehandleClick
function call is the following code:
function wrapListener(listenerFn, ...) { return function wrapListenerIn_markDirtyAndPreventDefault(e) { let result = executeListenerWithErrorHandling(listenerFn, ...); } }
ThelistenerFn
function in the above code points tohandleClick
, but it Is the parameter ofwrapListener
function. In the example, the element is bound to a click event. The related template compilation product is probably as follows:
function AppComponent_Template(rf, ctx) { ...... i0["ɵɵlistener"]("click", function AppComponent_Template_div_click_0_listener() { return ctx.handleClick(); }) }
When loading the application for the first time, it will executerenderView
, thenexecuteTemplate
, and then trigger With the above template function, the click function of the element is passed all the way to thelistenerFn
parameter. At this point we understand that the trigger source of the click function iszone.js
, but the actual click function delivery is implemented by Angular. So how arezone.js
and Angular related? ?zone.js
will arrange a task for each asynchronous event. Based on the example in this article,invokeTask
is called by the following code:
function forkInnerZoneWithAngularBehavior(zone) { zone._inner = zone._inner.fork({ name: 'angular', properties: { 'isAngularZone': true }, onInvokeTask: (delegate, current, target, task, applyThis, applyArgs) => { try { onEnter(zone); return delegate.invokeTask(target, task, ...); } finally { onLeave(zone); } } }) }
See here Isn't it very familiar, because there are similar code snippets in the previous article introduced byzone.js
. TheforkInnerZoneWithAngularBehavior
function is called by the constructor of class NgZone. So far we have introduced NgZone, a protagonist of Angular change detection, which is a simple encapsulation ofzone.js
.
Now that we know how the click function in the example is executed, if the application data changes after the function is executed, how can the view be updated in time? We still go back to theforkInnerZoneWithAngularBehavior
function mentioned above. In thetry finally
statement block, theinvokeTask
function will eventually executeonLeave(zone )
function. Further analysis can see that theonLeave
function finally calls thecheckStable
function:
function checkStable(zone) { zone.onMicrotaskEmpty.emit(null); }
is subscribed accordingly in the classApplicationRef
constructor Got thisemit
event:
class ApplicationRef { /** @internal */ constructor() { this._zone.onMicrotaskEmpty.subscribe({ next: () => { this._zone.run(() => { this.tick(); }); } }); }
In the subscription-related callback function, doesthis.tick()
look familiar? If you have read my previous article about Angular life cycle functions, then you will definitely have the impression that it is the key call to trigger view updates. Although this function was mentioned in the life cycle introduction article, the focus of this article is change detection. Therefore, although the function is the same, the focus has changed slightly.this.tick
The relevant calling sequence is roughly like this:
this.tick() -> view.detectChanges() -> renderComponentOrTemplate() -> refreshView()
HererefreshView
is more important and is analyzed separately:
function refreshView(tView, lView, templateFn, context) { ...... if (templateFn !== null) { // 关键代码1 executeTemplate(tView, lView, templateFn, ...); } ...... if (components !== null) { // 关键代码2 refreshChildComponents(lView, components); } }
In this processrefreshView
The function will be called twice. The first time it enters the key code 2 branch, and then the following functions are called in sequence to re-enter therefreshView
function:
refreshChildComponents() -> refreshChildComponents() -> refreshComponent() -> refreshView()
The second time Entering therefreshView
function call is the key code 1 branch, that is, theexecuteTemplate
function is executed. And what this function ultimately executes is theAppComponent_Template
function in the template compilation product:
function AppComponent_Template(rf, ctx) { if (rf & 1) { // 条件分支1 i0["ɵɵelementStart"](0, "div", 0); i0["ɵɵlistener"]("click", function AppComponent_Template_div_click_0_listener() { return ctx.handleClick(); }); i0["ɵɵtext"](1); i0["ɵɵelementEnd"](); } if (rf & 2) { // 条件分支2 i0["ɵɵadvance"](1); i0["ɵɵtextInterpolate"](ctx.title); } }
If there are still readers who are not sure how the functions in the above template compilation product come from, it is recommended to read the previous about This article explains the principles of dependency injection and will not be repeated due to space limitations. At this time, theAppComponent_Template
function executes the code in conditional branch 2, and theɵɵadvance
function is to update the relevant index value to ensure that the correct element is found. The focus here is on theɵɵtextInterpolate
function, which ultimately calls the functionɵɵtextInterpolate1
:
function ɵɵtextInterpolate1(prefix, v0, suffix) { const lView = getLView(); // 关键代码1 const interpolated = interpolation1(lView, prefix, v0, suffix); if (interpolated !== NO_CHANGE) { // 关键代码2 textBindingInternal(lView, getSelectedIndex(), interpolated); } return ɵɵtextInterpolate1; }
值得指出的是,该函数名末尾是数字1,这是因为还有类似的ɵɵtextInterpolate2
、ɵɵtextInterpolate3
等等,Angular 内部根据插值表达式的数量调用不同的专用函数,本文示例中文本节点的插值表达式数量为1,因此实际调用的是ɵɵtextInterpolate1
函数。该函数主要做了两件事,关键代码1作用是比较插值表达式值有没有更新,关键代码2则是更新文本节点的值。先来看看关键代码1的函数interpolation1
,它最终调用的是:
function bindingUpdated(lView, bindingIndex, value) { const oldValue = lView[bindingIndex]; if (Object.is(oldValue, value)) { return false; } else { lView[bindingIndex] = value; return true; } }
变更检测前的文本节点值称之为oldValue
, 该值存储在lView
中,lView
我在之前的文章中也提到过,忘记了的读者可以去看看lView
的作用。bindingUpdated
首先会比较新值和旧值,比较的方法便是Object.is
。如果新值旧值没有变化,则返回false
。如果有变化,则更新lView
中存储的值,并返回true
。关键代码2的函数textBindingInternal
最终调用的是下述函数:
function updateTextNode(renderer, rNode, value) { ngDevMode && ngDevMode.rendererSetText++; isProceduralRenderer(renderer) ? renderer.setValue(rNode, value) : rNode.textContent = value; }
走完上述流程,我们点击div
元素时,界面显示内容便会由aa
变为bb
,即完成了从应用数据的变更到 UI 状态的同步更新,这便是 Angular 最基本的变更检测过程了。
因篇幅限制,本文所举示例比较简单,但 Angular 的变更检测还有很多没有讲到。比如,如果应用是由若干个组件组成的,父子组件间的变更检测如何进行,以及如何通过策略优化变更检测等等。如果有对这方面感兴趣的朋友,欢迎关注我的个人公众号【朱玉洁的博客】,后续将在那里分享更多前端知识。
更多编程相关知识,请访问:编程学习!!
The above is the detailed content of Let's talk about change detection in Angular through examples. For more information, please follow other related articles on the PHP Chinese website!