久久精品五月,日韩不卡视频在线观看,国产精品videossex久久发布 ,久久av综合

站長資訊網(wǎng)
最全最豐富的資訊網(wǎng)站

淺析Angular中的多級依賴注入設(shè)計

本篇文章帶大家進行Angular源碼學(xué)習(xí),介紹一下多級依賴注入設(shè)計,希望對大家有所幫助!

淺析Angular中的多級依賴注入設(shè)計

作為“為大型前端項目”而設(shè)計的前端框架,Angular 其實有許多值得參考和學(xué)習(xí)的設(shè)計,本系列主要用于研究這些設(shè)計和功能的實現(xiàn)原理。本文主要圍繞 Angular 中的最大特點——依賴注入,介紹 Angular 中多級依賴注入的設(shè)計?!鞠嚓P(guān)教程推薦:《angular教程》】

上一篇我們介紹了 Angular 中的Injectot注入器、Provider提供者,以及注入器機制。那么,在 Angular 應(yīng)用中,各個組件和模塊間又是怎樣共享依賴的,同樣的服務(wù)是否可以多次實例化呢?

組件和模塊的依賴注入過程,離不開 Angular 多級依賴注入的設(shè)計,我們來看看。

多級依賴注入

前面我們說過,Angular 中的注入器是可繼承、且分層的。

在 Angular 中,有兩個注入器層次結(jié)構(gòu):

  • ModuleInjector模塊注入器:使用@NgModule()@Injectable()注解在此層次結(jié)構(gòu)中配置ModuleInjector
  • ElementInjector元素注入器:在每個 DOM 元素上隱式創(chuàng)建

模塊注入器和元素注入器都是樹狀結(jié)構(gòu)的,但它們的分層結(jié)構(gòu)并不完全一致。

模塊注入器

模塊注入器的分層結(jié)構(gòu),除了與應(yīng)用中模塊設(shè)計有關(guān)系,還有平臺模塊(PlatformModule)注入器與應(yīng)用程序模塊(AppModule)注入器的分層結(jié)構(gòu)。

平臺模塊(PlatformModule)注入器

在 Angular 術(shù)語中,平臺是供 Angular 應(yīng)用程序在其中運行的上下文。Angular 應(yīng)用程序最常見的平臺是 Web 瀏覽器,但它也可以是移動設(shè)備的操作系統(tǒng)或 Web 服務(wù)器。

Angular 應(yīng)用在啟動時,會創(chuàng)建一個平臺層:

  • 平臺是 Angular 在網(wǎng)頁上的入口點,每個頁面只有一個平臺
  • 頁面上運行的每個 Angular 應(yīng)用程序,所共有的服務(wù)都在平臺內(nèi)綁定

一個 Angular 平臺,主要包括創(chuàng)建模塊實例、銷毀等功能:

@Injectable() export class PlatformRef {   // 傳入注入器,作為平臺注入器   constructor(private _injector: Injector) {}    // 為給定的平臺創(chuàng)建一個 @NgModule 的實例,以進行離線編譯   bootstrapModuleFactory<M>(moduleFactory: NgModuleFactory<M>, options?: BootstrapOptions):       Promise<NgModuleRef<M>> {}    // 使用給定的運行時編譯器,為給定的平臺創(chuàng)建一個 @NgModule 的實例   bootstrapModule<M>(       moduleType: Type<M>,       compilerOptions: (CompilerOptions&BootstrapOptions)|       Array<CompilerOptions&BootstrapOptions> = []): Promise<NgModuleRef<M>> {}    // 注冊銷毀平臺時要調(diào)用的偵聽器   onDestroy(callback: () => void): void {}    // 獲取平臺注入器   // 該平臺注入器是頁面上每個 Angular 應(yīng)用程序的父注入器,并提供單例提供程序   get injector(): Injector {}    // 銷毀頁面上的當前 Angular 平臺和所有 Angular 應(yīng)用程序,包括銷毀在平臺上注冊的所有模塊和偵聽器   destroy() {} }

實際上,平臺在啟動的時候(bootstrapModuleFactory方法中),在ngZone.run中創(chuàng)建ngZoneInjector,以便在 Angular 區(qū)域中創(chuàng)建所有實例化的服務(wù),而ApplicationRef(頁面上運行的 Angular 應(yīng)用程序)將在 Angular 區(qū)域之外創(chuàng)建。

在瀏覽器中啟動時,會創(chuàng)建瀏覽器平臺:

export const platformBrowser: (extraProviders?: StaticProvider[]) => PlatformRef =     createPlatformFactory(platformCore, 'browser', INTERNAL_BROWSER_PLATFORM_PROVIDERS);  // 其中,platformCore 平臺必須包含在任何其他平臺中 export const platformCore = createPlatformFactory(null, 'core', _CORE_PLATFORM_PROVIDERS);

使用平臺工廠(例如上面的createPlatformFactory)創(chuàng)建平臺時,將隱式初始化頁面的平臺:

export function createPlatformFactory(     parentPlatformFactory: ((extraProviders?: StaticProvider[]) => PlatformRef)|null, name: string,     providers: StaticProvider[] = []): (extraProviders?: StaticProvider[]) => PlatformRef {   const desc = `Platform: ${name}`;   const marker = new InjectionToken(desc); // DI 令牌   return (extraProviders: StaticProvider[] = []) => {     let platform = getPlatform();     // 若平臺已創(chuàng)建,則不做處理     if (!platform || platform.injector.get(ALLOW_MULTIPLE_PLATFORMS, false)) {       if (parentPlatformFactory) {         // 若有父級平臺,則直接使用父級平臺,并更新相應(yīng)的提供者         parentPlatformFactory(             providers.concat(extraProviders).concat({provide: marker, useValue: true}));       } else {         const injectedProviders: StaticProvider[] =             providers.concat(extraProviders).concat({provide: marker, useValue: true}, {               provide: INJECTOR_SCOPE,               useValue: 'platform'             });         // 若無父級平臺,則新建注入器,并創(chuàng)建平臺         createPlatform(Injector.create({providers: injectedProviders, name: desc}));       }     }     return assertPlatform(marker);   }; }

通過以上過程,我們知道 Angular 應(yīng)用在創(chuàng)建平臺的時候,創(chuàng)建平臺的模塊注入器ModuleInjector。我們從上一節(jié)Injector定義中也能看到,NullInjector是所有注入器的頂部:

export abstract class Injector {   static NULL: Injector = new NullInjector(); }

因此,在平臺模塊注入器之上,還有NullInjector()。而在平臺模塊注入器之下,則還有應(yīng)用程序模塊注入器。

應(yīng)用程序根模塊(AppModule)注入器

每個應(yīng)用程序有至少一個 Angular 模塊,根模塊就是用來啟動此應(yīng)用的模塊:

@NgModule({ providers: APPLICATION_MODULE_PROVIDERS }) export class ApplicationModule {   // ApplicationRef 需要引導(dǎo)程序提供組件   constructor(appRef: ApplicationRef) {} }

AppModule根應(yīng)用模塊由BrowserModule重新導(dǎo)出,當我們使用 CLI 的new命令創(chuàng)建新應(yīng)用時,它會自動包含在根AppModule中。應(yīng)用程序根模塊中,提供者關(guān)聯(lián)著內(nèi)置的 DI 令牌,用于為引導(dǎo)程序配置根注入器。

Angular 還將ComponentFactoryResolver添加到根模塊注入器中。此解析器存儲了entryComponents系列工廠,因此它負責(zé)動態(tài)創(chuàng)建組件。

模塊注入器層級

到這里,我們可以簡單地梳理出模塊注入器的層級關(guān)系:

  • 模塊注入器樹的最上層則是應(yīng)用程序根模塊(AppModule)注入器,稱作 root。

  • 在 root 之上還有兩個注入器,一個是平臺模塊(PlatformModule)注入器,一個是NullInjector()

因此,模塊注入器的分層結(jié)構(gòu)如下:

淺析Angular中的多級依賴注入設(shè)計

在我們實際的應(yīng)用中,它很可能是這樣的:

淺析Angular中的多級依賴注入設(shè)計

Angular DI 具有分層注入體系,這意味著下級注入器也可以創(chuàng)建它們自己的服務(wù)實例。

元素注入器

前面說過,在 Angular 中有兩個注入器層次結(jié)構(gòu),分別是模塊注入器和元素注入器。

元素注入器的引入

當 Angular 中懶加載的模塊開始廣泛使用時,出現(xiàn)了一個 issue:依賴注入系統(tǒng)導(dǎo)致懶加載模塊的實例化加倍。

在這一次修復(fù)中,引入了新的設(shè)計:注入器使用兩棵并行的樹,一棵用于元素,另一棵用于模塊

Angular 會為所有entryComponents創(chuàng)建宿主工廠,它們是所有其他組件的根視圖。

這意味著每次我們創(chuàng)建動態(tài) Angular 組件時,都會使用根數(shù)據(jù)(RootData)創(chuàng)建根視圖(RootView):

class ComponentFactory_ extends ComponentFactory<any>{   create(       injector: Injector, projectableNodes?: any[][], rootSelectorOrNode?: string|any,       ngModule?: NgModuleRef<any>): ComponentRef<any> {     if (!ngModule) {       throw new Error('ngModule should be provided');     }     const viewDef = resolveDefinition(this.viewDefFactory);     const componentNodeIndex = viewDef.nodes[0].element!.componentProvider!.nodeIndex;     // 使用根數(shù)據(jù)創(chuàng)建根視圖     const view = Services.createRootView(         injector, projectableNodes || [], rootSelectorOrNode, viewDef, ngModule, EMPTY_CONTEXT);     // view.nodes 的訪問器     const component = asProviderData(view, componentNodeIndex).instance;     if (rootSelectorOrNode) {       view.renderer.setAttribute(asElementData(view, 0).renderElement, 'ng-version', VERSION.full);     }     // 創(chuàng)建組件     return new ComponentRef_(view, new ViewRef_(view), component);   } }

該根數(shù)據(jù)(RootData)包含對elInjectorngModule注入器的引用:

function createRootData(     elInjector: Injector, ngModule: NgModuleRef<any>, 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è)計比較簡單。通過更改注入器層次結(jié)構(gòu),避免交錯插入模塊和組件注入器,從而導(dǎo)致延遲加載模塊的雙倍實例化。因為每個注入器都只有一個父對象,并且每次解析都必須精確地尋找一個注入器來檢索依賴項。

元素注入器(Element Injector)

在 Angular 中,視圖是模板的表示形式,它包含不同類型的節(jié)點,其中便有元素節(jié)點,元素注入器位于此節(jié)點上:

export interface ElementDef {   ...   // 在該視圖中可見的 DI 的公共提供者   publicProviders: {[tokenKey: string]: NodeDef}|null;   // 與 visiblePublicProviders 相同,但還包括位于此元素上的私有提供者   allProviders: {[tokenKey: string]: NodeDef}|null; }

默認情況下ElementInjector為空,除非在@Directive()@Component()providers屬性中進行配置。

當 Angular 為嵌套的 HTML 元素創(chuàng)建元素注入器時,要么從父元素注入器繼承它,要么直接將父元素注入器分配給子節(jié)點定義。

如果子 HTML 元素上的元素注入器具有提供者,則應(yīng)該繼承該注入器。否則,無需為子組件創(chuàng)建單獨的注入器,并且如果需要,可以直接從父級的注入器中解決依賴項。

元素注入器與模塊注入器的設(shè)計

那么,元素注入器與模塊注入器是從哪個地方開始成為平行樹的呢?

我們已經(jīng)知道,應(yīng)用程序根模塊(AppModule)會在使用 CLI 的new命令創(chuàng)建新應(yīng)用時,自動包含在根AppModule中。

當應(yīng)用程序(ApplicationRef)啟動(bootstrap)時,會創(chuàng)建entryComponent

const compRef = componentFactory.create(Injector.NULL, [], selectorOrNode, ngModule);

該過程會使用根數(shù)據(jù)(RootData)創(chuàng)建根視圖(RootView),同時會創(chuàng)建根元素注入器,在這里elInjectorInjector.NULL。

在這里,Angular 的注入器樹被分成元素注入器樹和模塊注入器樹,這兩個平行的樹了。

Angular 會有規(guī)律的創(chuàng)建下級注入器,每當 Angular 創(chuàng)建一個在@Component()中指定了providers的組件實例時,它也會為該實例創(chuàng)建一個新的子注入器。類似的,當在運行期間加載一個新的NgModule時,Angular 也可以為它創(chuàng)建一個擁有自己的提供者的注入器。

子模塊和組件注入器彼此獨立,并且會為所提供的服務(wù)分別創(chuàng)建自己的實例。當 Angular 銷毀NgModule或組件實例時,也會銷毀這些注入器以及注入器中的那些服務(wù)實例。

Angular 解析依賴過程

上面我們介紹了 Angular 中的兩種注入器樹:模塊注入器樹和元素注入器樹。那么,Angular 在提供依賴時,又會以怎樣的方式去進行解析呢。

在 Angular 種,當為組件/指令解析 token 獲取依賴時,Angular 分為兩個階段來解析它:

  • 針對ElementInjector層次結(jié)構(gòu)(其父級)
  • 針對ModuleInjector層次結(jié)構(gòu)(其父級)

其過程如下(參考多級注入器-解析規(guī)則):

  • 當組件聲明依賴項時,Angular 會嘗試使用它自己的ElementInjector來滿足該依賴。

  • 如果組件的注入器缺少提供者,它將把請求傳給其父組件的ElementInjector。

  • 這些請求將繼續(xù)轉(zhuǎn)發(fā),直到 Angular 找到可以處理該請求的注入器或用完祖先ElementInjector。

  • 如果 Angular 在任何ElementInjector中都找不到提供者,它將返回到發(fā)起請求的元素,并在ModuleInjector層次結(jié)構(gòu)中進行查找。

  • 如果 Angular 仍然找不到提供者,它將引發(fā)錯誤。

為此,Angular 引入一種特殊的合并注入器。

合并注入器(Merge Injector)

合并注入器本身沒有任何值,它只是視圖和元素定義的組合。

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

當 Angular 解析依賴項時,合并注入器則是元素注入器樹和模塊注入器樹之間的橋梁。當 Angular 嘗試解析組件或指令中的某些依賴關(guān)系時,會使用合并注入器來遍歷元素注入器樹,然后,如果找不到依賴關(guān)系,則切換到模塊注入器樹以解決依賴關(guān)系。

class ViewContainerRef_ implements ViewContainerData {   ...   // 父級試圖元素注入器的查詢   get parentInjector(): Injector {     let view = this._view;     let elDef = this._elDef.parent;     while (!elDef && view) {       elDef = viewParentEl(view);       view = view.parent!;     }      return view ? new Injector_(view, elDef) : new Injector_(this._view, null);   } }

解析過程

注入器是可繼承的,這意味著如果指定的注入器無法解析某個依賴,它就會請求父注入器來解析它。具體的解析算法在resolveDep()方法中實現(xiàn):

export function resolveDep(     view: ViewData, elDef: NodeDef, allowPrivateServices: boolean, depDef: DepDef,     notFoundValue: any = Injector.THROW_IF_NOT_FOUND): any {   //   //          mod1   //         /   //       el1   mod2   //           /   //         el2   //   // 請求 el2.injector.get(token)時,按以下順序檢查并返回找到的第一個值:   // - el2.injector.get(token, default)   // - el1.injector.get(token, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR) -> do not check the module   // - mod2.injector.get(token, default) }

如果是<child></child>這樣模板的根AppComponent組件,那么在 Angular 中將具有三個視圖:

<!-- HostView_AppComponent -->     <my-app></my-app> <!-- View_AppComponent -->     <child></child> <!-- View_ChildComponent -->     some content

依賴解析過程,解析算法會基于視圖層次結(jié)構(gòu),如圖所示進行:

淺析Angular中的多級依賴注入設(shè)計

如果在子組件中解析某些令牌,Angular 將:

  • 首先查看子元素注入器,進行檢查elRef.element.allProviders|publicProviders。

  • 然后遍歷所有父視圖元素(1),并檢查元素注入器中的提供者。

  • 如果下一個父視圖元素等于null(2),則返回到startView(3),檢查startView.rootData.elnjector(4)。

  • 只有在找不到令牌的情況下,才檢查startView.rootData module.injector( 5 )。

由此可見,Angular 在遍歷組件以解析某些依賴性時,將搜索特定視圖的父元素而不是特定元素的父元素。視圖的父元素可以通過以下方法獲得:

// 對于組件視圖,這是宿主元素 // 對于嵌入式視圖,這是包含視圖容器的父節(jié)點的索引 export function viewParentEl(view: ViewData): NodeDef|null {   const parentView = view.parent;   if (parentView) {     return view.parentNodeDef !.parent;   } else {     return null;   } }

總結(jié)

本文主要介紹了 Angular 中注入器的層級結(jié)構(gòu),在 Angular 中有兩棵平行的注入器樹:模塊注入器樹和元素注入器樹。

元素注入器樹的引入,主要是為了解決依賴注入解析懶加載模塊時,導(dǎo)致模塊的雙倍實例化問題。在元素注入器樹引入后,Angular 解析依賴的過程也有調(diào)整,優(yōu)先尋找元素注入器以及父視圖元素注入器等注入器的依賴,只有元素注入器中無法找到令牌時,才會查詢模塊注入器中的依賴。

贊(0)
分享到: 更多 (0)
?
網(wǎng)站地圖   滬ICP備18035694號-2    滬公網(wǎng)安備31011702889846號
久久精品五月,日韩不卡视频在线观看,国产精品videossex久久发布 ,久久av综合
日本成人中文字幕在线视频| 丝袜美腿亚洲一区二区图片| 最新国产精品视频| 亚洲一区不卡| 蜜臀精品一区二区三区在线观看| 午夜在线一区| 亚洲精一区二区三区| 日本v片在线高清不卡在线观看| 免费在线欧美视频| 中文视频一区| 巨乳诱惑日韩免费av| 免费欧美在线视频| 日本中文字幕视频一区| 日韩不卡一区二区三区| 国产精品入口久久| 久久一区国产| 伊人网在线播放| 中文字幕在线免费观看视频| 性欧美videohd高精| 99成人在线视频| 性一交一乱一区二区洋洋av| 日本中文字幕不卡| 国产精品久久久久久久久久妞妞| 欧美国产先锋| 日韩精品免费一区二区三区| 欧美福利一区| 国产成人免费精品| 精品在线播放| 日韩一区二区三区精品视频第3页| 日韩av中文字幕一区二区三区| 欧美三级第一页| 精品一区91| 午夜视频精品| 91成人在线精品视频| 国产精品毛片一区二区在线看| 99久久www免费| 天堂av在线一区| 国产精品一区2区3区| 欧美成人a交片免费看| 在线亚洲观看| 日韩精品视频网| 国产一区调教| 国产精品88久久久久久| 婷婷亚洲成人| 国产一区二区三区91| 欧洲毛片在线视频免费观看| 日韩一区二区三区免费视频| 中文字幕在线看片| 亚洲精品在线a| 丁香六月综合| 视频一区日韩精品| 亚洲国产成人二区| 亚洲我射av| av日韩中文| 视频一区中文字幕精品| 国精品产品一区| 免费观看日韩电影| 国产黄大片在线观看| 美女91精品| 久久精品伊人| 日av在线不卡| 国产网站在线| 日本免费一区二区视频| 亚洲v在线看| 911亚洲精品| 88xx成人免费观看视频库| 日欧美一区二区| 久久久久国产精品一区二区| 777久久精品| 婷婷激情图片久久| 国产精品多人| 欧美资源在线| 9999国产精品| 国产日韩欧美| 久久国产福利| 色婷婷亚洲mv天堂mv在影片| 无码日韩精品一区二区免费| 不卡av一区二区| 国产一区不卡| 日本午夜精品久久久| 午夜日本精品| 欧美羞羞视频| 麻豆高清免费国产一区| 蜜桃av一区二区在线观看| 四虎884aa成人精品最新| 日本aⅴ亚洲精品中文乱码| 自拍日韩欧美| 亚洲播播91| 国产盗摄——sm在线视频| 欧美午夜网站| 免费一区二区视频| 欧美另类专区| 欧美综合另类| 91一区二区三区四区| 国产精品探花在线观看| 中文无码日韩欧| 99pao成人国产永久免费视频| 日韩国产在线| 国产一区二区三区91| 老司机免费视频一区二区| 亚洲欧美专区| 蜜桃av一区二区| 午夜亚洲一区| 激情欧美丁香| 日韩精品一区二区三区免费观影| 国产一区二区视频在线看| 国产福利一区二区精品秒拍 | 色88888久久久久久影院| 国产精品视频一区二区三区四蜜臂| 免播放器亚洲一区| 日韩在线播放一区二区| 成人精品亚洲| 久久久久中文| 91tv亚洲精品香蕉国产一区| 国产高潮在线| 亚洲国产成人二区| 色综合五月天| 日韩a一区二区| 国产精品成人a在线观看| 国产中文字幕一区二区三区| 久久精品网址| 精品久久99| 激情不卡一区二区三区视频在线| 久久精品国产999大香线蕉| 国产精品一区二区中文字幕| 欧美精品福利| 视频一区中文字幕精品 | 国产精品一级| 久久国产人妖系列| 欧美亚洲tv| 久久超级碰碰| 国内不卡的一区二区三区中文字幕| 成人午夜在线| 日韩啪啪电影网| 日韩在线观看一区| 久久久人人人| 国产一区视频在线观看免费| 国产一区日韩一区| 欧美一区二区三区高清视频| 欧美另类专区| 亚洲精品欧美| 国产精东传媒成人av电影| 国产aⅴ精品一区二区三区久久| 水蜜桃久久夜色精品一区| 日韩在线不卡| 精品1区2区3区4区| 蜜桃视频在线观看一区| 91久久精品无嫩草影院| 国产精品第一| 桃色av一区二区| 99视频+国产日韩欧美| 深夜福利亚洲| 精品国产aⅴ| 欧美aa在线观看| 午夜国产精品视频| 中文字幕亚洲影视| 国产麻豆精品| 麻豆视频在线观看免费网站黄 | 三级精品视频| 石原莉奈在线亚洲二区| 日韩高清一级| 久久精品国产999大香线蕉| 成人久久久久| 视频一区国产视频| 日本少妇一区二区| 国产一区二区三区久久| 91久久国产| 欧美日韩夜夜| 91精品国产成人观看| 蜜臀av性久久久久蜜臀aⅴ四虎| 7777精品| 99久久亚洲精品蜜臀| 亚洲精品乱码久久久久久蜜桃麻豆| 国产精品久久久久久久久久白浆| 四季av一区二区凹凸精品| 一区在线免费观看| 国产精品久久久久久久久免费高清 | 成人日韩精品| 日本欧洲一区二区| 色一区二区三区四区| 亚洲作爱视频| 欧美激情日韩| 一本色道久久精品| 精品视频一区二区三区四区五区 | 久久激情综合网| 日韩美女一区二区三区在线观看| 性一交一乱一区二区洋洋av| 国产九九精品| 欧美精品黄色| 久久精品毛片| 一二三区精品| 亚洲精品永久免费视频| 亚洲18在线| 私拍精品福利视频在线一区| 欧美一级久久| 欧美aa在线观看| 97se亚洲| 欧美日韩视频| 精品亚洲a∨一区二区三区18| 欧美成人国产|