ForRoot (ROUTES)与 RouterModule.forChild (ROUTES)

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

医生并没有多大帮助:

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

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

我模糊地猜测,一个用于“ main”模块,另一个用于任何导入的模块(因为它们已经从主模块获得了可用的服务) ,但我实在想不出用例。

131178 次浏览

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

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

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

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

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

模块与供应商

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

@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

这和路由器模块有什么关系? 假设它们都使用相同的标记进行访问:

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

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

首先让我们提供一些信息:

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

就这样。

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

但是,当 共享(!)模块声明了 providers,并且该模块被惰性 还有app.module导入时,会发生什么情况呢?再说一次,就像我们说的,两个例子。

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

事实证明,我们可以声明一个不包含 providers:[]的共享模块,但仍然可以提供提供程序(抱歉:)

怎么做? 像这样:

enter image description here

注意,没有供应商。

但是

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

  • 当一个延迟模块导入具有 POV 服务的共享模块时会发生什么。

通过公约进入手册机制:

您将注意到,图片中的提供程序有 service1service2

这允许我们为延迟加载的模块导入 service2,为非延迟加载的模块导入 service1

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

另外,如果 app.module调用 forRoot(没有人调用 forchild) ,那也没关系,但是根注入器将只有 service1。(适用于所有应用程序)

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

它允许一个共享模块,能够 分开其 Different-与热切模块和惰性模块一起使用的提供程序- 通过 forRootforChild约定。我重复: < strong > 约定

就是这样。

等等! ! 没有一个字是关于单身的? ? 那我为什么要读 到处都是辛格尔顿?

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

它允许一个共享模块,能够 分开其 Different-与热切模块和惰性模块一起使用的提供程序- 通过 为了根和为了孩子

大会(! ! !)允许它是单例的-或者更精确地说-如果你不遵循惯例-你将 没有得到一个单例。
因此,如果只在 app.module中加载 forRoot,那么只能得到一个实例,因为只能在 app.module中调用 forRoot
顺便说一下,在这一点上,你可以忘记 forChild。延迟加载的模块不应该/不会调用 forRoot-所以你在单例 POV 中是安全的。

ForRoot 和 forChild 并不是一个不可破解的软件包——只是没有必要调用 Root,因为它显然只能在 app.module中加载,而不能提供延迟模块、拥有自己的服务、不能创建应该是单例的新服务。

这个约定为您提供了一个称为 forChild的很好的能力——使用“仅用于延迟加载模块的服务”。

下面是一个演示根提供程序产生正数,延迟加载模块产生负数。

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






]

为了便于理解,请将此视为路由器模块的实现。 延迟加载路由和公共路由必须分开处理,支持延迟加载

   @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)将从 app 模块(root 模块)注入到 root 注入器

Forchild-> 将添加与提供者(服务)的 routerModule,但作为公共服务(例如 RouterHistoryService)已经添加到根目录中 注入器,从延迟加载模块,我们可能能够使用它们,不需要再次添加,如果我们 Add 将创建两个实例。 但也可能有特别需要处理的服务 因此,在这种情况下,当调用 forchild 时,我们可以提供它们(例如 翻译: handleChildRouterService)

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

1)在根注入器 routerHistoryService 中,以前的路由是 “家/懒模块”

2)但是在带有新历史服务的延迟模块中,以前的路由为空

因此,它没有数据返回时,回按钮点击。 这就是为什么 routerModule 按照这种模式实现,以确保路由器模块只获得单个实例 在整个申请过程中

forRoot静态方法:

RouterModule.forRoot(routes)

ForRoot 静态方法是为应用程序配置根路由模块的方法。当您调用 RouterModule.forRoot (路由)时,您要求 Angular 在全局范围内实例化路由器类的一个实例。就像 Angular 创建了一个新的基本 AppModule 来导入所有的功能模块一样,它也提供了 AppRoutingModule 来导入所有的子路由。

在通过 Angular CLI 创建的新应用程序中,forRoot 方法实际上已经在应用程序路由内部使用。模组。在应用程序中,只需要使用一次 forRoot 方法。这是因为这个方法告诉 Angular 在引擎盖下实例化一个路由器类的实例,并且在你的应用程序中只能有一个路由器。ForRoot 静态方法是确保使用单例类的模式的一部分。

儿童路线:

RouterModule.forChild(routes)

当你使用 forChild 静态方法时,你基本上是在告诉 Angular,“应用程序中已经有一个可用的路由器实例了,所以请用这个实例注册所有这些路由。”ForChild 方法是您将调用的方法,用于在整个应用程序中注册路由,并且您将在子模块中使用它,即您创建的路由模块。

ForChild 静态方法很有用,因为它是 Angular 模块功能的核心部分,允许你在应用中维护关注点分离。

假设您想为应用程序中的用户设置创建一个新的功能模块,该功能将包含几个路由。不需要直接将这些路由添加到 AppRoutingModule 中,随着应用程序的增长,这些路由最终将变得不可维护,你可以使用 forChild 方法在应用程序中维护关注点分离。首先,创建一个新的 UserSettingsRoutingModule。

import { NgModule } from  '@angular/core';
import { Routes, RouterModule } from  '@angular/router';


import { UserSettingsComponent } from './user-settings.component';
import { UserProfileComponent } from  './user-profile/user-profile.component';


const  routes:  Routes  = [
{
path:  'settings',
component:  UserSettingsComponent,
children: [
{
path:  'profile',
component: UserProfileComponent
}
]
}
];


@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export  class  UserSettingsRoutingModule { }

请注意上面 forChild 方法的用法。因为已经使用了 forRoot 方法,所以只需要注册到已经实例化的应用程序路由器的路由。

现在您需要像下面这样创建您的 UserSettingsModule:

import { NgModule, CommonModule } from  '@angular/core';
import { UserSettingsRoutingModule } from  './user-settings-routing.module';


@NgModule({
imports: [
CommonModule,
UserSettingsRoutingModule
],
// Configure the rest of your module here
})
export class UserSettingsModule { }

就是这样!现在你需要做的就是将这个 UserSettingsModule 导入到你的根目录 AppModule 中,你的子路由器指向它们各自的组件,这些子路由器会在你的应用程序中配置。

我希望这个指南已经帮助你理解了 Angular Router 的静态方法 for Root 和 forChild 如何帮助你在你的应用程序中创建结构良好的路由。要了解更多信息,请查看 文件