RouterModule.forRoot(ROUTES)与RouterModule.forChild(ROUTES)


111

两者之间有什么区别,每种情况都有哪些用例?

文档不是完全有帮助:

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

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

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


1
您能更具体地了解您不了解的内容吗?您所引用的报价从字面上告诉您区别是什么。
jonrsharpe

2
我不明白使用.forChild()的意义是什么。我什么时候需要没有服务的指令和路线?在此期间,请回答您从帖子中删除的问题...
VSO

17
RouterService单个Angular2应用程序应该只有一个。forRoot将初始化服务,并与一些航线配置一起注册DI,而forChild只能注册其他途径CONFIGS告诉Angular2重用RouterServiceforRoot创造。
哈里·宁

@HarryNinh:谢谢-那就是我想要的。但是,您什么时候要在初始注册之外注册其他路线?看起来有点傻。我猜没有办法动态创建路由。
VSO

1
角度路由器作者victor 看到了这一点
尼克希尔·梅塔

Answers:


123

我强烈建议您阅读本文:

提供者模块

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

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

3
因此换句话说,我们应该在功能模块中调用forChild(),因为在根模块中已经调用了forRoot(),并且添加了所有必需的服务。那么再次调用forRoot()会导致不可预测的状态吗?
Wachburn '17

7
@Wachburn,调用forRoot延迟加载的模块将在延迟加载的模块注入器中创建所有全局服务的新实例。是的,这将导致不可预测的结果。另请阅读这篇文章避免在Angular中与模块发生常见的混乱
Max Koretskyi,2017年

1
@Willwsharp,为什么?
Max Koretskyi

1
我完全明白这一点。但是,Routermodule.foRoot和VS Routermodule.forChild有什么区别?forRoot&forChild只是静态方法,它根据传递的值给出对象作为回报。所以在应用程序模块中,为什么我不能使用forChild?为什么会抛出错误?为什么我不能使用多个forRoot?
Subhadeep

2
有时,直接回答是很好的。大多数时候,信息过载会导致更多的混乱。
AdépòjùOlúwáségun

33

我认为答案是正确的,但我认为缺少某些内容。
缺少的是“为什么以及如何解决?”。
好,让我们开始吧。

首先让我们提一些信息:

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

故事结局。

此^只是为了表明延迟加载的模块具有自己的注入点(与非延迟加载的模块相反)。

但是,当shared(!)模块已经声明providers并且该模块由lazy 导入时会发生什么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约定。我再说一遍:约定

而已。

等待!关于单身人士一个字?那为什么我到处都读单例呢?

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

它允许共享模块通过forRoot和forChild 拆分其不同的提供程序以与渴望的模块和惰性模块一起使用

公约(!!!)允许它是单-或者更准确地说-如果你不遵循约定-你不是得到一个单例。
因此,如果仅加载forRootin app.module ,则只会得到一个实例,因为您只应在中调用forRootapp.module
顺便说一句-在这一点上,您可以忘记forChild。延迟加载的模块不应/不会调用forRoot-因此您可以安全地使用单例POV。

forRoot和forChild并不是一个牢不可破的程序包-只是没有必要调用Root,显然,Root只会在app.module 不提供惰性模块功能,拥有自己的服务,而无需创建新服务的情况下加载-singleton。

该约定为您提供了一种很好的功能forChild-使用“仅针对延迟加载的模块的服务”。

这是一个演示根提供商产生正数,延迟加载的模块产生负数。


1.什么是POV?2.堆栈闪电不再起作用。
Nicholas K

@NicholasK一段时间后,这个堆叠闪电总是弄乱事情。我将尝试上传新版本
Royi Namir

26

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

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

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

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


2
这些文件似乎已被移动,v2.angular.io
docs /

1

如果appRoutes包含站点中各种功能的路径(admin crud,user crud,book crud),并且我们想将它们分开,则可以简单地做到这一点:

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



]
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.