成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

[譯] Angular 屬性綁定更新機(jī)制

tianhang / 3349人閱讀

摘要:本文主要介紹輸入輸出綁定方式,特別是當(dāng)父組件輸入綁定值變化時(shí),如何更新子組件輸入值。更新指令的屬性上文中已經(jīng)描述了函數(shù)是用來(lái)更新元素的屬性,而是用來(lái)更新子組件的輸入綁定屬性,并且變更檢測(cè)期間傳入的參數(shù)就是函數(shù)。

原文鏈接:The mechanics of property bindings update in Angular

所有現(xiàn)代前端框架都是用組件來(lái)合成 UI,這樣很自然就會(huì)產(chǎn)生父子組件層級(jí),這就需要框架提供父子組件通信的機(jī)制。同樣,Angular 也提供了兩種方式來(lái)實(shí)現(xiàn)父子組件通信:輸入輸出綁定共享服務(wù)。對(duì)于 stateless presentational components 我更喜歡輸入輸出綁定方式,然而對(duì)于 stateful container components 我使用共享服務(wù)方式。

本文主要介紹輸入輸出綁定方式,特別是當(dāng)父組件輸入綁定值變化時(shí),Angular 如何更新子組件輸入值。如果想了解 Angular 如何更新當(dāng)前組件 DOM,可以查看 譯 Angular DOM 更新機(jī)制,這篇文章也會(huì)有助于加深對(duì)本文的理解。由于我們將探索 Angular 如何更新 DOM 元素和組件的輸入綁定屬性,所以假定你知道 Angular 內(nèi)部是如何表現(xiàn)組件和指令的,如果你不是很了解并且很感興趣,可以查看 譯 為何 Angular 內(nèi)部沒有發(fā)現(xiàn)組件, 這篇文章主要講了 Angular 內(nèi)部如何使用指令形式來(lái)表示組件。而本文對(duì)于組件和指令兩個(gè)概念互換使用,因?yàn)?Angular 內(nèi)部就是把組件當(dāng)做指令。

模板綁定語(yǔ)法

你可能知道 Angular 提供了 屬性綁定語(yǔ)法 —— [],這個(gè)語(yǔ)法很通用,它可以用在子組件上,也可以用在原生 DOM 元素上。如果你想從父組件把數(shù)據(jù)傳給子組件 b-comp 或者原生 DOM 元素 span,你可以在父組件模板中這么寫:

import { Component } from "@angular/core";

@Component({
  moduleId: module.id,
  selector: "a-comp",
  template: `
      
      
  `
})
export class AComponent {
  AText = "some";
}

你不必為原生 DOM 元素做些額外的工作,但是對(duì)于子組件 b-comp 你需要申明輸入屬性 textContent

@Component({
    selector: "b-comp",
    template: "Comes from parent: {{textContent}}"
})
export class BComponent {
    @Input() textContent;
}

這樣當(dāng)父組件 AComponent.AText 屬性改變時(shí),Angular 會(huì)自動(dòng)更新子組件 BComponent.textContent 屬性,和原生元素 span.textContent 屬性。同時(shí),還會(huì)調(diào)用子組件 BComponent 的生命周期鉤子函數(shù) ngOnChanges(注:實(shí)際上還有 ngDoCheck,見下文)。

你可能好奇 Angular 是怎么知道 BComponentspan 支持 textContent 綁定的。這是因?yàn)?Angular 編譯器在解析模板時(shí),如果遇到簡(jiǎn)單 DOM 元素如 span,就去查找這個(gè)元素是否定義在 dom_element_schema_registry,從而知道它是 HTMLElement 子類,textContent 是其中的一個(gè)屬性(注:可以試試如果 span 綁定一個(gè) [abc]=AText 就報(bào)錯(cuò),沒法識(shí)別 abc 屬性);如果遇到了組件或指令,就去查看其裝飾器 @Component/@Directive 的元數(shù)據(jù) input 屬性里是否有該綁定屬性項(xiàng),如果沒有,編譯器同樣會(huì)拋出錯(cuò)誤:

Can’t bind to ‘textContent’ since it isn’t a known property of?…

這些知識(shí)都很好理解,現(xiàn)在讓我們進(jìn)一步看看其內(nèi)部發(fā)生了什么。

組件工廠

盡管在子組件 BComponentspan 元素綁定了輸入屬性,但是輸入綁定更新所需要的信息全部在父組件 AComponent 的組件工廠里。讓我們看下 AComponent 的組件工廠代碼:

function View_AComponent_0(_l) {
  return jit_viewDef1(0, [
     jit_elementDef_2(..., "b-comp", ...),
     jit_directiveDef_5(..., jit_BComponent6, [], {
         textContent: [0, "textContent"]
     }, ...),
     jit_elementDef_2(..., "span", [], [[8, "textContent", 0]], ...)
  ], function (_ck, _v) {
     var _co = _v.component;
     var currVal_0 = _co.AText;
     var currVal_1 = "d";
     _ck(_v, 1, 0, currVal_0, currVal_1);
  }, function (_ck, _v) {
     var _co = _v.component;
     var currVal_2 = _co.AText;
     _ck(_v, 2, 0, currVal_2);
  });
}

如果你讀了 譯 Angular DOM 更新機(jī)制譯 為何 Angular 內(nèi)部沒有發(fā)現(xiàn)組件,就會(huì)對(duì)上面代碼中的各個(gè)視圖節(jié)點(diǎn)比較熟悉了。前兩個(gè)節(jié)點(diǎn)中,jit_elementDef_2 是元素節(jié)點(diǎn),jit_directiveDef_5 是指令節(jié)點(diǎn),這兩個(gè)組成了子組件 BComponent;第三個(gè)節(jié)點(diǎn) jit_elementDef_2 也是元素節(jié)點(diǎn),組成了 span 元素。

節(jié)點(diǎn)綁定

相同類型的節(jié)點(diǎn)使用相同的節(jié)點(diǎn)定義函數(shù),但區(qū)別是接收的參數(shù)不同,比如 jit_directiveDef_5 節(jié)點(diǎn)定義函數(shù)參數(shù)如下:

jit_directiveDef_5(..., jit_BComponent6, [], {
    textContent: [0, "textContent"]
}, ...),

其中,參數(shù) {textContent: [0, "textContent"]} 叫做 props,這點(diǎn)可以查看 directiveDef 函數(shù)的參數(shù)列表:

directiveDef(..., props?: {[name: string]: [number, string]}, ...)

props 參數(shù)是一個(gè)對(duì)象,每一個(gè)鍵為綁定屬性名,對(duì)應(yīng)的值為綁定索引和綁定屬性名組成的數(shù)組,比如本例中只有一個(gè)綁定,textContent 對(duì)應(yīng)的值為:

{textContent: [0, "textContent"]}

如果指令有多個(gè)綁定,比如:

props 參數(shù)值也包含兩個(gè)屬性:

jit_directiveDef5(49152, null, 0, jit_BComponent6, [], {
    textContent: [0, "textContent"],
    otherProp: [1, "otherProp"]
}, null),

Angular 會(huì)使用這些值來(lái)生成當(dāng)前指令節(jié)點(diǎn)的 binding,從而生成當(dāng)前視圖的指令節(jié)點(diǎn)。在變更檢測(cè)時(shí),每一個(gè) binding 決定 Angular 使用哪種操作來(lái)更新節(jié)點(diǎn)和提供上下文信息,綁定類型是通過 BindingFlags 設(shè)置的(注:每一個(gè)綁定定義是 BindingDef,它的屬性 flags: BindingFlags 決定 Angular 該采取什么操作,比如 Class 型綁定和 Style 型綁定都會(huì)調(diào)用對(duì)應(yīng)的操作函數(shù),見下文)。比如,如果是屬性綁定,編譯器會(huì)設(shè)置綁定標(biāo)志位為:

export const enum BindingFlags {
    TypeProperty = 1 << 3,
注:上文說(shuō)完了指令定義函數(shù)的參數(shù),下面說(shuō)說(shuō)元素定義函數(shù)的參數(shù)。

本例中,因?yàn)?span 元素有屬性綁定,編譯器會(huì)設(shè)置綁定參數(shù)為 [[8, "textContent", 0]]

jit_elementDef2(..., "span", [], [[8, "textContent", 0]], ...)

不同于指令節(jié)點(diǎn),對(duì)元素節(jié)點(diǎn)來(lái)說(shuō),綁定參數(shù)結(jié)構(gòu)是個(gè)二維數(shù)組,因?yàn)?span 元素只有一個(gè)綁定,所以它僅僅只有一個(gè)子數(shù)組。數(shù)組 [8, "textContent", 0] 中第一個(gè)參數(shù)也同樣是綁定標(biāo)志位 BindingFlags,決定 Angular 應(yīng)該采取什么類型操作(注:[8, "textContent", 0] 中的 8 表示為 property 型綁定):

export const enum BindingFlags {
    TypeProperty = 1 << 3, // 8

其他類型標(biāo)志位已經(jīng)在文章 譯 Angular DOM 更新機(jī)制 有所解釋:

TypeElementAttribute = 1 << 0,
TypeElementClass = 1 << 1,
TypeElementStyle = 1 << 2,

編譯器不會(huì)為指令定義提供綁定標(biāo)志位,因?yàn)橹噶畹慕壎愋鸵仓荒苁?BindingFlags.TypeProperty。

注:節(jié)點(diǎn)綁定 這一節(jié)主要講的是對(duì)于元素節(jié)點(diǎn)來(lái)說(shuō),每一個(gè)節(jié)點(diǎn)的 binding 類型是由 BindingFlags 決定的;對(duì)于指令節(jié)點(diǎn)來(lái)說(shuō),每一個(gè)節(jié)點(diǎn)的 binding 類型只能是 BindingFlags.TypeProperty。
updateRenderer 和 updateDirectives

組件工廠代碼里,編譯器還為我們生成了兩個(gè)函數(shù):

function (_ck, _v) {
    var _co = _v.component;
    var currVal_0 = _co.AText;
    var currVal_1 = _co.AProp;
    _ck(_v, 1, 0, currVal_0, currVal_1);
},
function (_ck, _v) {
    var _co = _v.component;
    var currVal_2 = _co.AText;
    _ck(_v, 2, 0, currVal_2);
}

如果你讀了 譯 Angular DOM 更新機(jī)制,應(yīng)該對(duì)第二個(gè)函數(shù)即 updateRenderer 有所熟悉。第一個(gè)函數(shù)叫做 updateDirectives。這兩個(gè)函數(shù)都是 ViewUpdateFn 類型接口,兩者都是視圖定義的屬性:

interface ViewDefinition {
  flags: ViewFlags;
  updateDirectives: ViewUpdateFn;
  updateRenderer: ViewUpdateFn;

有趣的是這兩個(gè)函數(shù)的函數(shù)體基本相同,參數(shù)都是 _ck_v,并且兩個(gè)函數(shù)的對(duì)應(yīng)參數(shù)都指向同一個(gè)對(duì)象,所以為何需要兩個(gè)函數(shù)?

因?yàn)樵谧兏鼨z測(cè)期間,這是不同階段的兩個(gè)不同行為:

更新子組件的輸入綁定屬性

更新當(dāng)前組件的 DOM 元素

這兩個(gè)操作是在變更檢測(cè)的不同階段執(zhí)行,所以 Angular 需要兩個(gè)獨(dú)立的函數(shù)分別在對(duì)應(yīng)的階段調(diào)用:

updateDirectives——變更檢測(cè)的開始階段被調(diào)用,來(lái)更新子組件的輸入綁定屬性

updateRenderer——變更檢測(cè)的中間階段被調(diào)用,來(lái)更新當(dāng)前組件的 DOM 元素

這兩個(gè)函數(shù)都會(huì)在 Angular 每次的變更檢測(cè)時(shí) 被調(diào)用,并且函數(shù)參數(shù)也是在這時(shí)被傳入的。讓我們看看函數(shù)內(nèi)部做了哪些工作。

_ck 就是 check 的縮寫,其實(shí)就是函數(shù) prodCheckAndUpdateNode,另一個(gè)參數(shù)就是 組件視圖數(shù)據(jù)。函數(shù)的主要功能就是從組件對(duì)象里拿到綁定屬性的當(dāng)前值,然后和視圖數(shù)據(jù)對(duì)象、視圖節(jié)點(diǎn)索引等一起傳入 prodCheckAndUpdateNode 函數(shù)。其中,因?yàn)?Angular 會(huì)更新每一個(gè)視圖的 DOM,所以需要傳入當(dāng)前視圖的索引。如果我們有兩個(gè) span 和兩個(gè)組件:




編譯器生成的 updateRenderer 函數(shù)和 updateDirectives 函數(shù)如下:

function(_ck, _v) {
    var _co = _v.component;
    var currVal_0 = _co.AText;
    
    // update first component
    _ck(_v, 1, 0, currVal_0);
    var currVal_1 = _co.AText;
    
    // update second component
    _ck(_v, 3, 0, currVal_1);
}, 
function(_ck, _v) {
    var _co = _v.component;
    var currVal_2 = _co.AText;
    
    // update first span
    _ck(_v, 4, 0, currVal_2);
    var currVal_3 = _co.AText;

    // update second span
    _ck(_v, 5, 0, currVal_3);
}

沒有什么更復(fù)雜的東西,這兩個(gè)函數(shù)還不是重點(diǎn),重點(diǎn)是 _ck 函數(shù),接著往下看。

更新元素的屬性

從上文我們知道,編譯器生成的 updateRenderer 函數(shù)會(huì)在每一次變更檢測(cè)被調(diào)用,用來(lái)更新 DOM 元素的屬性,并且其參數(shù) _ck 就是函數(shù) prodCheckAndUpdateNode。對(duì)于 DOM 元素的更新,該函數(shù)經(jīng)過一系列的函數(shù)調(diào)用后,最終會(huì)調(diào)用函數(shù) checkAndUpdateElementValue,這個(gè)函數(shù)會(huì)檢查綁定標(biāo)志位是 [attr.name, class.name, style.some] 其中的哪一個(gè),又或者是屬性綁定(注:可查看源碼這段 L233-L250):

case BindingFlags.TypeElementAttribute -> setElementAttribute
case BindingFlags.TypeElementClass     -> setElementClass
case BindingFlags.TypeElementStyle     -> setElementStyle
case BindingFlags.TypeProperty         -> setElementProperty;

上面代碼就是剛剛說(shuō)的幾個(gè)綁定類型,當(dāng)綁定標(biāo)志位是 BindingFlags.TypeProperty,會(huì)調(diào)用函數(shù) setElementProperty,該函數(shù)內(nèi)部也是通過調(diào)用 DOM Renderer 的 setProperty 方法來(lái)更新 DOM。

注:setElementProperty 函數(shù)里這行代碼 view.renderer.setProperty(renderNode,name, renderValue);,renderer 就是 Renderer2 interface,它僅僅是一個(gè)接口,在瀏覽器平臺(tái)下,它的實(shí)現(xiàn)就是 DefaultDomRenderer2。
更新指令的屬性

上文中已經(jīng)描述了 updateRenderer 函數(shù)是用來(lái)更新元素的屬性,而 updateDirective 是用來(lái)更新子組件的輸入綁定屬性,并且變更檢測(cè)期間傳入的參數(shù) _ck 就是函數(shù) prodCheckAndUpdateNode。只是進(jìn)過一系列函數(shù)調(diào)用后,最終調(diào)用的函數(shù)卻是checkAndUpdateDirectiveInline,這是因?yàn)檫@次節(jié)點(diǎn)的標(biāo)志位是 NodeFlags.TypeDirective(注:可查看源碼 L428-L429),checkAndUpdateDirectiveInline 函數(shù)主要功能如下:

從當(dāng)前視圖節(jié)點(diǎn)里獲取組件/指令對(duì)象(注:查看 L156

檢查組件/指令對(duì)象的綁定屬性值是否發(fā)生改變(注:查看 L160-L199

如果屬性發(fā)生改變:

a. 如果變更策略設(shè)置為 OnPush,設(shè)置視圖狀態(tài)為 checksEnabled(注:查看 L438

b. 更新子組件的綁定屬性值(注:查看 L446

c. 準(zhǔn)備 SimpleChange 數(shù)據(jù)和更新視圖的 oldValues 屬性,新值替換舊值(注:查看 L451-L454

d. 調(diào)用生命周期鉤子 ngOnChanges(注:查看 L201

如果該視圖是首次執(zhí)行變更檢測(cè),則調(diào)用生命周期鉤子 ngOnInit(注:查看 L205

調(diào)用生命周期鉤子 ngDoCheck(注:查看 L233

當(dāng)然,只有在生命周期鉤子在組件內(nèi)定義了才被調(diào)用,Angular 使用 NodeDef 節(jié)點(diǎn)標(biāo)志位來(lái)判斷是否有生命周期鉤子,如果查看源碼你會(huì)發(fā)現(xiàn)類似如下代碼(注:查看 L203-L207):

if (... && (def.flags & NodeFlags.OnInit)) {
  directive.ngOnInit();
}
if (def.flags & NodeFlags.DoCheck) {
  directive.ngDoCheck();
}

和更新元素節(jié)點(diǎn)一樣,更新指令時(shí)也同樣把上一次的值存儲(chǔ)在視圖數(shù)據(jù)的屬性 oldValues 里(注:即上面的 3.c 步驟)。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/107751.html

相關(guān)文章

  • [] 你真的知道 Angular 單向數(shù)據(jù)流嗎

    摘要:所以,單向數(shù)據(jù)流的意思是指在變更檢測(cè)期間屬性綁定變更的架構(gòu)。相反,輸出綁定過程并沒有在變更檢測(cè)期間內(nèi)運(yùn)行,所以它沒有把單向數(shù)據(jù)流轉(zhuǎn)變?yōu)殡p向數(shù)據(jù)流。說(shuō)的單向數(shù)據(jù)流說(shuō)的是服務(wù)層,而不是視圖層嗷。 原文鏈接: Do you really know what unidirectional data flow means in?Angular 關(guān)于單向數(shù)據(jù)流,還可以參考這篇文章,且文中還有 y...

    fox_soyoung 評(píng)論0 收藏0
  • [] Angular DOM 更新機(jī)制

    摘要:注更新元素節(jié)點(diǎn)和文本節(jié)點(diǎn)都提到了渲染器,這也是一個(gè)重要的概念。每一個(gè)視圖對(duì)象都有一個(gè)屬性,即是的引用,也就是組件渲染器,的實(shí)際更新操作由它完成。 原文鏈接:The mechanics of DOM updates in Angular showImg(https://segmentfault.com/img/remote/1460000014687960?w=419&h=268); ...

    xumenger 評(píng)論0 收藏0
  • [] 關(guān)于 `ExpressionChangedAfterItHasBeenCheckedErro

    摘要:本文將解釋引起這個(gè)錯(cuò)誤的內(nèi)在原因,檢測(cè)機(jī)制的內(nèi)部原理,提供導(dǎo)致這個(gè)錯(cuò)誤的共同行為,并給出修復(fù)這個(gè)錯(cuò)誤的解決方案。這一次過程稱為。這個(gè)程序設(shè)計(jì)為子組件拋出一個(gè)事件,而父組件監(jiān)聽這個(gè)事件,而這個(gè)事件會(huì)引起父組件屬性值發(fā)生改變。 原文鏈接:Everything you need to know about the ExpressionChangedAfterItHasBeenCheckedE...

    andong777 評(píng)論0 收藏0
  • [] $digest 在 Angular 中重生

    摘要:但如果一個(gè)組件在生命周期鉤子里改變父組件屬性,卻是可以的,因?yàn)檫@個(gè)鉤子函數(shù)是在更新父組件屬性變化之前調(diào)用的注即第步,在第步之前調(diào)用。 原文鏈接:Angular.js’ $digest is reborn in the newer version of Angular showImg(https://segmentfault.com/img/remote/146000001468785...

    incredible 評(píng)論0 收藏0
  • [] 如何對(duì) Angular Controller 進(jìn)行單元測(cè)試

    摘要:原文地址上面一篇文章簡(jiǎn)單介紹了如何使用進(jìn)行的單元測(cè)試我們用了一段簡(jiǎn)單的代碼進(jìn)行計(jì)算的測(cè)試。添加測(cè)試接下來(lái)終于到了我們的主題,添加一些單元測(cè)試給我們忽略代碼中部分,主要集中在的代碼中。 原文地址:http://www.bradoncode.com/blog/2015/05/17/angularjs-testing-controller/@Bradley Braithwaite show...

    aboutU 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<