【问题标题】:RouterModule.forRoot(ROUTES) vs RouterModule.forChild(ROUTES)RouterModule.forRoot(ROUTES) 与 RouterModule.forChild(ROUTES)
【发布时间】:2017-03-22 17:43:41
【问题描述】:

这两者有什么区别,各自的用例是什么?

docs 并不是很有帮助:

forRoot 创建一个包含所有指令的模块,给定的 路由,以及路由器服务本身。

forChild 创建一个模块 包含所有指令和给定的路由,但不包括 路由器服务。

我的模糊猜测是一个用于“主”模块,另一个用于任何导入的模块(因为它们已经可以从主模块获得服务),但我真的想不出一个用例.

【问题讨论】:

  • 您能不能更具体地说明您不明白的地方?您所引用的引文从字面上告诉您有什么区别。
  • 我不明白使用 .forChild() 有什么意义。我什么时候需要没有服务的指令和路线?同时,请回答您从帖子中删除的问题...
  • 单个 Angular2 应用程序应该只有一个 RouterServiceforRoot 将初始化该服务并将其与一些路由配置一起注册到 DI,而 forChild 只会注册其他路由配置并告诉 Angular2 重用 forRoot 已创建的 RouterService
  • @HarryNinh:谢谢——这就是我想要的。但是,您什么时候想在初始注册之外注册其他路线?好像有点傻我猜没有办法动态创建路由。
  • 参见 Angular 路由器作者 victor 的 this

标签: angular router


【解决方案1】:

我强烈建议阅读这篇文章:

提供者模块

当你导入一个模块时,你通常使用对模块类的引用:

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

-----------------------------------

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

这样,在模块A 上注册的所有提供程序都将被添加到根注入器并可供整个应用程序使用。

但还有另一种方法可以向提供者注册模块,如下所示:

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

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

----------------------

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

这与前一个含义相同。

您可能知道延迟加载的模块有自己的注入器。因此,假设您想注册 AService 以供整个应用程序使用,但某些 BService 仅可用于延迟加载的模块。你可以像这样重构你的模块:

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

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

export const moduleWithProvidersForChild = {
    ngModule: A,
    providers: [BService]
};

------------------------------------------

@NgModule({
    imports: [moduleWithProvidersForRoot]
})
export class B
    
// lazy loaded module    
@NgModule({
    imports: [moduleWithProvidersForChild]
})
export class C

现在BService 将仅可用于子延迟加载模块,AService 将可用于整个应用程序。

您可以像这样将上面的内容重写为导出的模块:

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

    forChild() {
        return {
            ngModule: A,
            providers: [BService]
        }
    }
}

--------------------------------------

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

// lazy loaded module
@NgModule({
    imports: [A.forChild()]
})
export class C

###这与RouterModule有什么关系? 假设它们都使用相同的令牌进行访问:

export const moduleWithProvidersForRoot = {
    ngModule: A,
    providers: [{provide: token, useClass: AService}]
};

export const moduleWithProvidersForChild = {
    ngModule: A,
    providers: [{provide: token, useClass: BService}]
};

当您从延迟加载的模块请求token 时,使用单独的配置,您将按计划获得BService

RouterModule 使用ROUTES 令牌来获取特定于模块的所有路由。由于它希望特定于延迟加载模块的路由在该模块中可用(类似于我们的 BService),它对延迟加载的子模块使用不同的配置:

static forChild(routes: Routes): ModuleWithProviders {
    return {
        ngModule: RouterModule, 
        providers: [{provide: ROUTES, multi: true, useValue: routes}]
    };
}

【讨论】:

  • 也就是说,我们应该在功能模块中调用 forChild(),因为在根模块中已经调用了 forRoot(),并且添加了所有必要的服务。那么再次调用 forRoot() 会导致不可预知的状态吗?
  • @Wachburn,在延迟加载模块中调用 forRoot 将在延迟加载模块注入器中创建所有全局服务的新实例。是的,这将导致不可预测的结果。另请阅读这篇文章Avoiding common confusions with modules in Angular
  • @Willwsharp,为什么?
  • 我完全理解这一点。但是Routermodule.foRoot,VS Routermodule.forChild 有什么区别呢? forRoot &forChild 只是静态方法,它根据传递的值返回对象。那么在应用程序模块中,为什么我不能使用 forChild?为什么它的抛出错误?为什么我不能使用多个 forRoot?
  • 有时,直截了当地回答是件好事。大多数时候,信息过载会导致更多的混乱。
【解决方案2】:

为了便于理解,将其视为路由器模块的实现。 懒加载路由和普通路由分开处理,支持懒加载

   @NgModule({
      declarations: [
        
      ],
      exports: [],
    })
    export class RouteModule {
      static forRoot(): ModuleWithProviders<RouteModule> {
        return { ngModule: RouteModule, providers: [routerHistoryService,handleCommonRoutesService] };
      }
    
      static forChild(): ModuleWithProviders<RouteModule> {
        return {
          ngModule: RouteModule, providers: [handleChildRouterService]
        };
      }
    }
    
    

forroot -> 将添加 routerModule 和 providers (services) ,所以所有的服务都需要 普通角路由 (例如 routerHistoryService )将从应用程序模块(根模块)注入到根注入器

forchild -> 将添加 routerModule 和提供者(服务),但作为公共服务(例如 routerHistoryService ) 已添加到 root 注入器,从延迟加载的模块中,我们可以使用它们而无需再次添加,如果我们 添加它将创建两个实例。 但可能有服务,特别需要处理 子路线。所以在这种情况下,当我们调用 forchild 时,我们可以提供它们(例如 :handleChildRouterService)

如果 routerModule 如果我们没有实现 forRoot 和 forChild 考虑下面的场景

1) 在根注入器 routerHistoryService 之前的路由是 “家庭/懒惰模块”

2)但是在带有新历史服务的惰性模块中,之前的路由是空的

所以当点击后退按钮时它没有数据可以返回。 这就是为什么 routerModule 遵循这种模式来确保路由器模块只有一个实例 在整个应用程序中

【讨论】:

    【解决方案3】:

    我认为答案是正确的,但我认为缺少一些东西。
    缺少的是“为什么以及它解决了什么?”。
    好的,让我们开始吧。

    首先让我们提一些信息:

    所有模块都可以访问根服务。
    因此,即使是延迟加载的模块也可以使用app.module 中提供的服务。
    如果延迟加载的模块会为自己提供应用程序模块已经提供的服务,会发生什么?将有 2 个实例。
    这不是问题但有时是
    我们该如何解决呢?只是不要将具有该提供程序的模块导入延迟加载的模块。

    故事结束。

    这个 ^ 只是为了表明延迟加载的模块有自己的注入点(相对于非延迟加载的模块)。

    但是当 shared(!) 模块声明了 providers,并且该模块由惰性 app.module 导入时会发生什么?同样,就像我们说的,两个实例。

    那么我们如何在共享模块 POV 中解决这个问题?我们需要一种方式来使用providers:[]!为什么?因为它们将被自动导入到使用lazy和app.module中,我们不希望这样,因为我们看到每个实例都有不同的实例。

    好吧,事实证明我们可以声明一个共享模块,它不会有providers:[],但仍然会提供提供者(抱歉:))

    怎么样?像这样:

    注意,没有提供者。

    但是

    • 当 app.module 导入带有服务 POV 的共享模块时会发生什么?什么都没有。

    • 当一个惰性模块导入带有 POV 服务的共享模块时会发生什么?什么都没有。

    通过约定进入手动机制:

    你会注意到图片中的提供者有service1service2

    这允许我们为延迟加载的模块导入service2,为非延迟加载的模块导入service1。 (咳...路由器....咳)

    顺便说一句,没有人阻止您在惰性模块中调用forRoot。但是您将有 2 个实例,因为 app.module 也应该这样做 - 所以不要在惰性模块中这样做。

    另外——如果app.module 调用forRoot(并且没有人调用forchild)——这很好,但根注入器将只有service1。 (适用于所有应用程序)

    那么我们为什么需要它呢?我想说:

    它允许一个共享模块,能够拆分它的 与急切模块和惰性模块一起使用的不同提供程序 - 通过forRootforChild 约定。我再说一遍:约定

    就是这样。

    WAIT !!没有一个关于单身的词??那我为什么要读书 到处都是单身?

    嗯-它隐藏在上面的句子中^

    它允许一个共享模块,能够拆分它的 与急切模块和惰性模块一起使用的不同提供程序 - 通过 forRoot 和 forChild

    约定 (!!!) 允许它是单例的 - 或者更准确地说 - 如果您不遵守约定 - 您将 NOT 获得单身人士。
    因此,如果您只在 app.module 中加载 forRoot ,那么您只会得到一个实例,因为您只应在 app.module 中调用 forRoot 它。
    顺便说一句-此时您可以忘记forChild。延迟加载的模块不应该/不会调用 forRoot - 所以你在单例的 POV 中是安全的。

    forRoot 和 forChild 不是一个牢不可破的包 - 只是没有必要调用 Root 显然只会在 app.module 中加载而不赋予惰性模块的能力,拥有自己的服务,而不创建新服务-应该是单身。

    这个约定给你一个很好的能力,叫做forChild - 使用“仅用于延迟加载模块的服务”。

    Here is a demo Root providers yields positive numbers , lazy loaded modules yields negative numbers.

    【讨论】:

    • 1.这里的 POV 是什么? 2. stackblitz 不再起作用了。
    • @NicholasK 这个堆栈闪电战总是在一段时间后把事情搞砸。我会尝试上传新版本
    • @RoyiNamir 正如您提到的延迟加载模块也访问根模块提供者,那么为什么我们需要在延迟模块中再次添加它们?即使在共享模块的情况下,如果我们在根模块中添加了,那么为什么我会在延迟加载的模块中再次添加共享模块?任何不在根目录中但在惰性模块中需要的提供程序,我可以在惰性模块中单独添加它们。正确的?那么为什么需要它。仍然对它们的用法感到困惑。
    【解决方案4】:

    如果 appRoutes 包含站点中各种功能的路径(管理员、用户、书籍)并且我们想将它们分开,我们可以简单地这样做:

     imports: [
        BrowserModule, HttpModule,
        AppRoutingModule,
        RouterModule.forRoot(categoriesRoutes),
        RouterModule.forRoot(auteursRoutes),
      ],
    

    对于路线:

    const auteursRoutes:Routes=[
      {path:'auteurs/ajouter',component:CreerAuteurComponent},
    ]
    const categoriesRoutes: Routes = [
    
    
      {path:'categories/consulter',component:ConsultercategoriesComponent},
      {path:'categories/getsouscategoriesbyid/:id',component:GetsouscategoriesbyIDComponent},
      {path:'categories/ajout',component:CreerCategorieComponent},
     {path:'categories/:id',component:ModifiercategorieComponent},
     {path:'souscategories/ajout/:id',component:AjoutersouscategorieComponent},
     {path:'souscategories/lecture/:id1',component:SouscategoriesComponent},
     {path:'souscategories/modifier/:id1',component:ModifiersupprimersouscategorieComponent},
      {path:'uploadfile',component:UploadfileComponent},
      {path:'categories',component:ConsultercategoriesComponent},
    
    
    
    ]
    

    【讨论】:

    • 你不应该给forRoot打电话两次
    【解决方案5】:

    文档清楚地说明了这种区别的目的是什么:https://angular.io/docs/ts/latest/guide/ngmodule.html#!#core-for-root

    仅在根应用程序模块 AppModule 中调用 forRoot。在任何其他模块中调用它,特别是在延迟加载的模块中,这与意图相反,并且可能会产生运行时错误。

    记得导入结果;不要将其添加到任何其他 @NgModule 列表中。

    每个应用程序都有一个起点(根),主路由服务应使用forRoot 初始化,而特定“子”功能的路由应额外注册为forChild。它对于不必在应用程序启动时加载的子模块和延迟加载模块非常有用,正如@Harry Ninh 所说,他们被告知重用 RouterService 而不是注册新服务,这可能会导致运行时错误。

    【讨论】:

    猜你喜欢
    • 2017-01-21
    • 1970-01-01
    • 1970-01-01
    • 2015-04-02
    • 1970-01-01
    • 2014-05-24
    • 2023-03-03
    • 2022-12-02
    • 1970-01-01
    相关资源
    最近更新 更多