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

資訊專欄INFORMATION COLUMN

[譯] 為何 Angular 內(nèi)部沒有發(fā)現(xiàn)組件

LiveVideoStack / 3548人閱讀

摘要:本質(zhì)上,本文主要解釋內(nèi)部是如何定義組件和指令的,并引入新的視圖節(jié)點定義指令定義。大多數(shù)指令使用屬性選擇器,但是有一些也選擇元素選擇器。實際上,表單指令就是使用元素選擇器來把特定行為附著在元素上。但是由于編譯器會為每一個

原文鏈接:Here is why you will not find components inside Angular

Component is just a directive with a template? Or is it?

從我開始使用 Angular 開始,就被組件和指令間區(qū)別的問題所困惑,尤其對那些從 Angular.js 世界來的人,因為 Angular.js 里只有指令,盡管我們也經(jīng)常把它當(dāng)做組件來使用。如果你在網(wǎng)上搜這個問題解釋,很多都會這么解釋(注:為清晰理解,不翻譯):

Components are just directives with a content defined in a template…

Angular components are a subset of directives. Unlike directives, components always have…

Components are high-order directives with templates and serve as…

這些說法貌似都對,我在查看由 Angular 編譯器編譯組件生成的視圖工廠源碼里,的確沒發(fā)現(xiàn)組件定義,你如果查看也只會發(fā)現(xiàn) 指令。

注:使用 Angular-CLI ng new 一個新項目,執(zhí)行 ng serve 運行程序后,就可在 Chrome Dev Tools 的 Source Tab 的 ng:// 域下查看到編譯組件后生成的 **.ngfactory.js 文件,該文件代碼即上面說的視圖工廠源碼。

但是我在網(wǎng)上沒有找到 原因解釋,因為想要知道原因就必須對 Angular 內(nèi)部工作原理比較熟悉,如果上面的問題也困讓了你很長一段時間,那本文正適合你。讓我們一起探索其中的奧秘并做好準(zhǔn)備吧。

本質(zhì)上,本文主要解釋 Angular 內(nèi)部是如何定義組件和指令的,并引入新的視圖節(jié)點定義——指令定義。

注:視圖節(jié)點還包括元素節(jié)點和文本節(jié)點,有興趣可查看 譯 Angular DOM 更新機制
視圖

如果你讀過我之前寫的文章,尤其是 譯 Angular DOM 更新機制,你可能會明白 Angular 程序內(nèi)部是一棵視圖樹,每一個視圖都是由視圖工廠生成的,并且每個視圖包含具有特定功能的不同視圖節(jié)點。在剛剛提到的文章中(那篇文章對了解本文很重要嗷),我介紹過兩個最簡單的節(jié)點類型——元素節(jié)點定義和文本節(jié)點定義。元素節(jié)點定義是用來創(chuàng)建所有 DOM 元素節(jié)點,而文本節(jié)點定義是用來創(chuàng)建所有 DOM 文本節(jié)點 。

所以如果你寫了如下的一個模板:

Hello {{name}}

Angular Compiler 將會編譯這個模板,并生成兩個元素節(jié)點,即 divh1 DOM 元素,和一個文本節(jié)點,即 Hello {{name}} DOM 文本。這些都是很重要的節(jié)點,因為沒有它們,你在屏幕上看不到任何東西。但是組件合成模式告訴我們可以嵌套組件,所以必然另一種視圖節(jié)點來嵌入組件。為了搞清楚這些特殊節(jié)點是什么,首先需要了解組件是由什么組成的。本質(zhì)上,組件本質(zhì)上是具有特定行為的 DOM 元素,而這些行為是在組件類里實現(xiàn)的。首先看下 DOM 元素吧。

自定義 DOM 元素

你可能知道在 html 里可以創(chuàng)建一個新的 HTML 標(biāo)簽,比如,如果不使用框架,你可以直接在 html 里插入一個新的標(biāo)簽:

然后查詢這個 DOM 節(jié)點并檢查類型,你會發(fā)現(xiàn)它是個完全合法的 DOM 元素(注:你可以在一個 html 文件里試試這部分代碼,甚至可以寫上 A Component,結(jié)果是可以運行的,原因見下文):

const element = document.querySelector("a-comp");
element.nodeType === Node.ELEMENT_NODE; // true

瀏覽器會使用 HTMLUnknownElement 接口來創(chuàng)建 a-comp 元素,這個接口又繼承 HTMLElement 接口,但是它不需要實現(xiàn)任何屬性或方法。你可以使用 CSS 來裝飾它,也可以給它添加事件監(jiān)聽器來監(jiān)聽一些普遍事件,比如 click 事件。所以正如我說的,a-comp 是一個完全合法的 DOM 元素。

然后,你可以把它轉(zhuǎn)變成 自定義 DOM 元素 來增強這個元素,你需要為它多帶帶創(chuàng)建一個類并使用 JS API 來注冊這個類:

class AComponent extends HTMLElement {...}
window.customElements.define("a-comp", AComponent);

這是不是和你一直在做的事情有些類似呀。

沒錯,這和你在 Angular 中定義一個組件非常類似,實際上,Angular 框架嚴(yán)格遵循 Web 組件標(biāo)準(zhǔn)但是為我們簡化了很多事情,所以我們不必自己創(chuàng)建 shadow root 并掛載到宿主元素(注:關(guān)于 shadow root 的概念網(wǎng)上資料很多,其實在 Chrome Dev Tools 里,點擊右上角 settings,然后點擊 Preferences -> Elements,打開 Show user agent shadow root 后,這樣你就可以在 Elements 面板里看到很多 DOM 元素下的 shadow root)。然而,我們在 Angular 中創(chuàng)建的組件并沒有注冊為自定義元素,它會被 Angular 以特定方式去處理。如果你對沒有框架時如何創(chuàng)建組件很好奇,你可以查看 Custom Elements v1: Reusable Web Components 。

現(xiàn)在已經(jīng)知道,我們可以創(chuàng)建任何一個 HTML 標(biāo)簽并在模板里使用它。所以,如果我們在 Angular 的組件模板里使用這個標(biāo)簽,框架將會給這個標(biāo)簽創(chuàng)建元素定義(注:這是由 Angular Compiler 編譯生成的):

function View_AppComponent_0(_l) {
    return jit_viewDef2(0, [
        jit_elementDef3(0, null, null, 1, "a-comp", [], ...)
    ])
}

然而,你得需要在 module 或組件裝飾器屬性里添加 schemas: [CUSTOM_ELEMENTS_SCHEMA],來告訴 Angular 你在使用自定義元素,否則 Angular Compiler 會拋出錯誤(注:所以如果需要使用某個組件,你不得不在 module.declarationsmodule.entryComponentscomponent.entryComponents 去注冊這個組件):

"a-comp" is not a known element:
1. If "c-comp" is an Angular component, then ...
2. If "c-comp" is a Web Component then add...

所以,我們已經(jīng)有了 DOM 元素但是還沒有附著在元素上的類呢,那 Angular 里除了組件外還有其他特殊類沒?當(dāng)然有——指令。讓我們看看指令有些啥。

指令定義

你可能知道每一個指令都有一個選擇器,用來掛載到特定的 DOM 元素上。大多數(shù)指令使用屬性選擇器(attribute selectors),但是有一些也選擇元素選擇器(element selectors)。實際上,Angular 表單指令就是使用 元素選擇器 form 來把特定行為附著在 html form元素上。

所以,讓我們創(chuàng)建一個空指令類,并把它附著在自定義元素上,再看看視圖定義是什么樣的:

@Directive({selector: "a-comp"})
export class ADirective {}

然后核查下生成的視圖工廠:

function View_AppComponent_0(_l) {
    return jit_viewDef2(0, [
        jit_elementDef3(0, null, null, 1, "a-comp", [], ...),
        jit_directiveDef4(16384, null, 0, jit_ADirective5, [],...)
    ], null, null);
}

現(xiàn)在 Angular Compiler 在視圖定義函數(shù)的第二個參數(shù)數(shù)組里,添加了新生成的指令定義 jit_directiveDef4 節(jié)點,并放在元素定義節(jié)點 jit_elementDef3 后面。同時設(shè)置元素定義的 childCount 為 1,因為附著在元素上的所有指令都會被看做該元素的子元素。

指令定義是個很簡單的節(jié)點定義,它是由 directiveDef 函數(shù)生成的,該函數(shù)參數(shù)列表如下(注:現(xiàn)在 Angular v5.x 版本略有不同):

Name Description
matchedQueries used when querying child nodes
childCount specifies how many children the current element have
ctor reference to the component or directive constructor
deps an array of constructor dependencies
props an array of input property bindings
outputs an array of output property bindings

本文我們只對 ctor 參數(shù)感興趣,它僅僅是我們定義的 ADirective 類的引用。當(dāng) Angular 創(chuàng)建指令對象時,它會實例化一個指令類,并存儲在視圖節(jié)點的 provider data 屬性里。

所以我們看到組件其實僅僅是一個元素定義加上一個指令定義,但僅僅如此么?你可能知道 Angular 總是沒那么簡單?。?/p> 組件展示

從上文知道,我們可以通過創(chuàng)建一個自定義元素和附著在該元素上的指令,來模擬創(chuàng)建出一個組件。讓我們定義一個真實的組件,并把由該組件編譯生成的視圖工廠類,與我們上面實驗性的視圖工廠類做個比較:

@Component({
  selector: "a-comp",
  template: "I am A component"
})
export class AComponent {}

做好準(zhǔn)備了么?下面是生成的視圖工廠類:

function View_AppComponent_0() {
    return jit_viewDef2(0, [
        jit_elementDef3(0, null, null, 1, "a-comp", [], ...
                    jit_View_AComponent_04, jit__object_Object_5),
        jit_directiveDef6(49152, null, 0, jit_AComponent7, [], ...)

好的,現(xiàn)在我們僅僅驗證了上文所說的。本示例中, Angular 使用兩種視圖節(jié)點來表示組件——元素節(jié)點定義和指令節(jié)點定義。但是當(dāng)使用一個真實的組件時,就會發(fā)現(xiàn)這兩個節(jié)點定義的參數(shù)列表還是有些不同的。讓我們看看有哪些不同吧。

節(jié)點類型

節(jié)點類型(NodeFlags)是所有節(jié)點定義函數(shù)的第一個參數(shù)(注:最新 Angular v5.* 中參數(shù)列表有點點不一樣,如 directiveDef 中第二個參數(shù)才是 NodeFlags)。它實際上是 NodeFlags 位掩碼(注:查看源碼,是用二進(jìn)制表示的),包含一系列特定的節(jié)點信息,大部分在 變更檢測循環(huán) 時被框架使用。并且不同節(jié)點類型采用不同數(shù)字:16384 表示簡單指令節(jié)點類型(注:僅僅是指令,可看 TypeDirective);49152 表示組件指令節(jié)點類型(注:組件加指令,即 TypeDirective + Component)。為了更好理解這些標(biāo)志位是如何被編譯器設(shè)置的,讓我們先轉(zhuǎn)換為二進(jìn)制:

16384 =  100000000000000 // 15th bit set
49152 = 1100000000000000 // 15th and 16th bit set

如果你很好奇這些轉(zhuǎn)換是怎么做的,可以查看我寫的文章 The simple math behind decimal-binary conversion algorithms 。所以,對于簡單指令 Angular 編譯器會設(shè)置 15-th 位為 1:

TypeDirective = 1 << 14

而對于組件節(jié)點會設(shè)置 15-th16-th 位為 1:

TypeDirective = 1 << 14
Component = 1 << 15

現(xiàn)在明白為何這些數(shù)字不同了。對于指令來說,生成的節(jié)點被標(biāo)記為 TypeDirective 節(jié)點;對于組件指令來說,生成的節(jié)點除了被標(biāo)記為 TypeDirective 節(jié)點,還被標(biāo)記為 Component 節(jié)點。

視圖定義解析器

因為 a-comp 是一個組件,所以對于下面的簡單模板:

I am A component

編譯器會編譯它,生成一個帶有視圖定義和視圖節(jié)點的工廠函數(shù):

function View_AComponent_0(_l) {
    return jit_viewDef1(0, [
        jit_elementDef2(0, null, null, 1, "span", [], ...),
        jit_textDef3(null, ["I am A component"])

Angular 是一個視圖樹,所以父視圖需要有個對子視圖的引用,子視圖會被存儲在元素節(jié)點內(nèi)。本例中,a-comp 的視圖存儲在為 生成的宿主元素節(jié)點內(nèi)(注:意思就是 AComponent 視圖存儲在該組件宿主元素的元素定義內(nèi),就是存在 componentView 屬性里。也可以查看 _Host.ngfactory.js 文件,該文件表示宿主元素 的工廠,里面存儲 AComponent 視圖對象)。jit_View_AComponent_04 參數(shù)是一個 代理類 的引用,這個代理類將會解析 工廠函數(shù) 創(chuàng)建一個 視圖定義。每一個視圖定義僅僅創(chuàng)建一次,然后存儲在 DEFINITION_CACHE,然后這個視圖定義函數(shù)被 Angular 用來 創(chuàng)建視圖對象

注:這段由于涉及大量的源碼函數(shù),會比較晦澀。作者講的是創(chuàng)建視圖的具體過程,細(xì)致到很多函數(shù)的調(diào)用??傊恍枰涀∫稽c就行:視圖解析器通過解析視圖工廠(ViewDefinitionFactory)得到視圖(ViewDefinition)。細(xì)節(jié)暫不用管。

拿到了視圖,又該如何畫出來呢?看下文。

組件渲染器類型

Angular 根據(jù)組件裝飾器中定義的 ViewEncapsulation 模式來決定使用哪種 DOM 渲染器:

Emulated Encapsulation Renderer

Shadow Renderer

Default Renderer

以上組件渲染器是通過 DomRendererFactory2 來創(chuàng)建的。componentRendererType 參數(shù)是在元素定義里被傳入的,本例即是 jit__object_Object_5(注:上面代碼里有這個對象,是 jit_elementDef3() 的最后一個參數(shù)),該參數(shù)是渲染器的一個基本描述符,用來決定使用哪一個渲染器渲染組件。其中,最重要的是視圖封裝模式和所用于組件的樣式(注:componentRendererType 參數(shù)的結(jié)構(gòu)是 RendererType2):

{
  styles:[["h1[_ngcontent-%COMP%] {color: green}"]], 
  encapsulation:0
}

如果你為組件定義了樣式,編譯器會自動設(shè)置組件的封裝模式為 ViewEncapsulation.Emulated,或者你可以在組件裝飾器里顯式設(shè)置 encapsulation 屬性。如果沒有設(shè)置任何樣式,并且也沒有顯式設(shè)置 encapsulation 屬性,那描述符會被設(shè)置為 ViewEncapsulation.Emulated,并被 忽略生效,使用這種描述符的組件會使用父組件的組件渲染器。

子指令

現(xiàn)在,最后一個問題是,如果我們像下面這樣,把一個指令作用在組件模板上,會生成什么:

我們已經(jīng)知道當(dāng)為 AComponent 生成工廠函數(shù)時,編譯器會為 a-comp 元素創(chuàng)建元素定義,會為 AComponent 類創(chuàng)建指令定義。但是由于編譯器會為每一個指令生成指令定義節(jié)點,所以上面模板的工廠函數(shù)像這樣(注:Angular v5.* 版本是會為 元素多帶帶生成一個 *_Host.ngfactory.js 文件,表示宿主視圖,多出來的 jit_directiveDef6(16384, null, 0, jit_ADirective8, [], ...) 是在這個文件代碼里??梢?ng cli 新建項目查看 Sources Tab -> ng://。但作者表達(dá)的意思還是一樣的。):

function View_AppComponent_0() {
    return jit_viewDef2(0, [
        jit_elementDef3(0, null, null, 2, "a-comp", [], ...
        jit_View_AComponent_04, jit__object_Object_5),

    jit_directiveDef6(49152, null, 0, jit_AComponent7, [], ...)
    jit_directiveDef6(16384, null, 0, jit_ADirective8, [], ...)

上面代碼都是我們熟悉的,僅僅是多添加了一個指令定義,和子組件數(shù)量增加為 2。

以上就是全部了!

注:全文主要講的是組件(視圖)在 Angular 內(nèi)部是如何用指令節(jié)點和元素節(jié)點定義的。

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

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

相關(guān)文章

  • [] Angular 屬性綁定更新機制

    摘要:本文主要介紹輸入輸出綁定方式,特別是當(dāng)父組件輸入綁定值變化時,如何更新子組件輸入值。更新指令的屬性上文中已經(jīng)描述了函數(shù)是用來更新元素的屬性,而是用來更新子組件的輸入綁定屬性,并且變更檢測期間傳入的參數(shù)就是函數(shù)。 原文鏈接:The mechanics of property bindings update in Angular showImg(https://segmentfault....

    tianhang 評論0 收藏0
  • [] $digest 在 Angular 中重生

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

    incredible 評論0 收藏0
  • [] 關(guān)于 `ExpressionChangedAfterItHasBeenCheckedErro

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

    andong777 評論0 收藏0
  • [] 別再對 Angular Modules 感到迷惑

    摘要:大多數(shù)初學(xué)者會認(rèn)為也有封裝規(guī)則,但實際上沒有。第二個規(guī)則是最后導(dǎo)入模塊的,會覆蓋前面導(dǎo)入模塊的。 原文鏈接:Avoiding common confusions with modules in Angular showImg(https://segmentfault.com/img/remote/1460000015298243?w=270&h=360); Angular Modul...

    LMou 評論0 收藏0
  • [] 你真的知道 Angular 單向數(shù)據(jù)流嗎

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

    fox_soyoung 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<