- forRoot创建一个模块,其中包含所有指令、给定的路由以及路由服务本身。 - forChild创建一个模块,其中包含所有指令和给定的路由,但不包括路由服务。
我初步猜测其中一个是用于“主”模块,另一个则用于任何导入的模块(因为它们已经可以从主模块中获取到该服务),但我无法想出具体的使用场景。
我强烈建议阅读这篇文章:
当你导入一个模块时,通常使用模块类的引用:
@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}]
};
}
我认为答案是正确的,但是我认为有些东西缺失了。
缺失的是“为什么以及它解决了什么问题?”
好的,让我们开始吧。
首先,让我们提供一些信息:
所有模块都可以访问根服务。
因此,即使是惰性加载的模块也可以使用在app.module
中提供的服务。
如果一个惰性加载的模块将自己提供给应用程序模块已经提供的服务,会发生什么?就会有2个实例。
这不是问题,但有时候会是问题。
我们如何解决它?简单地说,不要在惰性加载的模块中导入具有该提供程序的模块。
故事结束。
这^只是为了说明惰性加载的模块有它们自己的注入点(与非惰性加载的模块相对)。
但是,当一个共享(!)模块声明了providers
,并且该模块被惰性加载和app.module
导入时会发生什么?同样,像我们所说的那样,会有两个实例。
那么,我们如何从共享模块的角度解决这个问题?我们需要一种方法不使用providers:[]
!为什么?因为它们将自动导入到消费惰性和app.module中,我们不希望这样,因为我们看到每个实例都有一个不同的实例。
好吧,事实证明我们可以声明一个没有providers:[]
但仍然提供提供程序的共享模块(抱歉:))
怎么做?像这样:
注意,没有提供程序。
但是
现在,当app.module以服务的POV导入共享模块时会发生什么?什么也不会发生。
当一个懒加载模块从服务的角度导入共享模块时,现在会发生什么?什么都不会发生。
通过约定进入手动机制:
您会注意到图片中的提供程序具有service1
和service2
这使我们可以为惰性加载的模块导入service2
,而为非惰性模块导入service1
。(咳咳...路由器...咳咳)
顺便说一句,没人阻止您在惰性加载模块中调用forRoot
。但是,由于app.module
也应该这样做,因此您将拥有2个实例,所以不要在惰性模块中这样做。
此外 - 如果app.module
调用forRoot
(没有人调用forchild
)-那就没问题了,但根注入器只有service1
。(对所有应用程序可用)
那么我们为什么需要它呢?我会说:
它允许共享模块通过
forRoot
和forChild
约定将其不同的提供程序拆分为可用于急切模块和惰性模块。我重复一遍:约定
就这样。
等等!!完全没有单例的提到??那么我为什么到处都看到单例?
好吧 - 它隐藏在上面的句子中 ^
约定(!!!)允许它成为单例-更确切地说,如果不遵循约定-将不会得到单例。它允许共享模块,通过
forRoot
和forChild
将其不同的提供程序拆分为可用于急切模块和惰性模块。
app.module
中加载forRoot
,则只会得到一个实例,因为您只应该在app.module
中调用forRoot
。forChild
。延迟加载的模块不应/不会调用forRoot
-因此,在单例POV方面是安全的。
forRoot
和forChild
不是一个不可分割的包-只是没有必要调用明显仅在app.module
中加载的forRoot
,而不给予延迟加载的模块拥有自己的服务的能力,而无需创建应该是单例的新服务。
这个约定给了你一个称为forChild
的好功能-以消耗“仅适用于延迟加载模块”的服务。
文档清晰地说明了此处区分的目的:https://angular.io/docs/ts/latest/guide/ngmodule.html#!#core-for-root
只在根应用程序模块 AppModule 中调用 forRoot。在任何其他模块(尤其是延迟加载模块)中调用它,都与意图相反,并可能导致运行时错误。
记得导入结果;不要将其添加到任何其他 @NgModule 列表中。
每个应用程序都有且仅有一个起始点(根),其中主路由服务应该使用 forRoot
进行初始化,而特定“子”功能的路由应该使用 forChild
进行注册。这对于子模块和延迟加载模块非常有用,它们不必在应用程序启动时加载,并且如@Harry Ninh所说,它们被告知重用 RouterService 而不是注册新服务,这可能会导致运行时错误。
forRoot
静态方法:
RouterModule.forRoot(routes)
forRoot静态方法是配置应用程序的根路由模块的方法。当您调用RouterModule.forRoot(routes)时,您正在请求Angular全局实例化Router类的一个实例。就像Angular会创建一个新的基本AppModule来导入所有功能模块一样,它还提供了AppRoutingModule来导入所有子路由。
在您通过Angular CLI创建的新应用程序中,forRoot方法实际上已经在app-routing.module.ts中被使用了。在您的应用程序中,您只需要使用forRoot方法一次。这是因为此方法在幕后告诉Angular实例化一个Router类的实例,而在您的应用程序中只能有一个路由器。 forRoot静态方法是确保您使用单例类的模式的一部分。
子路由:
RouterModule.forChild(routes)
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静态方法forRoot和forChild如何帮助你创建一个良好结构化的路由。想要更多信息,请查看文档。
如果appRoutes包含网站中各种功能的路径(管理员crud、用户crud、图书crud),我们想要将它们分开,我们可以简单地这样做:
如果appRoutes包含网站中各种功能的路径(管理员crud、用户crud、图书crud),我们想要将它们分开,我们可以简单地这样做:
imports: [
BrowserModule, HttpModule,
AppRoutingModule,
RouterModule.forRoot(categoriesRoutes),
RouterModule.forRoot(auteursRoutes),
],
And for routes :
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
。 - Oram @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,因此所有常见的Angular路由所需的服务(例如routerHistoryService)将从应用程序模块(根模块)注入到根注入器中。
forChild -> 将添加带有提供者(服务)的routerModule,但是由于常见服务(例如routerHistoryService)已经添加到了根注入器中,因此从惰性加载的模块中可以使用它们,无需再次添加,如果添加将创建两个实例。但是可能需要处理子路由的特定服务。在这种情况下,当调用forChild时,我们可以提供它们(例如:handleChildRouterService)。
如果我们不实现forRoot和forChild,则考虑以下情况:
1)在根注入器中,routerHistoryService的先前路由为“home/lazymodule”
2)但是在惰性模块中,具有新历史记录服务的先前路由为null
因此,在单击返回按钮时,它没有数据可以返回。这就是为什么routerModule按照以下模式实施以确保整个应用程序中只有一个routerModule实例。
RouterService
。调用forRoot
会初始化这个服务,并将它和路由配置一起注册到DI中,而调用forChild
则只会注册额外的路由配置,并告诉Angular2重用forRoot
创建的RouterService
。 - Harry Ninh