摘要:注入器樹(shù)大多數(shù)開(kāi)發(fā)者知道,會(huì)創(chuàng)建根注入器,根注入器內(nèi)的服務(wù)都是單例的。也會(huì)被添加到這個(gè)根注入器對(duì)象內(nèi),它主要用來(lái)創(chuàng)建動(dòng)態(tài)組件,因?yàn)樗鎯?chǔ)了屬性指向的組件數(shù)組。為了理解依賴解析算法,我們首先需要知道視圖和父視圖元素概念。
What you always wanted to know about Angular Dependency Injection tree
如果你之前沒(méi)有深入了解 Angular 依賴注入系統(tǒng),那你現(xiàn)在可能認(rèn)為 Angular 程序內(nèi)的根注入器包含所有合并的服務(wù)提供商,每一個(gè)組件都有它自己的注入器,延遲加載模塊有它自己的注入器。
但是,僅僅知道這些可能還不夠呢?
不久前有個(gè)叫 Tree-Shakeable Tokens feature 被合并到 master 分支,如果你和我一樣充滿好奇心,可能也想知道這個(gè) feature 改變了哪些東西。
所以現(xiàn)在去看看,可能有意外收獲嗷。
注入器樹(shù)(Injector Tree)大多數(shù)開(kāi)發(fā)者知道,Angular 會(huì)創(chuàng)建根注入器,根注入器內(nèi)的服務(wù)都是單例的。但是,貌似還有其他注入器是它的父級(jí)。
作為一名開(kāi)發(fā)者,我想知道 Angular 是怎么構(gòu)建注入器樹(shù)的,下圖是注入器樹(shù)的頂層部分:
這不是整棵樹(shù),目前還沒(méi)有任何組件呢,后面會(huì)繼續(xù)畫(huà)樹(shù)的剩余部分。但是現(xiàn)在先看下根注入器 AppModule Injector,因?yàn)樗亲畛J褂玫摹?/p> 根注入器(Root AppModule Injector)
我們知道 Angular 程序根注入器 就是上圖的 AppModule Injector,上文說(shuō)了,這個(gè)根注入器包含所有中間模塊的服務(wù)提供商,也就是說(shuō)(注:不翻譯):
If we have a module with some providers and import this module directly in AppModule or in any other module, which has already been imported in AppModule, then those providers become application-wide providers.
根據(jù)這個(gè)規(guī)則,上圖中 EagerModule2 的 MyService2 也會(huì)被包含在根注入器 AppModule Injector 中。
ComponentFactoryResolver 也會(huì)被 Angular 添加 到這個(gè)根注入器對(duì)象內(nèi),它主要用來(lái)創(chuàng)建動(dòng)態(tài)組件,因?yàn)樗鎯?chǔ)了 entryComponents 屬性指向的組件數(shù)組。
值得注意的是,所有服務(wù)提供商中有 Module Tokens,它們都是被導(dǎo)入模塊的類名。后面探索到 tree-shakeable tokens 時(shí)候,還會(huì)回到這個(gè) Module Tokens 話題。
Angular 使用 AppModule 工廠函數(shù)來(lái)實(shí)例化根注入器 AppModule Injector,這個(gè) AppModule 工廠函數(shù)就在所謂的 module.ngfactory.js 文件內(nèi):
我們可以看到這個(gè)工廠函數(shù)返回一個(gè)包含所有被合并服務(wù)提供商的模塊對(duì)象,所有開(kāi)發(fā)者都應(yīng)當(dāng)熟悉這個(gè)(注:可以查看 譯 Angular 的 @Host 裝飾器和元素注入器)。
Tip: If you have angular application in dev mode and want to see all providers from root AppModule injector then just open devtools console and write:
ng.probe(getAllAngularRootElements()[0]).injector.view.root.ngModule._providers
還有很多其他知識(shí)點(diǎn),我在這里沒(méi)有描述,因?yàn)楣倬W(wǎng)上已經(jīng)談到了:
https://angular.io/guide/ngmodule-faq
https://angular.io/guide/hierarchical-dependency-injection
Platform Injector實(shí)際上,根注入器 AppModule Injector 有個(gè)父注入器 NgZoneInjector,而它又是 PlatformInjector 的子注入器。
PlatformInjector 會(huì)在 PlatformRef 對(duì)象初始化的時(shí)候,包含內(nèi)置的服務(wù)提供商,但也可以額外包含服務(wù)提供商:
const platform = platformBrowserDynamic([ { provide: SharedService, deps:[] }]); platform.bootstrapModule(AppModule); platform.bootstrapModule(AppModule2);
這些額外的服務(wù)提供商是由我們開(kāi)發(fā)者傳入的,且必須是 StaticProviders。如果你不熟悉 StaticProviders 和 Provider 兩者間的區(qū)別,可以查看這個(gè) StackOverflow 的答案。
Tip: If you have angular application in dev mode and want to see all providers from Platform injector then just open devtools console and write:
ng.probe(getAllAngularRootElements()[0]).injector.view.root.ngModule._parent.parent._records; // to see stringified value use ng.probe(getAllAngularRootElements()[0]).injector.view.root.ngModule._parent.parent.toString();
盡管根注入器以及其父注入器解析依賴的過(guò)程清晰明了,但是組件級(jí)別的注入器如何解析依賴卻讓我很困惑,所以,我接著去深入了解。
EntryComponent and RootData我在上文聊到 ComponentFactoryResolver 時(shí),就涉及到 entryComponents 入口組件。這些入口組件會(huì)在 NgModule 的 bootstrap 或 entryComponents 屬性中聲明,@angular/router 也會(huì)用它們動(dòng)態(tài)創(chuàng)建組件。
Angular 會(huì)為所有入口組件創(chuàng)建宿主工廠函數(shù),這些宿主工廠函數(shù)就是其他視圖的根視圖,也就是說(shuō)(注:不翻譯):
Every time we create dynamic component angular creates root view with root data, that contains references to elInjector and ngModule injector.
function createRootData( elInjector: Injector, ngModule: NgModuleRef, rendererFactory: RendererFactory2, projectableNodes: any[][], rootSelectorOrNode: any): RootData { const sanitizer = ngModule.injector.get(Sanitizer); const errorHandler = ngModule.injector.get(ErrorHandler); const renderer = rendererFactory.createRenderer(null, null); return { ngModule, injector: elInjector, projectableNodes, selectorOrNode: rootSelectorOrNode, sanitizer, rendererFactory, renderer, errorHandler }; }
假設(shè)現(xiàn)在正在運(yùn)行一個(gè) Angular 程序。
下面代碼執(zhí)行時(shí),其內(nèi)部發(fā)生了什么:
platformBrowserDynamic().bootstrapModule(AppModule);
事實(shí)上,其內(nèi)部發(fā)生了很多事情,但是我們僅僅對(duì) Angular 是 如何創(chuàng)建入口組件 這塊感興趣:
const compRef = componentFactory.create(Injector.NULL, [], selectorOrNode, ngModule);
Angular 注入樹(shù)就是從這里開(kāi)始,分叉為兩顆并行樹(shù)。
Element Injector vs Module Injector不久前,當(dāng)延遲加載模塊被廣泛使用時(shí),在 github 上 有人報(bào)告了一個(gè)奇怪的案例:依賴注入系統(tǒng)會(huì)兩次實(shí)例化延遲加載模塊。結(jié)果,一個(gè)新的設(shè)計(jì)被引入。所以,從那開(kāi)始,Angular 有兩個(gè)并行注入樹(shù):元素注入樹(shù)和模塊注入樹(shù)。
主要規(guī)則是:當(dāng)組件或指令需要解析依賴時(shí),Angular 使用 Merge Injector 來(lái)遍歷 element injector tree,如果沒(méi)找到該依賴,則遍歷 module injector tree 去查找依賴。
Please note I don"t use phrase "component injector" but rather "element injector".
什么是 Merge Injector?
你以前可能寫(xiě)過(guò)如下類似代碼:
@Directive({ selector: "[someDir]" } export class SomeDirective { constructor(private injector: Injector) {} }
這里的 injector 就是 Merge Injector,當(dāng)然你也可以在組件中注入這個(gè) Merge Injector。
Merge Injector 對(duì)象的定義如下:
class Injector_ implements Injector { constructor(private view: ViewData, private elDef: NodeDef|null) {} get(token: any, notFoundValue: any = Injector.THROW_IF_NOT_FOUND): any { const allowPrivateServices = this.elDef ? (this.elDef.flags & NodeFlags.ComponentView) !== 0 : false; return Services.resolveDep( this.view, this.elDef, allowPrivateServices, {flags: DepFlags.None, token, tokenKey: tokenKey(token)}, notFoundValue); } }
如上代碼顯示了 Merge Injector 僅僅是視圖和元素的組合,這個(gè)注入器充當(dāng)依賴解析時(shí) element injector tree 和 module injector tree 之間的橋梁。
Merge Injector 也可以解析內(nèi)置的對(duì)象,如 ElementRef,ViewContainerRef,TemplateRef,ChangeDetectorRef 等等,更有趣的是,它還可以返回 Merge Injector。
基本上每一個(gè) DOM 元素都有一個(gè) merge injector,即使沒(méi)有提供任何令牌。
Tip: to get merge injector just open console and write:
ng.probe($0).injector
但是你可能會(huì)問(wèn) element injector 是什么?
我們知道 @angular/compiler 會(huì)編譯組件模板生成工廠函數(shù),該函數(shù)實(shí)際上只是調(diào)用 viewDef() 函數(shù)返回 ViewDefinition 類型對(duì)象,視圖僅僅是模板的一種表現(xiàn)形式,里面包含各種類型節(jié)點(diǎn),如 directive,text,provider 和 query 等等。其中有元素節(jié)點(diǎn) element node 用來(lái)表示 DOM 元素的。實(shí)際上,元素注入器 element injector 就在這個(gè)節(jié)點(diǎn)內(nèi)。Angular 會(huì)把該元素節(jié)點(diǎn)上所有的服務(wù)提供商都存儲(chǔ)在該節(jié)點(diǎn)的兩個(gè)屬性里:
export interface ElementDef { ... /** * visible public providers for DI in the view, * as see from this element. This does not include private providers. */ publicProviders: {[tokenKey: string]: NodeDef}|null; /** * same as visiblePublicProviders, but also includes private providers * that are located on this element. */ allProviders: {[tokenKey: string]: NodeDef}|null; }
讓我們看看 元素注入器是如何解析依賴的:
const providerDef = (allowPrivateServices ? elDef.element!.allProviders : elDef.element!.publicProviders)![tokenKey]; if (providerDef) { let providerData = asProviderData(searchView, providerDef.nodeIndex); if (!providerData) { providerData = { instance: _createProviderInstance(searchView, providerDef) }; searchView.nodes[providerDef.nodeIndex] = providerData as any; } return providerData.instance; }
這里僅僅檢查 allProviders 屬性,或依據(jù)私有性檢查 publicProviders。
這個(gè)注入器包含組件/指令對(duì)象,和其中的所有服務(wù)提供商。
視圖實(shí)例化階段 時(shí)主要由 ProviderElementContext 對(duì)象提供這些服務(wù)提供商,該對(duì)象也是 @angular/compiler Angular 編譯器的一部分。如果我們深入挖掘這個(gè)對(duì)象,會(huì)發(fā)現(xiàn)一些有趣的事情。
比如說(shuō),當(dāng)使用 @Host 裝飾器時(shí)會(huì)有一些 限制,可以使用宿主元素的 viewProviders 屬性來(lái)解決這些限制,可以查看 https://medium.com/@a.yurich.... 。
另一個(gè)有趣的事情是,如果組件宿主元素上掛載指令,但組件和指令提供相同的令牌,則指令的服務(wù)提供商會(huì) 勝出。
Tip: to get element injector just open console and write:
ng.probe($0).injector.elDef.element依賴解析算法
視圖內(nèi)依賴解析算法代碼是 resolveDep() 函數(shù),merge injector 在 get() 方法中也是使用這個(gè)函數(shù)來(lái)解析依賴(Services.resolveDep)。為了理解依賴解析算法,我們首先需要知道視圖和父視圖元素概念。
如果根組件有模板
HostView_AppComponentView_AppComponent View_ChildComponent some content
依賴解析算法會(huì)根據(jù)多級(jí)視圖來(lái)解析:
如果子組件需要解析依賴,那 Angular 會(huì)首先查找該組件的元素注入器,也就是檢查 elRef.element.allProviders|publicProviders,然后 向上遍歷父視圖元素 檢查元素注入器的服務(wù)提供商(1),直到父視圖元素等于 null(2), 則返回 startView(3),然后檢查 startView.rootData.elnjector(4),最后,只有當(dāng)令牌沒(méi)找到,再去檢查 startView.rootData module.injector(5)。(注:元素注入器 -> 組件注入器 -> 模塊注入器)
當(dāng)向上遍歷組件視圖來(lái)解析依賴時(shí),會(huì)搜索 視圖的父元素而不是元素的父元素。Angular 使用 viewParentEl() 函數(shù)獲取視圖父元素:
/** * for component views, this is the host element. * for embedded views, this is the index of the parent node * that contains the view container. */ export function viewParentEl(view: ViewData): NodeDef|null { const parentView = view.parent; if (parentView) { return view.parentNodeDef !.parent; } else { return null; } }
比如說(shuō),假設(shè)有如下的一段小程序:
@Component({ selector: "my-app", template: `` }) export class AppComponent {} @Component({ selector: "my-list", template: ` ` }) export class MyListComponent {} @Component({ selector: "grid-list", template: `1 2 3 ` }) export class GridListComponent {} @Component({ selector: "grid-tile", template: `...` }) export class GridTileComponent { constructor(private gridList: GridListComponent) {} }
假設(shè) grid-tile 組件依賴 GridListComponent,我們可以成功拿到該組件對(duì)象。但是這是怎么做到的?
這里父視圖元素究竟是什么?
下面的步驟回答了這個(gè)問(wèn)題:
查找 起始元素。GridListComponent 組件模板里包含 grid-tile 元素選擇器,因此需要找到匹配 grid-tile 選擇器的元素。所以起始元素就是 grid-tile 元素。
查找擁有 grid-tile 元素的 模板,也就是 MyListComponent 組件模板。
決定該元素的視圖。如果沒(méi)有父嵌入視圖,則為組件視圖,否則為嵌入視圖。grid-tile 元素之上沒(méi)有任何 ng-template 或 *structuralDirective,所以這里是組件視圖 View_MyListComponent。
查找視圖的父元素。這里是視圖的父元素,而不是元素的父元素。
這里有兩種情況:
對(duì)于嵌入視圖,父元素則為包含該嵌入視圖的視圖容器。
比如,假設(shè) grid-list 上掛載有結(jié)構(gòu)指令:
@Component({ selector: "my-list", template: `` }) export class MyListComponent {}1 2 3
則 grid-tile 視圖的父元素則是 div.container。
對(duì)于組件視圖,父元素則為宿主元素。
我們上面的小程序也就是組件視圖,所以父視圖元素是 my-list 元素,而不是 grid-list。
現(xiàn)在,你可能想知道如果 Angular 跳過(guò) grid-list,則它是怎么解析 GridListComponent 依賴的?
關(guān)鍵是 Angular 使用 原型鏈繼承 來(lái)搜集服務(wù)提供商:
每次我們?yōu)橐粋€(gè)元素提供服務(wù)提供商時(shí),Angular 會(huì)新創(chuàng)建繼承于父節(jié)點(diǎn)的 allProviders 和 publicProviders 數(shù)組,否則不會(huì)新創(chuàng)建,僅僅會(huì)共享父節(jié)點(diǎn)的這兩個(gè)數(shù)組。
這就表示了 grid-tile 包含當(dāng)前視圖內(nèi)所有父元素的所有服務(wù)提供商。
下圖基本說(shuō)明了 Angular 是如何為模板內(nèi)元素收集服務(wù)提供商:
正如上圖顯示的,grid-tile 使用元素注入器通過(guò) allProviders 成功拿到 GridListComponent 依賴,因?yàn)?grid-tile 元素注入器包含來(lái)自于父元素的服務(wù)提供商。
想要了解更多,可以查看 StackOverflow answer。
元素注入器的服務(wù)提供商使用了原型鏈繼承,導(dǎo)致我們不能使用 multi 選項(xiàng)來(lái)提供同一令牌多個(gè)服務(wù)。但是由于依賴注入系統(tǒng)很靈活,也有辦法去解決這個(gè)問(wèn)題,可以查看 https://stackoverflow.com/que...。
可以把上文的解釋裝入腦中,現(xiàn)在繼續(xù)畫(huà)注入樹(shù)。
Simple my-app->child->grand-child application假設(shè)有如下簡(jiǎn)單程序:
@Component({ selector: "my-app", template: ``, }) export class AppComponent {} @Component({ selector: "child", template: ` ` }) export class ChildComponent {} @Component({ selector: "grand-child", template: `grand-child` }) export class GrandChildComponent { constructor(private service: Service) {} } @NgModule({ imports: [BrowserModule], declarations: [ AppComponent, ChildComponent, GrandChildComponent ], bootstrap: [AppComponent] }) export class AppModule { }
我們有三層樹(shù)結(jié)構(gòu),并且 GrandChildComponent 依賴于 Service:
my-app child grand-child(ask for Service dependency)
下圖解釋了 Angular 內(nèi)部是如何解析 Service 依賴的:
上圖從 View_Child (1)的 grand-child 元素開(kāi)始,并向上遍歷查找所有視圖的父元素,當(dāng)視圖沒(méi)有父元素時(shí),本實(shí)例中 may-app 沒(méi)有父元素,則 使用根視圖的注入器查找(2):
startView.root.injector.get(depDef.token, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR);
本實(shí)例中 startView.root.injector 就是 NullInjector,由于 NullInjector 沒(méi)有任何服務(wù)提供商,則 Angular 就會(huì) 切換到模塊注入器(3):
startView.root.ngModule.injector.get(depDef.token, notFoundValue);
所以 Angular 會(huì)按照以下順序解析依賴:
AppModule Injector || / ZoneInjector || / Platform Injector || / NullInjector || / Error路由程序
讓我們修改程序,添加路由器:
@Component({ selector: "my-app", template: ``, }) export class AppComponent {} ... @NgModule({ imports: [ BrowserModule, RouterModule.forRoot([ { path: "child", component: ChildComponent }, { path: "", redirectTo: "/child", pathMatch: "full" } ]) ], declarations: [ AppComponent, ChildComponent, GrandChildComponent ], bootstrap: [ AppComponent ] }) export class AppModule { }
這樣視圖樹(shù)就類似為:
my-app router-outlet child grand-child(dynamic creation)
現(xiàn)在讓我們看看 路由是如何創(chuàng)建動(dòng)態(tài)組件的:
const injector = new OutletInjector(activatedRoute, childContexts, this.location.injector); this.activated = this.location.createComponent(factory, this.location.length, injector);
這里 Angular 使用新的 rootData 對(duì)象創(chuàng)建一個(gè)新的根視圖,同時(shí)傳入 OutletInjector 作為根元素注入器 elInjector。OutletInjector 又依賴于父注入器 this.location.injector,該父注入器是 router-outlet 的元素注入器。
OutletInjector 是一種特別的注入器,行為有些像路由組件和父元素 router-outlet 之間的橋梁,該對(duì)象代碼可以看 這里:
延遲加載程序最后,讓我們把 GrandChildComponent 移到延遲加載模塊,為此需要在子組件中添加 router-outlet,并修改路由配置:
@Component({ selector: "child", template: ` Child` }) export class ChildComponent {} ... @NgModule({ imports: [ BrowserModule, RouterModule.forRoot([ { path: "child", component: ChildComponent, children: [ { path: "grand-child", loadChildren: "./grand-child/grand-child.module#GrandChildModule"} ] }, { path: "", redirectTo: "/child", pathMatch: "full" } ]) ], declarations: [ AppComponent, ChildComponent ], bootstrap: [AppComponent] }) export class AppModule {}
my-app router-outlet child (dynamic creation) router-outlet +grand-child(lazy loading)
讓我們?yōu)檠舆t加載程序畫(huà)兩顆獨(dú)立的樹(shù):
Tree-shakeable tokens are on horizonAngular 團(tuán)隊(duì)為讓框架變得更小,后續(xù)又做了大量工作,從 version 6 開(kāi)始,提供了另一種注冊(cè)服務(wù)提供商的方式。
Injectable之前由 Injectable 裝飾的類不能說(shuō)明它是否有依賴,與它如何被使用也無(wú)關(guān)。所以,如果一個(gè)服務(wù)沒(méi)有依賴,那 Injectable 裝飾器是可以被移除的。
隨著 API 變得穩(wěn)定,可以配置 Injectable 裝飾器來(lái)告訴 Angular,該服務(wù)是屬于哪一個(gè)模塊的,以及它是被如何實(shí)例化的:
export interface InjectableDecorator { (): any; (options?: {providedIn: Type| "root" | null}&InjectableProvider): any; new (): Injectable; new (options?: {providedIn: Type | "root" | null}&InjectableProvider): Injectable; } export type InjectableProvider = ValueSansProvider | ExistingSansProvider | StaticClassSansProvider | ConstructorSansProvider | FactorySansProvider | ClassSansProvider;
下面是一個(gè)簡(jiǎn)單實(shí)用案例:
@Injectable({ providedIn: "root" }) export class SomeService {} @Injectable({ providedIn: "root", useClass: MyService, deps: [] }) export class AnotherService {}
與 ngModule factory 包含所有服務(wù)提供商不同,這里把有關(guān)服務(wù)提供商的信息存儲(chǔ)在 Injectable 裝飾器內(nèi)。這個(gè)技術(shù)會(huì)讓我們程序代碼變得更小,因?yàn)闆](méi)有被使用的服務(wù)會(huì)被搖樹(shù)優(yōu)化掉。如果我們使用 Injectable 來(lái)注冊(cè)服務(wù)提供商,而使用者又不導(dǎo)入我們的服務(wù)提供商,那最后被打包的代碼不包含這些服務(wù)提供商,所以,
Prefer registering providers in Injectables over NgModule.providers over Component.providers
本文開(kāi)始時(shí)我提到過(guò)根注入器的 Modules Tokens,所以 Angular 能夠區(qū)分哪一個(gè)模塊出現(xiàn)在特定的模塊注入器內(nèi)。
依賴解析器會(huì)使用這個(gè)信息來(lái) 判斷可搖樹(shù)優(yōu)化令牌是否屬于模塊注入器。
InjectionToken可以使用 InjectionToken 對(duì)象來(lái)定義依賴注入系統(tǒng)如何構(gòu)造一個(gè)令牌以及該令牌應(yīng)用于哪一個(gè)注入器:
export class InjectionToken{ constructor(protected _desc: string, options?: { providedIn?: Type | "root" | null, factory: () => T }) {} }
所以應(yīng)該這樣使用:
export const apiUrl = new InjectionToken("tree-shakeable apiUrl token", { providedIn: "root", factory: () => "someUrl" });結(jié)論
依賴注入是 Angular 框架中的一個(gè)非常復(fù)雜的話題,知道其內(nèi)部工作原理會(huì)讓你對(duì)你做的事情更有信心,所以我強(qiáng)烈建議偶爾去深入研究 Angular 源代碼。
注:這篇文章很有深度,很長(zhǎng)也很難,加油吧!
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/108392.html
摘要:裝飾器我們?yōu)樯兑懻撛刈⑷肫鞫皇茄b飾器這是因?yàn)闀?huì)把元素注入器依賴解析過(guò)程限制在當(dāng)前組件視圖內(nèi)。但是一旦使用了裝飾器,整個(gè)依賴解析過(guò)程就會(huì)在第一階段完成后停止解析,也就是說(shuō),元素注入器只在組件視圖內(nèi)解析依賴,然后就停止解析工作。 原文鏈接:A curious case of the @Host decorator and Element Injectors in Angular 我...
摘要:大多數(shù)初學(xué)者會(huì)認(rèn)為也有封裝規(guī)則,但實(shí)際上沒(méi)有。第二個(gè)規(guī)則是最后導(dǎo)入模塊的,會(huì)覆蓋前面導(dǎo)入模塊的。 原文鏈接:Avoiding common confusions with modules in Angular showImg(https://segmentfault.com/img/remote/1460000015298243?w=270&h=360); Angular Modul...
摘要:在這篇文章里,我將介紹如何使用去編寫(xiě)的。一個(gè)新的子將被創(chuàng)建并作為變量注入到的構(gòu)造函數(shù)當(dāng)中。當(dāng)使用一個(gè)構(gòu)造函數(shù)就可以很好地解決問(wèn)題,因?yàn)楹瘮?shù)提升起到了很重要的作用。自定義接口類型接著就可以在構(gòu)造器使用參數(shù)獲得強(qiáng)類型和智能支持了。 原文鏈接 : How to write AngularJS controller using TypeScript?原文作者 : Siddharth Pande...
摘要:本文將解釋引起這個(gè)錯(cuò)誤的內(nèi)在原因,檢測(cè)機(jī)制的內(nèi)部原理,提供導(dǎo)致這個(gè)錯(cuò)誤的共同行為,并給出修復(fù)這個(gè)錯(cuò)誤的解決方案。這一次過(guò)程稱為。這個(gè)程序設(shè)計(jì)為子組件拋出一個(gè)事件,而父組件監(jiān)聽(tīng)這個(gè)事件,而這個(gè)事件會(huì)引起父組件屬性值發(fā)生改變。 原文鏈接:Everything you need to know about the ExpressionChangedAfterItHasBeenCheckedE...
摘要:第一種方式是使用模塊加載器,如果你使用加載器的話,路由在加載子路由模塊時(shí)也是用的作為模塊加載器。還需注意的是,想要使用還需像這樣去注冊(cè)它你當(dāng)然可以在里使用任何標(biāo)識(shí),不過(guò)路由模塊使用標(biāo)識(shí),所以最好也使用相同。 原文鏈接:Here is what you need to know about dynamic components in?Angular showImg(https://se...
閱讀 1565·2021-10-25 09:44
閱讀 2926·2021-09-04 16:48
閱讀 1543·2019-08-30 15:44
閱讀 2475·2019-08-30 15:44
閱讀 1731·2019-08-30 15:44
閱讀 2816·2019-08-30 14:14
閱讀 2964·2019-08-30 13:00
閱讀 2143·2019-08-30 11:09