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

VSO*_*VSO 88 router angular

这两者之间的区别是什么?每种用例有什么用处?

文档是不完全帮助:

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

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

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

Max*_*kyi 111

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

提供者模块

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

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

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

@NgModule({
    imports: [A]
})
export class B
Run Code Online (Sandbox Code Playgroud)

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

但是还有另一种方法可以向这样的提供者注册模块:

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

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

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

@NgModule({
    imports: [moduleWithProviders]
})
export class B
Run Code Online (Sandbox Code Playgroud)

这与前一个含义相同.

您可能知道延迟加载的模块有自己的注入器.因此,假设您希望注册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
Run Code Online (Sandbox Code Playgroud)

现在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
Run Code Online (Sandbox Code Playgroud)

这与RouterModule有什么关系?

假设它们都使用相同的令牌访问:

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

export const moduleWithProvidersForChild = {
    ngModule: A,
    providers: [{provide: token, useClass: BService}]
};
Run Code Online (Sandbox Code Playgroud)

当您token从延迟加载的模块请求时,您可以BService按照计划获得单独的配置.

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

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

  • @Wachburn,在延迟加载的模块中调用`forRoot`将在延迟加载的模块注入器中创建所有全局服务的新实例.是的,这将导致不可预测的结果.另请阅读本文[避免与Angular模块的常见混淆](https://blog.angularindepth.com/avoiding-common-confusions-with-modules-in-angular-ada070e6891f) (4认同)
  • 有时,直接回答问题是好的。大多数时候信息过载会导致更多的混乱。 (4认同)
  • 所以换句话说,我们应该在功能模块中调用 forChild() 因为 forRoot() 已经在根模块中被调用并且所有必要的服务都被添加了。那么再次调用 forRoot() 会导致不可预知的状态吗? (3认同)
  • 我完全理解这一点。但是 Routermodule.foRoot 和 Routermodule.forChild 之间有什么区别?forRoot 和 forChild 只是静态方法,它根据传递的值返回对象。那么在应用程序模块中,为什么我无法使用 forChild?为什么它会抛出错误?为什么我不能使用多个 forRoot? (2认同)

Mar*_*cin 25

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

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

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

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

  • 这些文件似乎被移动了,https://v2.angular.io/docs/ts/latest/guide/ngmodule.html# !#core-for-root (2认同)

Roy*_*mir 25

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

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

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

故事结局.

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

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

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

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

怎么样 ?像这样 :

在此输入图像描述

请注意,没有提供者.

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

  • 现在,当懒惰模块将导入具有POV服务的共享模块时会发生什么?没有.

通过约定输入手动机制:

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

这允许我们导入service2延迟加载的模块和service1非惰性模块.(咳嗽......路由器......咳嗽)

顺便说一下,没有人阻止你forRoot在一个懒惰的模块中打电话.但你将有2个实例,因为app.module也应该这样做 - 所以不要在懒惰的模块中这样.

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

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

它允许共享模块能够分割其不同的提供者,以便与渴望的模块和惰性模块一起使用 - 通过forRootforChild约定.我再说一遍:惯例

而已.

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

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

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

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

forRoot和forChild不是一个牢不可破的包-它只是没有要求根的点,这显然只会加载app.module 没有给懒惰模块的能力,有自己的服务,而无需创建新的服务-这,应该成为的-singleton.

这个约定为你提供了一个很好的能力forChild- 消耗"仅用于延迟加载的模块的服务".

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


小智 5

静态forRoot方法:

RouterModule.forRoot(routes)
Run Code Online (Sandbox Code Playgroud)

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

在您通过 Angular CLI 创建的新应用程序中,forRoot 方法实际上已经在 app-routing.module.ts 内部使用。在您的应用程序中,您只想使用 forRoot 方法一次。这是因为此方法告诉 Angular 在底层实例化 Router 类的实例,并且您的应用程序中只能有一个路由器。forRoot 静态方法是确保您使用单例类的模式的一部分。

儿童路线:

RouterModule.forChild(routes)
Run Code Online (Sandbox Code Playgroud)

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

forChild 静态方法非常有用,因为它通过允许您在应用程序中保持关注点分离来发挥 Angular 模块功能的核心部分。

假设您想在应用程序中为用户设置创建一个新的功能模块,并且该功能将包含一些路由。您可以使用 forChild 方法在应用程序中保持关注点分离,而不是直接将这些路由添加到 AppRoutingModule 中(随着应用程序的增长,这最终会变得站不住脚)。首先,创建一个新的 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 { }
Run Code Online (Sandbox Code Playgroud)

注意上面 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 { }
Run Code Online (Sandbox Code Playgroud)

现在你就拥有了!现在您需要做的就是将此 UserSettingsModule 导入到您的根 AppModule 中,并且指向其各自组件的子路由将在您的应用程序中进行配置。

我希望本指南能够帮助您了解 Angular Router 静态方法 forRoot 和 forChild 如何帮助您在应用程序中创建结构良好的路由。有关更多信息,请查看文档