Let's talk about change detection in Angular through examples

青灯夜游
Release: 2022-02-08 10:38:36
forward
1497 people have browsed it

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!

Let's talk about change detection in Angular through examples

#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}}
Copy after login

The example is relatively simple, that is, a click event is bound to thedivelement. 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 thedivelement, thehandleClickfunction 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.jsat the beginning, but here we have to further analyze the direct calling relationship and expand it layer by layer. The code closest to thehandleClickfunction call is the following code:

function wrapListener(listenerFn, ...) { return function wrapListenerIn_markDirtyAndPreventDefault(e) { let result = executeListenerWithErrorHandling(listenerFn, ...); } }
Copy after login

ThelistenerFnfunction in the above code points tohandleClick, but it Is the parameter ofwrapListenerfunction. 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(); }) }
Copy after login

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 thelistenerFnparameter. 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.jsand Angular related? ?zone.jswill arrange a task for each asynchronous event. Based on the example in this article,invokeTaskis 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); } } }) }
Copy after login

See here Isn't it very familiar, because there are similar code snippets in the previous article introduced byzone.js. TheforkInnerZoneWithAngularBehaviorfunction 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 theforkInnerZoneWithAngularBehaviorfunction mentioned above. In thetry finallystatement block, theinvokeTaskfunction will eventually executeonLeave(zone )function. Further analysis can see that theonLeavefunction finally calls thecheckStablefunction:

function checkStable(zone) { zone.onMicrotaskEmpty.emit(null); }
Copy after login

is subscribed accordingly in the classApplicationRefconstructor Got thisemitevent:

class ApplicationRef { /** @internal */ constructor() { this._zone.onMicrotaskEmpty.subscribe({ next: () => { this._zone.run(() => { this.tick(); }); } }); }
Copy after login

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.tickThe relevant calling sequence is roughly like this:

this.tick() -> view.detectChanges() -> renderComponentOrTemplate() -> refreshView()
Copy after login

HererefreshViewis 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); } }
Copy after login

In this processrefreshViewThe 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 therefreshViewfunction:

refreshChildComponents() -> refreshChildComponents() -> refreshComponent() -> refreshView()
Copy after login

The second time Entering therefreshViewfunction call is the key code 1 branch, that is, theexecuteTemplatefunction is executed. And what this function ultimately executes is theAppComponent_Templatefunction 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); } }
Copy after login

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_Templatefunction executes the code in conditional branch 2, and theɵɵadvancefunction is to update the relevant index value to ensure that the correct element is found. The focus here is on theɵɵtextInterpolatefunction, 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; }
Copy after login

值得指出的是,该函数名末尾是数字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; } }
Copy after login

变更检测前的文本节点值称之为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; }
Copy after login

走完上述流程,我们点击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!

Related labels:
source:juejin.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!