RouterModule.forRoot(ROUTES)vs RouterModule.forChild(ROUTES)

时间:2016-11-08 23:19:19

标签: angular router

这两者之间的区别是什么?每个用例的用例是什么?

docs并非完全有用:

  

forRoot创建一个包含所有指令的模块   路由和路由器服务本身。

     

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

我的模糊猜测是,一个是主要的'模块和另一个用于任何导入的模块(因为他们已经可以从主模块获得服务),但我不能真正想到一个用例。

6 个答案:

答案 0 :(得分:95)

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

带提供商的模块

导入模块时,通常使用对模块类的引用:

@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}]
    };
}

答案 1 :(得分:21)

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

  

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

     

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

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

答案 2 :(得分:17)

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

首先让我们提一下信息:

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

故事结束。

这只是为了表明延迟加载的模块有自己的注入点(而不是非延迟加载的模块)。

但是当共享(!)模块声明providers并且该模块是由lazy app.module导入时会发生什么?再次,就像我们说的那样,有两个例子。

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

嗯,事实证明我们可以申报一个不会有providers:[]的共享模块,但仍然会提供刺激(抱歉:))

怎么样?像这样:

enter image description here

注意,没有提供者。

但是

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

  • 当懒惰模块将导入具有POV服务的共享模块时会发生什么? NOTHING。

按惯例输入手动机制:

您会注意到图片中的提供商有service1service2

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

是的,没有人阻止你在懒惰的模块中调用forRoot。但是你会有2个实例,因为app.module也应该这样做 - 所以不要在懒惰模块中执行它。

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

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

  

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

那就是它。

  

等待!! 关于单身人士没有一句话?那我为什么要读   单身人士到处都是?

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

  

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

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

forRoot和forChild不是一个牢不可破的软件包 - 它只是没有调用Root的意义,显然只会在app.module中加载而不提供懒惰模块的能力,有自己的服务,而不是创造新的服务 - 应该是单身人士。

这个约定为你提供了一个很好的能力forChild - 只为延迟加载的模块使用"服务"。

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

答案 3 :(得分:0)

如果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},



]

答案 4 :(得分:0)

将其视为路由器模块的实现,延迟加载的路由和普通路由必须以单独的方式处理,支持延迟加载

   @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,因此所有服务需要 公共角度路由 (示例routerHistoryService)将从应用模块(根模块)注入到根注入器

forchild-> 将添加有provider(services)的routerModule,但作为普通服务(例如 routerHistoryService)已添加到根目录 进样器,从延迟加载的模块中,我们可以使用它们,而无需再次添加 添加它将创建两个实例。 但是可能会有一些服务,特别是处理 子路线。因此,在这种情况下,我们可以给孩子打电话给他们(例如 :handleChildRouterService)

如果routerModule不采用这种模式,请考虑

1)在根注入器routerHistoryService的上一条路径是 “ home / lazymodule”

2)但在具有新历史记录服务的惰性模块中,以前的路由为空

,因此单击后退按钮时没有要返回的数据。 这就是为什么routerModule按照此模式实施以确保路由器模块仅获得单个实例的原因 在整个应用程序中

答案 5 :(得分:-1)

谢谢你罗伊。对我有帮助。该演示不再为我运行,因此我在这里进行了更新: https://stackblitz.com/edit/forroot-forchild-example?embed=1&file=src/app/app.component.ts