亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

[譯] 別再對(duì) Angular Modules 感到迷惑

LMou / 2523人閱讀

摘要:大多數(shù)初學(xué)者會(huì)認(rèn)為也有封裝規(guī)則,但實(shí)際上沒(méi)有。第二個(gè)規(guī)則是最后導(dǎo)入模塊的,會(huì)覆蓋前面導(dǎo)入模塊的。

原文鏈接:Avoiding common confusions with modules in Angular

Angular Modules 是個(gè)相當(dāng)復(fù)雜的話題,甚至 Angular 開(kāi)發(fā)團(tuán)隊(duì)在官網(wǎng)上寫了好幾篇有關(guān) NgModule 的文章教程。這些教程清晰的闡述了 Modules 的大部分內(nèi)容,但是仍欠缺一些內(nèi)容,導(dǎo)致很多開(kāi)發(fā)者被誤導(dǎo)。我看到很多開(kāi)發(fā)者由于不知道 Modules 內(nèi)部是如何工作的,所以經(jīng)常理解錯(cuò)相關(guān)概念,使用 Modules API 的姿勢(shì)也不正確。

本文將深度解釋 Modules 內(nèi)部工作原理,爭(zhēng)取幫你消除一些常見(jiàn)的誤解,而這些錯(cuò)誤我在 StackOverflow 上經(jīng)??吹接腥颂釂?wèn)。

模塊封裝

Angular 引入了模塊封裝的概念,這個(gè)和 ES 模塊概念很類似(注:ES Modules 概念可以查看 TypeScript 中文網(wǎng)的 Modules),基本意思是所有聲明類型,包括組件、指令和管道,只可以在當(dāng)前模塊內(nèi)部,被其他聲明的組件使用。比如,如果我在 App 組件中使用 A 模塊的 a-comp 組件:

@Component({
  selector: "my-app",
  template: `
      

Hello {{name}}

` }) export class AppComponent { }

Angular 編譯器就會(huì)拋出錯(cuò)誤:

Template parse errors: "a-comp" is not a known element

這是因?yàn)?App 模塊中沒(méi)有申明 a-comp 組件,如果我想要使用這個(gè)組件,就不得不導(dǎo)入 A 模塊,就像這樣:

@NgModule({
  imports: [..., AModule]
})
export class AppModule { }

上面描述的就是 模塊封裝。不僅如此,如果想要 a-comp 組件正常工作,得設(shè)置它為可以公開(kāi)訪問(wèn),即在 A 模塊的 exports 屬性中導(dǎo)出這個(gè)組件:

@NgModule({
  ...
  declarations: [AComponent],
  exports: [AComponent]
})
export class AModule { }

同理,對(duì)于指令和管道,也得遵守 模塊封裝 的規(guī)則:

@NgModule({
  ...
  declarations: [
    PublicPipe, 
    PrivatePipe, 
    PublicDirective, 
    PrivateDirective
  ],
  exports: [PublicPipe, PublicDirective]
})
export class AModule {}

需要注意的是,模塊封裝 原則不適用于在 entryComponents 屬性中注冊(cè)的組件,如果你在使用動(dòng)態(tài)視圖時(shí),像 譯 關(guān)于 Angular 動(dòng)態(tài)組件你需要知道的 這篇文章中所描述的方式去實(shí)例化動(dòng)態(tài)組件,就不需要在 A 模塊的 exports 屬性中去導(dǎo)出 a-comp 組件。當(dāng)然,還得導(dǎo)入 A 模塊。

大多數(shù)初學(xué)者會(huì)認(rèn)為 providers 也有封裝規(guī)則,但實(shí)際上沒(méi)有。在 非懶加載模塊 中申明的任何 provider 都可以在程序內(nèi)的任何地方被訪問(wèn),下文將會(huì)詳細(xì)解釋原因。

模塊層級(jí)

初學(xué)者最大的一個(gè)誤解就是認(rèn)為,一個(gè)模塊導(dǎo)入其他模塊后會(huì)形成一個(gè)模塊層級(jí),認(rèn)為該模塊會(huì)成為這些被導(dǎo)入模塊的父模塊,從而形成一個(gè)類似模塊樹(shù)的層級(jí),當(dāng)然這么想也很合理。但實(shí)際上,不存在這樣的模塊層級(jí)。因?yàn)?所有模塊在編譯階段會(huì)被合并,所以導(dǎo)入和被導(dǎo)入模塊之間不存在任何層級(jí)關(guān)系。

就像 組件 一樣,Angular 編譯器也會(huì)為根模塊生成一個(gè)模塊工廠,根模塊就是你在 main.ts 中,以參數(shù)傳入 bootstrapModule() 方法的模塊:

platformBrowserDynamic().bootstrapModule(AppModule);

Angular 編譯器使用 createNgModuleFactory 方法來(lái)創(chuàng)建該模塊工廠(注:可參考 L274 -> L60 -> L109 -> L153-L155 -> L50),該方法需要幾個(gè)參數(shù)(注:為清晰理解,不翻譯。最新版本不包括第三個(gè)依賴參數(shù)。):

module class reference

bootstrap components

component factory resolver with entry components

definition factory with merged module providers

最后兩點(diǎn)解釋了為何 providersentry components 沒(méi)有模塊封裝規(guī)則,因?yàn)榫幾g結(jié)束后沒(méi)有多個(gè)模塊,而僅僅只有一個(gè)合并后的模塊。并且在編譯階段,編譯器不知道你將如何使用 providers 和動(dòng)態(tài)組件,所以編譯器去控制封裝。但是在編譯階段的組件模板解析過(guò)程時(shí),編譯器知道你是如何使用組件、指令和管道的,所以編譯器能控制它們的私有申明。(注:providersentry components 是整個(gè)程序中的動(dòng)態(tài)部分 dynamic content,Angular 編譯器不知道它會(huì)被如何使用,但是模板中寫的組件、指令和管道,是靜態(tài)部分 static content,Angular 編譯器在編譯的時(shí)候知道它是如何被使用的。這點(diǎn)對(duì)理解 Angular 內(nèi)部工作原理還是比較重要的。)

讓我們看一個(gè)生成模塊工廠的示例,假設(shè)你有 AB 兩個(gè)模塊,并且每一個(gè)模塊都定義了一個(gè) provider 和一個(gè) entry component

@NgModule({
  providers: [{provide: "a", useValue: "a"}],
  declarations: [AComponent],
  entryComponents: [AComponent]
})
export class AModule {}

@NgModule({
  providers: [{provide: "b", useValue: "b"}],
  declarations: [BComponent],
  entryComponents: [BComponent]
})
export class BModule {}

根模塊 App 也定義了一個(gè) provider 和根組件 app,并導(dǎo)入 AB 模塊:

@NgModule({
  imports: [AModule, BModule],
  declarations: [AppComponent],
  providers: [{provide: "root", useValue: "root"}],
  bootstrap: [AppComponent]
})
export class AppModule {}

當(dāng)編譯器編譯 App 根模塊生成模塊工廠時(shí),編譯器會(huì) 合并 所有模塊的 providers,并只為合并后的模塊創(chuàng)建模塊工廠,下面代碼展示模塊工廠是如何生成的:

createNgModuleFactory(
    // reference to the AppModule class
    AppModule,

    // reference to the AppComponent that is used
    // to bootstrap the application
    [AppComponent],

    // module definition with merged providers
    moduleDef([
        ...

        // reference to component factory resolver
        // with the merged entry components
        moduleProvideDef(512, jit_ComponentFactoryResolver_5, ..., [
            ComponentFactory_,
            ComponentFactory_,
            ComponentFactory_
        ])

        // references to the merged module classes 
        // and their providers
        moduleProvideDef(512, AModule, AModule, []),
        moduleProvideDef(512, BModule, BModule, []),
        moduleProvideDef(512, AppModule, AppModule, []),
        moduleProvideDef(256, "a", "a", []),
        moduleProvideDef(256, "b", "b", []),
        moduleProvideDef(256, "root", "root", [])
]);

從上面代碼知道,所有模塊的 providersentry components 都將會(huì)被合并,并傳給 moduleDef() 方法,所以無(wú)論導(dǎo)入多少個(gè)模塊,編譯器只會(huì)合并模塊,并只生成一個(gè)模塊工廠。該模塊工廠會(huì)使用模塊注入器來(lái)生成合并模塊對(duì)象(注:查看 L232),然而由于只有一個(gè)合并模塊,Angular 將只會(huì)使用這些 providers,來(lái)生成一個(gè)單例的根注入器。

現(xiàn)在你可能想到,如果兩個(gè)模塊里定義了相同的 provider token,會(huì)發(fā)生什么?

第一個(gè)規(guī)則 則是導(dǎo)入其他模塊的模塊中定義的 provider 總是優(yōu)先勝出,比如在 AppModule 中也同樣定義一個(gè) a provider

@NgModule({
  ...
  providers: [{provide: "a", useValue: "root"}],
})
export class AppModule {}

查看生成的模塊工廠代碼:

moduleDef([
     ...
     moduleProvideDef(256, "a", "root", []),
     moduleProvideDef(256, "b", "b", []),
 ]);

可以看到最后合并模塊工廠包含 moduleProvideDef(256, "a", "root", []),會(huì)覆蓋 AModule 中定義的 {provide: "a", useValue: "a"}

第二個(gè)規(guī)則 是最后導(dǎo)入模塊的 providers,會(huì)覆蓋前面導(dǎo)入模塊的 providers。同樣,也在 BModule 中定義一個(gè) a provider

@NgModule({
  ...
  providers: [{provide: "a", useValue: "b"}],
})
export class BModule {}

然后按照如下順序在 AppModule 中導(dǎo)入 AModuleBModule

@NgModule({
  imports: [AModule, BModule],
  ...
})
export class AppModule {}

查看生成的模塊工廠代碼:

moduleDef([
     ...
     moduleProvideDef(256, "a", "b", []),
     moduleProvideDef(256, "root", "root", []),
 ]);

所以上面代碼已經(jīng)驗(yàn)證了第二條規(guī)則。我們?cè)?BModule 中定義了 {provide: "a", useValue: "b"},現(xiàn)在讓我們交換模塊導(dǎo)入順序:

@NgModule({
  imports: [BModule, AModule],
  ...
})
export class AppModule {}

查看生成的模塊工廠代碼:

moduleDef([
     ...
     moduleProvideDef(256, "a", "a", []),
     moduleProvideDef(256, "root", "root", []),
 ]);

和預(yù)想一樣,由于交換了模塊導(dǎo)入順序,現(xiàn)在 AModule{provide: "a", useValue: "a"} 覆蓋了 BModule{provide: "a", useValue: "b"}。

注:上文作者提供了 AppModule 被 @angular/compiler 編譯后的代碼,并針對(duì)編譯后的代碼分析多個(gè) modules 的 providers 會(huì)被合并。實(shí)際上,我們可以通過(guò)命令 yarn ngc -p ./tmp/tsconfig.json 自己去編譯一個(gè)小實(shí)例看看,其中,./node_modules/.bin/ngc@angular/compiler-cli 提供的 cli 命令。我們可以使用 ng new module 新建一個(gè)項(xiàng)目,我的版本是 6.0.5。然后在項(xiàng)目根目錄創(chuàng)建 /tmp 文件夾,然后加上 tsconfig.json,內(nèi)容復(fù)制項(xiàng)目根目錄的 tsconfig.json,然后加上一個(gè) module.ts 文件。module.ts 內(nèi)容包含根模塊 AppModule,和兩個(gè)模塊 AModuleBModule,AModule 提供 AService 、{provide:"a", value:"a"}{provide:"b", value:"b"} 服務(wù),而 BModule 提供 BService{provide: "b", useValue: "c"}AModuleBModule 按照先后順序?qū)敫K AppModule,完整代碼如下:
import {Component, Inject, Input, NgModule} from "@angular/core";
import "./goog"; // goog.d.ts 源碼文件拷貝到 /tmp 文件夾下
import "hammerjs";
import {platformBrowserDynamic} from "@angular/platform-browser-dynamic";
export class AService {
}
@NgModule({
  providers: [
    AService,
    {provide: "a", useValue: "a"},
    {provide: "b", useValue: "b"},
  ],
})
export class AModule {
}
export class BService {
}
@NgModule({
  providers: [
    BService,
    {provide: "b", useValue: "c"}
  ]
})
export class BModule {
}
@Component({
  selector: "app",
  template: `
    

{{name}}

` }) export class AppComp { name = "lx1036"; } export class AppService { } @NgModule({ imports: [AModule, BModule], declarations: [AppComp], providers: [ AppService, {provide: "a", useValue: "b"} ], bootstrap: [AppComp] }) export class AppModule { } platformBrowserDynamic().bootstrapModule(AppModule).then(ngModuleRef => console.log(ngModuleRef));
然后 yarn ngc -p ./tmp/tsconfig.json 使用 @angular/compiler 編譯這個(gè) module.ts 文件會(huì)生成多個(gè)文件,包括 module.jsmodule.factory.js。
先看下 module.js。AppModule 類會(huì)被編譯為如下代碼,發(fā)現(xiàn)我們?cè)?@NgModule 類裝飾器中寫的元數(shù)據(jù),會(huì)被賦值給 AppModule.decorators 屬性,如果是屬性裝飾器,會(huì)被賦值給 propDecorators 屬性:
var AppModule = /** @class */ (function () {
    function AppModule() {
    }
    AppModule.decorators = [
        { type: core_1.NgModule, args: [{
                    imports: [AModule, BModule],
                    declarations: [AppComp],
                    providers: [
                        AppService,
                        { provide: "a", useValue: "b" }
                    ],
                    bootstrap: [AppComp]
                },] },
    ];
    return AppModule;
}());
exports.AppModule = AppModule;
然后看下 module.factory.js 文件,這個(gè)文件很重要,本文關(guān)于模塊 providers 合并就可以從這個(gè)文件看出。該文件 AppModuleNgFactory 對(duì)象中就包含合并后的 providers,這些 providers 來(lái)自于 AppModule,AModule,BModule,并且 AppModule 中的 providers 會(huì)覆蓋其他模塊的 providersBModule 中的 providers 會(huì)覆蓋 AModuleproviders,因?yàn)?BModuleAModule 之后導(dǎo)入,可以交換導(dǎo)入順序看看發(fā)生什么。其中,?cmf 是 createNgModuleFactory,?mod 是 moduleDef,?mpd 是 moduleProvideDef,moduleProvideDef 第一個(gè)參數(shù)是 enum NodeFlags 節(jié)點(diǎn)類型,用來(lái)表示當(dāng)前節(jié)點(diǎn)是什么類型,比如 i0.?mpd(256, "a", "a", []) 中的 256 表示 TypeValueProvider 是個(gè)值類型。
Object.defineProperty(exports, "__esModule", { value: true });
var i0 = require("@angular/core");
var i1 = require("./module");

var AModuleNgFactory = i0.?cmf(
  i1.AModule,
  [],
  function (_l) {
    return i0.?mod([
      i0.?mpd(512, i0.ComponentFactoryResolver, i0.?CodegenComponentFactoryResolver, [[8, []], [3, i0.ComponentFactoryResolver], i0.NgModuleRef]),
      i0.?mpd(4608, i1.AService, i1.AService, []),
      i0.?mpd(1073742336, i1.AModule, i1.AModule, []),
      i0.?mpd(256, "a", "a", []),
      i0.?mpd(256, "b", "b", [])]
    );
  });
exports.AModuleNgFactory = AModuleNgFactory;

var BModuleNgFactory = i0.?cmf(
  i1.BModule,
  [],
  function (_l) {
    return i0.?mod([
      i0.?mpd(512, i0.ComponentFactoryResolver, i0.?CodegenComponentFactoryResolver, [[8, []], [3, i0.ComponentFactoryResolver], i0.NgModuleRef]),
      i0.?mpd(4608, i1.BService, i1.BService, []),
      i0.?mpd(1073742336, i1.BModule, i1.BModule, []),
      i0.?mpd(256, "b", "c", [])
    ]);
  });
exports.BModuleNgFactory = BModuleNgFactory;

var AppModuleNgFactory = i0.?cmf(
  i1.AppModule,
  [i1.AppComp], // AppModule 的 bootstrapComponnets 啟動(dòng)組件數(shù)據(jù)
  function (_l) {
    return i0.?mod([
      i0.?mpd(512, i0.ComponentFactoryResolver, i0.?CodegenComponentFactoryResolver, [[8, [AppCompNgFactory]], [3, i0.ComponentFactoryResolver], i0.NgModuleRef]),
      i0.?mpd(4608, i1.AService, i1.AService, []),
      i0.?mpd(4608, i1.BService, i1.BService, []),
      i0.?mpd(4608, i1.AppService, i1.AppService, []),
      i0.?mpd(1073742336, i1.AModule, i1.AModule, []),
      i0.?mpd(1073742336, i1.BModule, i1.BModule, []),
      i0.?mpd(1073742336, i1.AppModule, i1.AppModule, []),
      i0.?mpd(256, "a", "b", []),
      i0.?mpd(256, "b", "c", [])]);
  });
exports.AppModuleNgFactory = AppModuleNgFactory;
自己去編譯實(shí)踐下,會(huì)比只看文章的解釋,效率更高很多。
懶加載模塊

現(xiàn)在又有一個(gè)令人困惑的地方-懶加載模塊。官方文檔是這樣說(shuō)的(注:不翻譯):

Angular creates a lazy-loaded module with its own injector, a?child?of the root injector… So a lazy-loaded module that imports that shared module makes its own copy of the service.

所以我們知道 Angular 會(huì)為懶加載模塊創(chuàng)建它自己的注入器,這是因?yàn)?Angular 編譯器會(huì)為每一個(gè)懶加載模塊編譯生成一個(gè) 獨(dú)立的組件工廠。這樣在該懶加載模塊中定義的 providers 不會(huì)被合并到主模塊的注入器內(nèi),所以如果懶加載模塊中定義了與主模塊有著相同的 provider,則 Angular 編譯器會(huì)為該 provider 創(chuàng)建一份新的服務(wù)對(duì)象。

所以懶加載模塊也會(huì)創(chuàng)建一個(gè)層級(jí),但是注入器的層級(jí),而不是模塊層級(jí)。 在懶加載模塊中,導(dǎo)入的所有模塊同樣會(huì)在編譯階段被合并為一個(gè),就和上文非懶加載模塊一樣。

以上相關(guān)邏輯是在 @angular/router 包的 RouterConfigLoader 代碼里,該段展示了如何加載模塊和創(chuàng)建注入器:

export class RouterConfigLoader {

  load(parentInjector, route) {
    ...
    const moduleFactory$ = this.loadModuleFactory(route.loadChildren);
    return moduleFactory$.pipe(map((factory: NgModuleFactory) => {
          ...

          const module = factory.create(parentInjector);
        ...
     }));
  }

  private loadModuleFactory(loadChildren) {
    ...
    return this.loader.load(loadChildren)
  }
}

查看這行代碼:

const module = factory.create(parentInjector);

傳入父注入器來(lái)創(chuàng)建懶加載模塊新對(duì)象。

forRoot 和 forChild 靜態(tài)方法

查看官網(wǎng)是如何介紹的(注:不翻譯):

Add a CoreModule.forRoot?method that configures the core?UserService… Call?forRoot?only in the root application module,?AppModule

這個(gè)建議是合理的,但是如果你不理解為什么這樣做,最終會(huì)寫出類似下面代碼:

@NgModule({
  imports: [
    SomeLibCarouselModule.forRoot(),
    SomeLibCheckboxModule.forRoot(),
    SomeLibCloseModule.forRoot(),
    SomeLibCollapseModule.forRoot(),
    SomeLibDatetimeModule.forRoot(),
    ...
  ]
})
export class SomeLibRootModule {...}

每一個(gè)導(dǎo)入的模塊(如 CarouselModule,CheckboxModule 等等)不再定義任何 providers,但是我覺(jué)得沒(méi)理由在這里使用 forRoot,讓我們一起看看為何在第一個(gè)地方需要 forRoot

當(dāng)你導(dǎo)入一個(gè)模塊時(shí),通常會(huì)使用該模塊的引用:

@NgModule({ providers: [AService] })
export class A {}

@NgModule({ imports: [A] })
export class B {}

這種情況下,在 A 模塊中定義的所有 providers 都會(huì)被合并到主注入器,并在整個(gè)程序上下文中可用,我想你應(yīng)該已經(jīng)知道原因-上文中已經(jīng)解釋了所有模塊 providers 都會(huì)被合并,用來(lái)創(chuàng)建注入器。

Angular 也支持另一種方式來(lái)導(dǎo)入帶有 providers 的模塊,它不是通過(guò)使用模塊的引用來(lái)導(dǎo)入,而是傳一個(gè)實(shí)現(xiàn)了 ModuleWithProviders 接口的對(duì)象:

interface ModuleWithProviders { 
   ngModule: Type
   providers?: Provider[] 
}

上文中我們可以這么改寫:

@NgModule({})
class A {}

const moduleWithProviders = {
    ngModule: A,
    providers: [AService]
};

@NgModule({
    imports: [moduleWithProviders]
})
export class B {}

最好能在模塊對(duì)象內(nèi)使用一個(gè)靜態(tài)方法來(lái)返回 ModuleWithProviders,而不是直接使用 ModuleWithProviders 類型的對(duì)象,使用 forRoot 方法來(lái)重構(gòu)代碼:

@NgModule({})
class A {
  static forRoot(): ModuleWithProviders {
    return {ngModule: A, providers: [AService]};
  }
}

@NgModule({
  imports: [A.forRoot()]
})
export class B {}

當(dāng)然對(duì)于文中這個(gè)簡(jiǎn)單示例沒(méi)必要定義 forRoot 方法返回 ModuleWithProviders 類型對(duì)象,因?yàn)榭梢栽趦蓚€(gè)模塊內(nèi)直接定義 providers 或如上文使用一個(gè) moduleWithProviders 對(duì)象,這里僅僅也是為了演示效果。然而如果我們想要分割 providers,并在被導(dǎo)入模塊中分別定義這些 providers,那上文中的做法就很有意義了。

比如,如果我們想要為非懶加載模塊定義一個(gè)全局的 A 服務(wù),為懶加載模塊定義一個(gè) B 服務(wù),就需要使用上文的方法。我們使用 forRoot 方法為非懶加載模塊返回 providers,使用 forChild 方法為懶加載模塊返回 providers

@NgModule({})
class A {
  static forRoot() {
    return {ngModule: A, providers: [AService]};
  }
  static forChild() {
    return {ngModule: A, providers: [BService]};
  }
}

@NgModule({
  imports: [A.forRoot()]
})
export class NonLazyLoadedModule {}

@NgModule({
  imports: [A.forChild()]
})
export class LazyLoadedModule {}

因?yàn)榉菓屑虞d模塊會(huì)被合并,所以 forRoot 中定義的 providers 全局可用(注:包括非懶加載模塊和懶加載模塊),但是由于懶加載模塊有它自己的注入器,你在 forChild 中定義的 providers 只在當(dāng)前懶加載模塊內(nèi)可用(注:不翻譯)。

Please note that the names of methods that you use to return?ModuleWithProviders?structure can be completely arbitrary. The names?forChild?and?forRoot?I used in the examples above are just conventional names recommended by Angular team and used in the?RouterModuleimplementation.(注:即 forRoot 和 forChild 方法名稱可以隨便修改。)

好吧,回到最開(kāi)始要看的代碼:

@NgModule({
  imports: [
    SomeLibCarouselModule.forRoot(),
    SomeLibCheckboxModule.forRoot(),
    ...

根據(jù)上文的理解,就發(fā)現(xiàn)沒(méi)有必要在每一個(gè)模塊里定義 forRoot 方法,因?yàn)樵诙鄠€(gè)模塊中定義的 providers 需要全局可用,也沒(méi)有為懶加載模塊多帶帶準(zhǔn)備 providers(注:即本就沒(méi)有切割 providers 的需求,但你使用 forRoot 強(qiáng)制來(lái)切割)。甚至,如果一個(gè)被導(dǎo)入模塊沒(méi)有定義任何 providers,那代碼寫的就更讓人迷惑。

Use forRoot/forChild convention only for shared modules with providers that are going to be imported into both eager and lazy module?modules

還有一個(gè)需要注意的是 forRootforChild 僅僅是方法而已,所以可以傳參。比如,@angular/router 包中的 RouterModule,就定義了 forRoot 方法并傳入了額外的參數(shù):

export class RouterModule {
  static forRoot(routes: Routes, config?: ExtraOptions)

傳入的 routes 參數(shù)是用來(lái)注冊(cè) ROUTES 標(biāo)識(shí)(token)的:

static forRoot(routes: Routes, config?: ExtraOptions) {
  return {
    ngModule: RouterModule,
    providers: [
      {provide: ROUTES, multi: true, useValue: routes}

傳入的第二個(gè)可選參數(shù) config 是用來(lái)作為配置選項(xiàng)的(注:如配置預(yù)加載策略):

static forRoot(routes: Routes, config?: ExtraOptions) {
  return {
    ngModule: RouterModule,
    providers: [
      {
        provide: PreloadingStrategy,
        useExisting: config.preloadingStrategy ?
          config.preloadingStrategy :
          NoPreloading
      }

正如你所看到的,RouterModule 使用了 forRootforChild 方法來(lái)分割 providers,并傳入?yún)?shù)來(lái)配置相應(yīng)的 providers

模塊緩存

Stackoverflow 上有段時(shí)間有位開(kāi)發(fā)者提了個(gè)問(wèn)題,擔(dān)心如果在非懶加載模塊和懶加載模塊導(dǎo)入相同的模塊,在運(yùn)行時(shí)會(huì)導(dǎo)致該模塊代碼有重復(fù)。這個(gè)擔(dān)心可以理解,不過(guò)不必?fù)?dān)心,因?yàn)樗心K加載器會(huì)緩存所有加載的模塊對(duì)象。

當(dāng) SystemJS 加載一個(gè)模塊后會(huì)緩存該模塊,下次當(dāng)懶加載模塊又再次導(dǎo)入該模塊時(shí),SystemJS 模塊加載器會(huì)從緩存里取出該模塊,而不是執(zhí)行網(wǎng)絡(luò)請(qǐng)求,這個(gè)過(guò)程對(duì)所有模塊適用(注:Angular 內(nèi)置了 SystemJsNgModuleLoader 模塊加載器)。比如,當(dāng)你在寫 Angular 組件時(shí),從 @angular/core 包中導(dǎo)入 Component 裝飾器:

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

你在程序里多處引用了這個(gè)包,但是 SystemJS 并不會(huì)每次加載這個(gè)包,它只會(huì)加載一次并緩存起來(lái)。

如果你使用 angular-cli 或者自己配置 Webpack,也同樣道理,它只會(huì)加載一次并緩存起來(lái),并給它分配一個(gè) ID,其他模塊會(huì)使用該 ID 來(lái)找到該模塊,從而可以拿到該模塊提供的多種多樣的服務(wù)。

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

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

相關(guān)文章

  • [] 再對(duì) Angular 表單的 ControlValueAccessor 感到迷惑

    摘要:在里我們簡(jiǎn)單保存了對(duì)回調(diào)函數(shù)的引用,回調(diào)函數(shù)是由指令傳入的譯者注參考,只要每次組件值發(fā)生改變,就會(huì)觸發(fā)這個(gè)回調(diào)函數(shù)。 原文鏈接:Never again be confused when implementing ControlValueAccessor in Angular?forms showImg(https://segmentfault.com/img/bV7rR7?w=400...

    blastz 評(píng)論0 收藏0
  • @angular/forms 源碼解析之雙向綁定

    摘要:由于的屬性提供了令牌,并且該令牌指向的對(duì)象就是,所以構(gòu)造函數(shù)中注入的令牌包含的對(duì)象數(shù)組只有一個(gè)。這樣的構(gòu)造函數(shù)里就會(huì)包含一個(gè)對(duì)象,然后把這個(gè)傳給對(duì)象,最后注冊(cè)回調(diào),這樣以后值更新時(shí)就會(huì)運(yùn)行。整個(gè)包的設(shè)計(jì)也是按照這種數(shù)據(jù)流形式,并不復(fù)雜。 我們知道,Angular 的 @angular/forms 包提供了 NgModel 指令,來(lái)實(shí)現(xiàn)雙向綁定,即把一個(gè) JS 變量(假設(shè)為 name)與...

    yangrd 評(píng)論0 收藏0
  • 2017-08-23 前端日?qǐng)?bào)

    摘要:前端日?qǐng)?bào)精選免費(fèi)的計(jì)算機(jī)編程類中文書籍英文技術(shù)文檔看不懂看印記中文就夠了的內(nèi)部工作原理美團(tuán)點(diǎn)評(píng)點(diǎn)餐前后端分離實(shí)踐讓你的動(dòng)畫坐上時(shí)光機(jī)中文譯有多棒簡(jiǎn)書譯別再使用圖片輪播了掘金譯如何在中使用掘金個(gè)讓增長(zhǎng)成億美元公司的獨(dú)特方法眾成翻 2017-08-23 前端日?qǐng)?bào) 精選 FPB 2.0:免費(fèi)的計(jì)算機(jī)編程類中文書籍 2.0英文技術(shù)文檔看不懂?看印記中文就夠了!Virtual DOM 的內(nèi)部工作...

    lordharrd 評(píng)論0 收藏0
  • 別再被各種單位迷惑:px/em/rem

    摘要:引自手冊(cè)是相對(duì)長(zhǎng)度單位。所有未經(jīng)調(diào)整的瀏覽器都符合。特點(diǎn)是新增的一個(gè)相對(duì)單位,根,這個(gè)單位引起了廣泛關(guān)注。目前,除了及更早版本外,所有瀏覽器均已支持。對(duì)于不支持它的瀏覽器,應(yīng)對(duì)方法也很簡(jiǎn)單,就是多寫一個(gè)絕對(duì)單位的聲明。 在他處看到一篇好文章,想記錄在自己的學(xué)習(xí)筆記中,原文作者看到我轉(zhuǎn)載若是介意,聯(lián)系我立馬就刪除,附上原文鏈接:http://www.huolg.net/html5/htm...

    teren 評(píng)論0 收藏0
  • [] 關(guān)于 Angular 動(dòng)態(tài)組件你需要知道的

    摘要:第一種方式是使用模塊加載器,如果你使用加載器的話,路由在加載子路由模塊時(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...

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

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

0條評(píng)論

LMou

|高級(jí)講師

TA的文章

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