W3Cschool
恭喜您成為首批注冊(cè)用戶(hù)
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
本頁(yè)面會(huì)提供一個(gè)概念性的概述,它介紹了一種建議庫(kù)開(kāi)發(fā)者使用的依賴(lài)注入技術(shù)。使用輕量級(jí)注入令牌設(shè)計(jì)你的庫(kù),這有助于優(yōu)化那些用到你庫(kù)的客戶(hù)應(yīng)用的發(fā)布包體積。
你可以使用可搖樹(shù)優(yōu)化的提供者來(lái)管理組件和可注入服務(wù)之間的依賴(lài)結(jié)構(gòu),以?xún)?yōu)化發(fā)布包體積。這通常會(huì)確保如果提供的組件或服務(wù)從未被應(yīng)用實(shí)際使用過(guò),那么編譯器就可以從發(fā)布包中刪除它的代碼。
但是,由于 Angular 存儲(chǔ)注入令牌的方式,可能會(huì)導(dǎo)致未用到的組件或服務(wù)最終進(jìn)入發(fā)布包中。本頁(yè)描述了依賴(lài)注入的一種設(shè)計(jì)模式,它通過(guò)使用輕量級(jí)注入令牌來(lái)支持正確的搖樹(shù)優(yōu)化。
這種輕量級(jí)注入令牌設(shè)計(jì)模式對(duì)于庫(kù)開(kāi)發(fā)者來(lái)說(shuō)尤其重要。它可以確保當(dāng)應(yīng)用只用到了你庫(kù)中的某些功能時(shí),可以從客戶(hù)應(yīng)用的發(fā)布包中刪除未使用過(guò)的代碼。
當(dāng)某應(yīng)用用到了你的庫(kù)時(shí),你的庫(kù)中可能會(huì)提供一些客戶(hù)應(yīng)用未用到的服務(wù)。在這種情況下,應(yīng)用開(kāi)發(fā)人員會(huì)期望該服務(wù)是可搖樹(shù)優(yōu)化的,不讓這部分代碼增加應(yīng)用的編譯后大小。由于應(yīng)用開(kāi)發(fā)人員既無(wú)法了解也無(wú)法解決庫(kù)的搖樹(shù)優(yōu)化問(wèn)題,因此這是庫(kù)開(kāi)發(fā)人員的責(zé)任。為了防止未使用的組件被保留下來(lái),你的庫(kù)應(yīng)該使用輕量級(jí)注入令牌這種設(shè)計(jì)模式。
為了更好地解釋令牌被保留的條件,我們考慮一個(gè)提供卡片組件的庫(kù),它包含一個(gè)卡片體,還可以包含一個(gè)可選的卡片頭。
<lib-card>
<lib-header>…</lib-header>
</lib-card>
在一個(gè)可能的實(shí)現(xiàn)中,?<lib-card>
? 組件使用 ?@ContentChild()
? 或者 ?@ContentChildren()
? 來(lái)獲取 ?<lib-header>
? 和 ?<lib-body>
?,如下所示。
@Component({
selector: 'lib-header',
…,
})
class LibHeaderComponent {}
@Component({
selector: 'lib-card',
…,
})
class LibCardComponent {
@ContentChild(LibHeaderComponent)
header: LibHeaderComponent|null = null;
}
因?yàn)?nbsp;?<lib-header>
? 是可選的,所以元素可以用最小化的形式 ?<lib-card></lib-card>
? 出現(xiàn)在模板中。在這個(gè)例子中,?<lib-header>
? 沒(méi)有用過(guò),你可能期望它會(huì)被搖樹(shù)優(yōu)化掉,但事實(shí)并非如此。這是因?yàn)?nbsp;?LibCardComponent
?實(shí)際上包含兩個(gè)對(duì) ?LibHeaderComponent
?引用。
@ContentChild(LibHeaderComponent) header: LibHeaderComponent;
LibHeaderComponent
?用作了類(lèi)型:?header: LibHeaderComponent
?;。
@ContentChild()
? 參數(shù)裝飾器的值:?@ContentChild(LibHeaderComponent)
?。編譯器對(duì)這些位置的令牌引用的處理方式也不同。
在這個(gè)例子中,編譯器保留了 ?LibHeaderComponent
?令牌,它出現(xiàn)在了值位置上,這就會(huì)防止所引用的組件被搖樹(shù)優(yōu)化掉,即使應(yīng)用開(kāi)發(fā)者實(shí)際上沒(méi)有在任何地方用過(guò) ?<lib-header>
?。如果 ?LibHeaderComponent
?很大(代碼、模板和樣式),把它包含進(jìn)來(lái)就會(huì)不必要地大大增加客戶(hù)應(yīng)用的大小。
當(dāng)一個(gè)組件被用作注入令牌時(shí),就會(huì)出現(xiàn)搖樹(shù)優(yōu)化的問(wèn)題。有兩種情況可能會(huì)發(fā)生。
在下面的例子中,兩處對(duì) ?OtherComponent
?令牌的使用導(dǎo)致 ?OtherComponent
?被保留下來(lái)(也就是說(shuō),防止它在未用到時(shí)被搖樹(shù)優(yōu)化掉)。
class MyComponent {
constructor(@Optional() other: OtherComponent) {}
@ContentChild(OtherComponent)
other: OtherComponent|null;
}
雖然轉(zhuǎn)換為 JavaScript 時(shí)只會(huì)刪除那些只用作類(lèi)型說(shuō)明符的令牌,但在運(yùn)行時(shí)依賴(lài)注入需要所有這些令牌。這些工作把 ?constructor(@Optional() other: OtherComponent)
? 改成了 ?constructor(@Optional() @Inject(OtherComponent) other)
?。該令牌現(xiàn)在處于值的位置,并使該搖樹(shù)優(yōu)化器保留該引用。
對(duì)于所有服務(wù),庫(kù)都應(yīng)該使用可搖樹(shù)優(yōu)化的提供者,在根級(jí)而不是組件構(gòu)造函數(shù)中提供依賴(lài)。
輕量級(jí)注入令牌設(shè)計(jì)模式包括:使用一個(gè)小的抽象類(lèi)作為注入令牌,并在稍后為它提供實(shí)際實(shí)現(xiàn)。該抽象類(lèi)固然會(huì)被留下(不會(huì)被搖樹(shù)優(yōu)化掉),但它很小,對(duì)應(yīng)用程序的大小沒(méi)有任何重大影響。
下例舉例說(shuō)明了這個(gè) ?LibHeaderComponent
?的工作原理。
abstract class LibHeaderToken {}
@Component({
selector: 'lib-header',
providers: [
{provide: LibHeaderToken, useExisting: LibHeaderComponent}
]
…,
})
class LibHeaderComponent extends LibHeaderToken {}
@Component({
selector: 'lib-card',
…,
})
class LibCardComponent {
@ContentChild(LibHeaderToken) header: LibHeaderToken|null = null;
}
在這個(gè)例子中,?LibCardComponent
?的實(shí)現(xiàn)里,?LibHeaderComponent
?既不會(huì)出現(xiàn)在類(lèi)型的位置也不會(huì)出現(xiàn)在值的位置。這樣就可以讓 ?LibHeaderComponent
?完全被搖樹(shù)優(yōu)化掉。?LibHeaderToken
?被留下了,但它只是一個(gè)類(lèi)聲明,沒(méi)有具體的實(shí)現(xiàn)。它很小,并且在編譯后保留時(shí)對(duì)應(yīng)用程序的大小沒(méi)有實(shí)質(zhì)影響。
不過(guò),?LibHeaderComponent
?本身實(shí)現(xiàn)了抽象類(lèi) ?LibHeaderToken
?。你可以放心使用這個(gè)令牌作為組件定義中的提供者,讓 Angular 能夠正確地注入具體類(lèi)型。
總結(jié)一下,輕量級(jí)注入令牌模式由以下幾部分組成。
@ContentChild()
? 或者 ?@ContentChildren()
?。
那些注入了輕量級(jí)注入令牌的組件可能要調(diào)用注入的類(lèi)中的方法。因?yàn)榱钆片F(xiàn)在是一個(gè)抽象類(lèi),并且可注入組件實(shí)現(xiàn)了那個(gè)抽象類(lèi),所以你還必須在作為輕量級(jí)注入令牌的抽象類(lèi)中聲明一個(gè)抽象方法。該方法的實(shí)現(xiàn)代碼(及其所有相關(guān)代碼)都會(huì)留在可注入組件中,但這個(gè)組件本身仍可被搖樹(shù)優(yōu)化。這樣就能讓父組件以類(lèi)型安全的方式與子組件(如果存在)進(jìn)行通信。
比如,?LibCardComponent
?現(xiàn)在要查詢(xún) ?LibHeaderToken
?而不是 ?LibHeaderComponent
?。這個(gè)例子展示了該模式如何讓 ?LibCardComponent
?與 ?LibHeaderComponent
?通信,卻不用實(shí)際引用 ?LibHeaderComponent
?。
abstract class LibHeaderToken {
abstract doSomething(): void;
}
@Component({
selector: 'lib-header',
providers: [
{provide: LibHeaderToken, useExisting: LibHeaderComponent}
]
…,
})
class LibHeaderComponent extends LibHeaderToken {
doSomething(): void {
// Concrete implementation of `doSomething`
}
}
@Component({
selector: 'lib-card',
…,
})
class LibCardComponent implement AfterContentInit {
@ContentChild(LibHeaderToken)
header: LibHeaderToken|null = null;
ngAfterContentInit(): void {
this.header && this.header.doSomething();
}
}
在這個(gè)例子中,父組件會(huì)查詢(xún)令牌以獲取子組件,并持有結(jié)果組件的引用(如果存在)。在調(diào)用子組件中的方法之前,父組件會(huì)檢查子組件是否存在。如果子組件已經(jīng)被搖樹(shù)優(yōu)化掉,那運(yùn)行期間就沒(méi)有對(duì)它的引用,當(dāng)然也沒(méi)有調(diào)用它的方法。
輕量級(jí)注入令牌只對(duì)組件有用。Angular 風(fēng)格指南中建議你使用“Component”后綴命名組件。比如“LibHeaderComponent”就遵循這個(gè)約定。
為了維護(hù)組件及其令牌之間的對(duì)應(yīng)關(guān)系,同時(shí)又要區(qū)分它們,推薦的寫(xiě)法是使用組件基本名加上后綴“?Token
?”來(lái)命名你的輕量級(jí)注入令牌:“?LibHeaderToken
?”。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話(huà):173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: