Angular2模块中的'imports'和'exports'是如何工作的?

24
我是一个完全不了解Angular2的新手,理解Angular2中的@NgModule导入和导出非常困难。
以以下代码为例:
@NgModule({
  imports: [
    RouterModule.forChild(heroesRoutes)
  ],
  exports: [
    RouterModule
  ]
})

在导入部分,我正在导入什么?路由器模块,forChild()函数还是由forChild()函数返回的模块?
也就是说,当我再次导出RouterModule时,documentation中说:
“我们的最后一步是重新导出RouterModule。通过重新导出RouterModule,当使用我们的Routing Module时,我们的feature module将提供Router Directives。”
那是否意味着导入上述NgModule的任何模块都将可以访问RouterModule中的所有代码?
这是否有点像我在C中编写一个自定义头文件,其中包括math.h,如果我现在使用该头文件,我不再需要单独包含math.h在我的程序中。我的类比正确吗?
能否有人解释一下核心概念,以便我在看到新代码时不会被绊倒。
3个回答

35

Angular2中的模块与Angular1中的模块非常相似,基本上是一个包含了所有有意义一起发行的部分和碎片的软件包。

例如,如果您有一个登录组件(LoginComponent),它出于某些原因与一个服务(Service)连接在一起,该服务是LoginService,它执行一些http请求以获取一些用户信息,那么您可能会创建一个LoginModule,将LoginComponent和LoginService作为LoginModule一起发布。

LoginService也可以使用一些接口,例如UserInterface,这些接口也可以被提供给LoginModule的使用者,这是有意义的。

所以,如果我是你的客户,并且我要使用你的通用登录功能,那么只需导入你的LoginModule,就可以轻松地将你所有的好处都提供给我的AppComponent!

因此,您的LoginModule类(仅仅是一个简单的JavaScript文件和一个函数)应该导出LoginModule,让我可以使用它:)。

LoginModule将如下所示:

   @NgModule({
      declaration:[LoginComponent , LogoutComponent], 
      exports: [
        LoginComponent , LogoutComponent
      ],
      providers:[LoginService]
    })
    export class LoginModule{

    }

因此,在我的AppModule中,我将导入您的LoginModule。

@NgModule({
  imports: [
    LoginModule,
    RouterModule.forChild(heroesRoutes) // another imported module that I need beside the Login
  ]
})

但是:

如果我知道你的代码,并且不想要任何额外的功能、组件、类或其他东西,而只需要使用你的 LoginComponent,那么我可能更喜欢仅声明我正在使用 LoginComponent,而不导入你庞大的 LoginModule!

@NgModule({
  declaration:[LoginComponent],
  imports: [
    RouterModule.forChild(heroesRoutes)
  ]
})

只有在 LoginComponent 没有直接依赖于 LoginService 的情况下,这才能起作用。否则 Angular 将会抱怨并显示:

**No Provider for LoginService**

在这种情况下,如果您是一个坚强的人并且仍然不相信使用LoginModule,您可以像下面这样帮助Angular并提供LoginService:

@NgModule({
  declaration:[LoginComponent],// declaring it 
  providers:[LoginService],// providing it
  imports: [
    RouterModule.forChild(heroesRoutes)
  ]
});

所以,这可能会继续下去,你可能会发现只需导入整个LoginModule并让模块自己完成所有工作会更容易!

这就是关于LoginModule的全部内容。


现在,来回答你关于forChild的问题。

LoginModule可能希望在给出其类时执行一些额外操作,但并非总是需要的,在这种情况下,您可能已经改进了LoginModule并为其添加了一些附加功能。

@NgModule({
   declaration:[LoginComponent , LogoutComponent], 
   exports: [LoginComponent , LogoutComponent],
   providers:[LoginService]
})  
export class LoginModule{
  public static logAndLoadModule(message){
     console.log('I first log and then give you my module');
     return {
      ngModule: LoginModule
     }
  }
  public static alertAndLoadModule(message){
      alert('I first alert and then give you my module');
     return {
      ngModule: LoginModule
     }
  }
}

在这种情况下,看起来这个模块在开始提供其包时可以记录一些内容。因此,我可以像这样使用它:

在这种情况下,看起来这个模块在开始提供其包时可以记录一些内容。因此,我可以像这样使用它:

   @NgModule({
      imports: [
        LoginModule.logAndLoadModule(),
        RouterModule.forChild(heroesRoutes)
      ]
    })

logAndLoadModule和RouterModule中的forChild函数很相似吗?

是的,它仍然会给您提供LoginModule,但也会执行一些额外操作。

因此,您仍然可以导入LoginModule,但也可以使用其警报和日志功能。

更新:

export class LoginModule 表示当前文件(可能是login.module.ts或其他文件)正在导出一个名为LoginModule的类,该类可以从其他文件导入,并且与Angular无关。这只是TypeScript,将编译为JavaScript。

  @NgModule({
      exports: [
        LoginModulesBrother
      ]
    })
  export class LoginModule{

  }

以上是Angular2特定的语言,意思是LoginModule也正在导出LoginModulesBrother,因此无论谁导入LoginModule,都可以访问LoginModulesBrother。

因此,LoginModulesBrother是另一个模块,可能在其他地方定义,例如:

  @NgModule({
      // some other magic here 
  })
  export class LoginModulesBrother{

  }

一般来说,导入和导出一个(可以是任何东西,如模块、组件、管道、简单函数或其他内容)仅仅是 TypeScript 的语法,但是导出数组和导入数组仅在 Angular 中有特定的含义,并且对于 TypeScript 没有意义。


好的,有几个疑问。当你写 export class LoginModule 时,为什么还要在 NgModule 的 exports 中再次包含呢?第二个问题是像 logAndLoadModule 这样的函数,它们只是由你定义的,还是它们是规范的一部分? - ng.newbie
好的,但将其添加到 @NgModule 的导出中是告诉 Angular2 该类已被导出,对吗?那么理论上,我可以编写一个没有 export 关键字的类,并将其添加到 ngModule 的导出数组中吗?那样还有效吗?我的意思是告诉 Angular2 这个类已经被导出,但并不告诉 TypeScript。这样可以吗? - ng.newbie

3
如果您使用了<router-outlet>组件或者routerLink或者routerLinkActive指令,那么您需要在每个想要使用它们的模块的imports: []中添加RouterModule
这对于任何指令、组件或管道都是适用的。只有通过导入才能在模块中实例化它们。
如果您将一个模块添加到imports: []中,则该模块中列出的所有指令、组件和管道都将可供导入模块使用。
此外,一个模块也可以重新导出其他模块,这样可使导出的指令、组件和管道都可供导入模块使用。
另请参阅:

0

来自RouterModule文档:

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

您可以使用forRoot为根模块配置路由,使用forChild为功能模块配置路由。

在功能模块中不需要路由器服务,因为您的应用程序只需要一个,您应该按照惯例(假设您的根模块命名为AppModule)在AppRoutingModule中进行配置。

以您的代码片段为例:

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

RouterModule有一些声明,例如routerLinkrouterLinkActive,可能会被AppModule中的组件使用。

为了使这些声明在AppModule中可用,您可以直接将RouterModule导入到AppModule中,或通过导出它使其通过AppRoutingModule可用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接