欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

深入淺出講解Angular變更檢測(cè)

 更新時(shí)間:2022年03月01日 14:58:12   作者:Zuckjet  
這篇文章主要給大家介紹了關(guān)于Angular變更檢測(cè)的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下

Angular 中的變更檢測(cè)是一種用來(lái)將應(yīng)用程序 UI 的狀態(tài)與數(shù)據(jù)的狀態(tài)同步的機(jī)制。當(dāng)應(yīng)用邏輯更改組件數(shù)據(jù)時(shí),綁定到視圖中 DOM 屬性上的值也要隨之更改。變更檢測(cè)器負(fù)責(zé)更新視圖以反映當(dāng)前的數(shù)據(jù)模型。閱讀本文之前,建議先查看我的前兩篇和變更檢測(cè)緊密相關(guān)的博文,即《 揭秘Angular 生命周期函數(shù)》 和 《 Angular 之 zone.js 介紹》。

紙上得來(lái)終覺淺,絕知此事要躬行。為了讓讀者朋友們更容易理解,本文先從一個(gè)小的示例入手,然后逐步展開。示例如下:

// 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
<div (click)="handleClick()">{{title}}</div>

示例比較簡(jiǎn)單,就是給div元素綁定了一個(gè)點(diǎn)擊事件,點(diǎn)擊該元素就會(huì)改變變量title的值,界面的顯示也會(huì)隨之更新??蚣苋绾沃朗裁磿r(shí)候需要更新視圖,以及如何更新視圖的呢?我們來(lái)一探究竟。

當(dāng)我們點(diǎn)擊div元素時(shí),handleClick函數(shù)會(huì)被執(zhí)行。那么在 Angular 應(yīng)用中該函數(shù)是如何被觸發(fā)執(zhí)行的呢?如果你看過(guò)我之前的關(guān)于zone.js介紹的文章就會(huì)知道,Angular 應(yīng)用中點(diǎn)擊事件已經(jīng)被zone.js接管。基于此答案便顯而易見,最開始肯定是被zone.js觸發(fā)執(zhí)行,但在這里我還們還要進(jìn)一步分析直接調(diào)用關(guān)系進(jìn)而層層展開。最靠近handleClick函數(shù)調(diào)用的是下面的代碼:

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

上述代碼中l(wèi)istenerFn函數(shù)指向的便是handleClick,但它又是wrapListener函數(shù)的參數(shù)。示例中元素綁定點(diǎn)擊事件,相關(guān)模板編譯產(chǎn)物大概是這樣:

function AppComponent_Template(rf, ctx) {
  ......
  i0["??listener"]("click", function AppComponent_Template_div_click_0_listener() {
    return ctx.handleClick();
  })
}

初次加載應(yīng)用會(huì)依次執(zhí)行renderView、然后執(zhí)行executeTemplate,接著便觸發(fā)了上述的模板函數(shù),就這樣元素的點(diǎn)擊函數(shù)便一路傳遞到了listenerFn參數(shù)。到這里我們了解了,點(diǎn)擊函數(shù)的觸發(fā)源頭是zone.js,真實(shí)的點(diǎn)擊函數(shù)傳遞卻是由 Angular 實(shí)現(xiàn),那么zone.js和 Angular 是如何關(guān)聯(lián)的呢?zone.js會(huì)為每個(gè)異步事件安排一個(gè)任務(wù),結(jié)合本文示例來(lái)說(shuō),invokeTask便是由下面代碼調(diào)用:

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);
      }
    }
  })
}

看到這里是不是就很熟悉了,因?yàn)樵谥暗膠one.js介紹的文章里,便有類似的代碼片段。而forkInnerZoneWithAngularBehavior函數(shù)又是由類 NgZone 的構(gòu)造函數(shù)調(diào)用。至此我們引出了 Angular 變更檢測(cè)的一個(gè)主角 NgZone,它是對(duì)zone.js的一個(gè)簡(jiǎn)單封裝。

現(xiàn)在我們知道示例中點(diǎn)擊函數(shù)是如何被執(zhí)行的,那么函數(shù)執(zhí)行了以后應(yīng)用數(shù)據(jù)有變化了,視圖又是如何及時(shí)更新的呢?我們還是回到上面提到的forkInnerZoneWithAngularBehavior函數(shù)中,try finally語(yǔ)句塊中,執(zhí)行了invokeTask函數(shù)最終還會(huì)執(zhí)行onLeave(zone)函數(shù)。再往下分析就能看到onLeave函數(shù)最終調(diào)用了checkStable函數(shù):

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

相應(yīng)地在類ApplicationRef構(gòu)造函數(shù)中訂閱了這個(gè)emit事件:

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

在訂閱相關(guān)回調(diào)函數(shù)中,this.tick()是不是很眼熟呢?如果你看了我之前的關(guān)于 Angular 生命周期函數(shù)的文章,那么你肯定還會(huì)有印象,它是觸發(fā)視圖更新的關(guān)鍵調(diào)用。雖然在那篇生命周期介紹的文章中有講過(guò)這個(gè)函數(shù),但本文的重點(diǎn)是變更檢測(cè)因此函數(shù)雖然相同但側(cè)重點(diǎn)略有變化。this.tick相關(guān)調(diào)用順序大概是這樣:

this.tick() -> 
view.detectChanges() -> 
renderComponentOrTemplate() ->
refreshView()

這里refreshView比較重要單獨(dú)拿出來(lái)分析一下:

function refreshView(tView, lView, templateFn, context) {
  ......
  if (templateFn !== null) {
    // 關(guān)鍵代碼1
    executeTemplate(tView, lView, templateFn, ...);
  }
  ......
  if (components !== null) {
    // 關(guān)鍵代碼2
    refreshChildComponents(lView, components);
  }
}

這個(gè)過(guò)程中refreshView函數(shù)會(huì)被調(diào)用二次,第一次進(jìn)入的是關(guān)鍵代碼2分支,然后依次調(diào)用如下函數(shù)重新進(jìn)入refreshView函數(shù):

refreshChildComponents() -> 
refreshChildComponents() ->
refreshComponent() ->
refreshView()

第二次進(jìn)入refreshView函數(shù)調(diào)用的便是關(guān)鍵代碼1分支了,即執(zhí)行的是:executeTemplate函數(shù)。而該函數(shù)最終執(zhí)行的是模板編譯產(chǎn)物中的AppComponent_Template函數(shù):

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);
  }

如果還有讀者不清楚上述模板編譯產(chǎn)物中的函數(shù)是怎么來(lái)的,建議閱讀之前關(guān)于依賴注入原理講解的文章,因篇幅限制不再贅述。此時(shí)AppComponent_Template函數(shù)執(zhí)行的是條件分支2里的代碼,??advance函數(shù)作用是更新相關(guān)的索引值,以保證找到正確的元素。這里重點(diǎn)講講??textInterpolate函數(shù),它最終調(diào)用的是函數(shù)??textInterpolate1:

function ??textInterpolate1(prefix, v0, suffix) {
    const lView = getLView();
    // 關(guān)鍵代碼1
    const interpolated = interpolation1(lView, prefix, v0, suffix);
    if (interpolated !== NO_CHANGE) {
        // 關(guān)鍵代碼2
        textBindingInternal(lView, getSelectedIndex(), interpolated);
    }
    return ??textInterpolate1;
}

值得指出的是,該函數(shù)名末尾是數(shù)字1,這是因?yàn)檫€有類似的??textInterpolate2、??textInterpolate3等等,Angular 內(nèi)部根據(jù)插值表達(dá)式的數(shù)量調(diào)用不同的專用函數(shù),本文示例中文本節(jié)點(diǎn)的插值表達(dá)式數(shù)量為1,因此實(shí)際調(diào)用的是??textInterpolate1函數(shù)。該函數(shù)主要做了兩件事,關(guān)鍵代碼1作用是比較插值表達(dá)式值有沒有更新,關(guān)鍵代碼2則是更新文本節(jié)點(diǎn)的值。先來(lái)看看關(guān)鍵代碼1的函數(shù)interpolation1,它最終調(diào)用的是:

function bindingUpdated(lView, bindingIndex, value) {
    const oldValue = lView[bindingIndex];
    if (Object.is(oldValue, value)) {
        return false;
    }
    else {
        lView[bindingIndex] = value;
        return true;
    }
}

變更檢測(cè)前的文本節(jié)點(diǎn)值稱之為oldValue, 該值存儲(chǔ)在lView中,lView我在之前的文章中也提到過(guò),忘記了的讀者可以去看看lView的作用。bindingUpdated首先會(huì)比較新值和舊值,比較的方法便是Object.is。如果新值舊值沒有變化,則返回false。如果有變化,則更新lView中存儲(chǔ)的值,并返回true。關(guān)鍵代碼2的函數(shù)textBindingInternal最終調(diào)用的是下述函數(shù):

function updateTextNode(renderer, rNode, value) {
    ngDevMode && ngDevMode.rendererSetText++;
    isProceduralRenderer(renderer) ? renderer.setValue(rNode, value) : rNode.textContent = value;
}

走完上述流程,我們點(diǎn)擊div元素時(shí),界面顯示內(nèi)容便會(huì)由aa變?yōu)閎b,即完成了從應(yīng)用數(shù)據(jù)的變更到 UI 狀態(tài)的同步更新,這便是 Angular 最基本的變更檢測(cè)過(guò)程了。

因篇幅限制,本文所舉示例比較簡(jiǎn)單,但 Angular 的變更檢測(cè)還有很多沒有講到。比如,如果應(yīng)用是由若干個(gè)組件組成的,父子組件間的變更檢測(cè)如何進(jìn)行,以及如何通過(guò)策略優(yōu)化變更檢測(cè)等等。

總結(jié)

到此這篇關(guān)于Angular變更檢測(cè)的文章就介紹到這了,更多相關(guān)Angular變更檢測(cè)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Angular2學(xué)習(xí)教程之TemplateRef和ViewContainerRef詳解

    Angular2學(xué)習(xí)教程之TemplateRef和ViewContainerRef詳解

    這篇文章主要給大家介紹了Angular2中TemplateRef和ViewContainerRef的相關(guān)資料,文中介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起看看吧。
    2017-05-05
  • Angular Universal服務(wù)器端渲染避免 window is not defined錯(cuò)誤消息

    Angular Universal服務(wù)器端渲染避免 window is not&

    這篇文章主要介紹了Angular Universal服務(wù)器端渲染避免 window is not defined錯(cuò)誤消息,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-07-07
  • Angular4 ElementRef的應(yīng)用

    Angular4 ElementRef的應(yīng)用

    本篇文章主要介紹了Angular4 ElementRef的應(yīng)用,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-02-02
  • 利用AngularJs實(shí)現(xiàn)京東首頁(yè)輪播圖效果

    利用AngularJs實(shí)現(xiàn)京東首頁(yè)輪播圖效果

    這篇文章給大家介紹了如何利用AngularJs實(shí)現(xiàn)京東首頁(yè)輪播圖的效果,本文通過(guò)示例代碼詳細(xì)介紹了實(shí)現(xiàn)過(guò)程,對(duì)大家學(xué)習(xí)AngularJS具有一定參考借鑒價(jià)值,有需要的朋友們可以參考借鑒。
    2016-09-09
  • 詳解angularjs結(jié)合pagination插件實(shí)現(xiàn)分頁(yè)功能

    詳解angularjs結(jié)合pagination插件實(shí)現(xiàn)分頁(yè)功能

    本篇文章主要介紹了詳解angularjs結(jié)合pagination插件實(shí)現(xiàn)分頁(yè)功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。
    2017-02-02
  • AngularJS解決ng界面長(zhǎng)表達(dá)式(ui-set)的方法分析

    AngularJS解決ng界面長(zhǎng)表達(dá)式(ui-set)的方法分析

    這篇文章主要介紹了AngularJS解決ng界面長(zhǎng)表達(dá)式(ui-set)的方法,通過(guò)具體問(wèn)題的分析并結(jié)合實(shí)例形式給出了AngularJS長(zhǎng)表達(dá)式的相關(guān)使用技巧,需要的朋友可以參考下
    2016-11-11
  • 整理AngularJS中的一些常用指令

    整理AngularJS中的一些常用指令

    這篇文章主要介紹了整理AngularJS中的一些常用指令,包括ng-app、ng-init、ng-model和ng-repeat這四個(gè)指令的講解,需要的朋友可以參考下
    2015-06-06
  • angularjs實(shí)現(xiàn)柱狀圖動(dòng)態(tài)加載的示例

    angularjs實(shí)現(xiàn)柱狀圖動(dòng)態(tài)加載的示例

    本篇文章主要介紹了angularjs實(shí)現(xiàn)柱狀圖動(dòng)態(tài)加載的示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-12-12
  • AngularJS監(jiān)聽ng-repeat渲染完成的兩種方法

    AngularJS監(jiān)聽ng-repeat渲染完成的兩種方法

    這篇文章主要介紹了AngularJS監(jiān)聽ng-repeat渲染完成的兩種方法,結(jié)合實(shí)例形式分析了AngularJS基于自定義指令及廣播事件實(shí)現(xiàn)監(jiān)聽功能的相關(guān)操作技巧,需要的朋友可以參考下
    2018-01-01
  • Angularjs實(shí)現(xiàn)mvvm式的選項(xiàng)卡示例代碼

    Angularjs實(shí)現(xiàn)mvvm式的選項(xiàng)卡示例代碼

    每位Web開發(fā)者應(yīng)該都知道,選項(xiàng)卡是現(xiàn)代web網(wǎng)頁(yè)中最常用的效果之一,所以本文重點(diǎn)是用angularjs這個(gè)非常火mvvm框架,實(shí)現(xiàn)選項(xiàng)卡效果。有需要的朋友們可以參考借鑒,下面來(lái)一起看看吧。
    2016-09-09

最新評(píng)論