91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Angular中的變更實例檢測分析

發布時間:2022-02-08 14:55:15 來源:億速云 閱讀:153 作者:iii 欄目:web開發

本篇內容介紹了“Angular中的變更實例檢測分析”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

Angular中的變更實例檢測分析

Angular 中的變更檢測是一種用來將應用程序 UI 的狀態與數據的狀態同步的機制。當應用邏輯更改組件數據時,綁定到視圖中 DOM 屬性上的值也要隨之更改。變更檢測器負責更新視圖以反映當前的數據模型。

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

// 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>

示例比較簡單,就是給div元素綁定了一個點擊事件,點擊該元素就會改變變量title的值,界面的顯示也會隨之更新。框架如何知道什么時候需要更新視圖,以及如何更新視圖的呢?我們來一探究竟。

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

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

上述代碼中listenerFn函數指向的便是handleClick,但它又是wrapListener函數的參數。示例中元素綁定點擊事件,相關模板編譯產物大概是這樣:

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

初次加載應用會依次執行renderView、然后執行executeTemplate,接著便觸發了上述的模板函數,就這樣元素的點擊函數便一路傳遞到了listenerFn參數。到這里我們了解了,點擊函數的觸發源頭是zone.js,真實的點擊函數傳遞卻是由 Angular 實現,那么zone.js和 Angular 是如何關聯的呢?zone.js會為每個異步事件安排一個任務,結合本文示例來說,invokeTask便是由下面代碼調用:

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

看到這里是不是就很熟悉了,因為在之前的zone.js介紹的文章里,便有類似的代碼片段。而forkInnerZoneWithAngularBehavior函數又是由類 NgZone 的構造函數調用。至此我們引出了 Angular 變更檢測的一個主角 NgZone,它是對zone.js的一個簡單封裝。

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

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

相應地在類ApplicationRef構造函數中訂閱了這個emit事件:

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

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

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

這里refreshView比較重要單獨拿出來分析一下:

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

這個過程中refreshView函數會被調用二次,第一次進入的是關鍵代碼2分支,然后依次調用如下函數重新進入refreshView函數:

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

第二次進入refreshView函數調用的便是關鍵代碼1分支了,即執行的是:executeTemplate函數。而該函數最終執行的是模板編譯產物中的AppComponent_Template函數:

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

如果還有讀者不清楚上述模板編譯產物中的函數是怎么來的,建議閱讀之前關于依賴注入原理講解的文章,因篇幅限制不再贅述。此時AppComponent_Template函數執行的是條件分支2里的代碼,??advance函數作用是更新相關的索引值,以保證找到正確的元素。這里重點講講??textInterpolate函數,它最終調用的是函數??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中的變更實例檢測分析”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

涿州市| 新疆| 夏河县| 涿鹿县| 青铜峡市| 阿拉尔市| 色达县| 紫阳县| 赫章县| 永定县| 景德镇市| 专栏| 洛宁县| 葵青区| 德兴市| 麻阳| 门头沟区| 美姑县| 乌兰县| 行唐县| 柳州市| 会理县| 基隆市| 子洲县| 苗栗县| 汶川县| 博野县| 塔河县| 海盐县| 太原市| 柳河县| 清水县| 丹江口市| 长岛县| 凤城市| 肃南| 铁岭县| 巴楚县| 南安市| 若羌县| 依兰县|