想要更好地理解 Angular
应用程序所有的不同构建的模块?
在这篇文章中,我们将走进模块的内容。
在 angular
应用中,模块是共享和重用代码的好方法。
共享模块不仅让你的应用联系紧密,而且可以对你的应用进行瘦身。
在这个教程中,我们将创建自定义的模块,并发掘它的组件。
然后,我们将学习怎么使用我们的模块来启用延迟加载,从而使应用更小,使用户体验更好。
我们开始吧!
App Module
在 Angular
里面,一切皆可组织成模块。所以,即使你不知道哪些是模块或者怎么使用它们,你已经无行在应用中使用它们了。其中最突出的是 AppModule
。
AppModule
是你应用中的根模块,并且对于运行我们的应用程序是必要的模块。在这里,我们可以定义应用程序使用哪些组件或者哪些模块。那么它长什么样呢?我们通过 angular-cli
来生成一个基本的 AppModule
。
// src/app/app.module.ts
import { BrowserModule } from '@angular/platform-browser'
import { NgModule } from '@angular/core'
import { AppComponent } from './app.component'
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}
复制代码
要生成你自己的模块,请打开终端并进入项目的根目录。
使用下面的命令行去生成一个新的模块文件。
代码语言:javascript复制ng generate module [name]
复制代码
一个 Angular Module 是什么?
简单来说,一个模块就是一个类,就像组件和服务一样。
Angular
中的代码通常以模块的形式组织。我们可以将模块视为包含特定用例所需要代码的包或捆绑包。
最重要的模块是 App-Module
,每个通过脚手架生成的应用都有它。
但是,App-Module
很可能不是你目前为止遇到的唯一模块。还有很多开箱即用的模块。
比如 Http-Client-Module
,它包含一个很有用处的 Http-Client
和 Forms-Module
(其中包含 UI
组件和 HTML-Forms
指令)。
正如我们上面的例子中看到的,我们要使用模块之前,需要先导入模块。
App-Module
是应用程序的根模块。该模块导入其他模块,这些模块可以自己导入其他模块。
就像组件一样,生成的结构是一个模块树。
@NgModule
在 @NgModule
操作符里面,我们定义模块的所有属性。我们提供了一个简单的 JavaScript
对象作为参数。让我们仔细点看,这些属性是什么,又干了些什么:
Bootstrap
定义应用程序的根组件。仅在 AppModule
中使用它。
Exports
我们在这里定义要组件、指令或者管道。这意味着,我们的模块在导入时将这些模块提供给其他模块。否则,这些模块将停留在模块内部,无法从外部访问。
Declarations
在 declarations
数组中,我们定义着所有的组件,指令和管道,我们可以在这个模块内使用。如果一个组件(或者指令或者管道)你并没有添加到 declarations
中,但是你又在模块或者应用中使用了,angular
应用在运行时报错。此外,一个组件只能在一个模块中声明。如果你想在多个模块中使用你的组件,你需要将改组件捆绑到一个单独的模块中,并将其导入到模块中。
Imports
说到导入... 你的模块可以导入任意数量的子模块。还没有定义任何自定义模块?没问题,我们将解决这个问题。即使你没有任何模块,你仍然需要导入一些 angular
模块。正如我们之前提到的,Angular
在构建之初已经考虑到了模块化。虽然很多特性都包含在 Angular
的核心中,但是有些特性被捆绑在它们自己的模块中。比如,你想使用 HttpClient
,你得想导入 HttpClientModule
。
Providers
我们定义了模块所需的任何的 @Injectables
。然后,任何子组件或者模块都可以通过依赖注入获得该 @Injectables
相同的实例。在 AppModule
案例中,这些 @Injectables
就是 application-scoped
。
构建自定义模块
我们假装已经构建了一个很棒的应用程序。这个程序只有一个模块,就是 AppModule
。
现在,为我们应用程序添加登录内容。登录内容将包含一个登录的页面和一个注册的页面。也许会有一个帮助的页面。每个页面都是以组件的方式呈现。
代码语言:javascript复制LoginComponent
RegisterComponent
HelpComponent
复制代码
同时,我们需要一个服务发起 Http
请求。
AuthenticationService
复制代码
由于这些页面是完全独立的,并且与我们应用程序的内容页面无关。我们决定将它们捆绑到一个单独的模块中。我们称这个模块为 AuthentictionModule
。
// src/app/authentication/authentication.module.ts
import { NgModule } from '@angular/core'
import { CommonModule } from '@angular/common'
@NgModule({
imports: [CommonModule],
})
export class AuthenticationModule {}
复制代码
现在,将我们的组件添加到 declarations
部分。同时,我们将它们放在 exports
部分,因为我们想在模块外部使用它们。
// src/app/authentication/authentication.module.ts
import { HelpComponent } from './help/help.component'
import { RegisterComponent } from './register/register.component'
import { LoginComponent } from './login/login.component'
import { NgModule } from '@angular/core'
import { CommonModule } from '@angular/common'
@NgModule({
imports: [CommonModule],
declarations: [LoginComponent, RegisterComponent, HelpComponent],
exports: [LoginComponent, RegisterComponent, HelpComponent],
})
export class AuthenticationModule {}
复制代码
已经准备好了。现在,我们可以在 AppModule
导入它,然后使用它里面的组件,比如在 AppComponent
中使用。
// src/app/app.module.ts
import { AuthenticationModule } from './authentication/authentication.module'
import { BrowserModule } from '@angular/platform-browser'
import { NgModule } from '@angular/core'
import { AppComponent } from './app.component'
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, AuthenticationModule],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}
复制代码
Lazy-Loading Modules
事实表明,你可以使用模块做更多事情,而不是仅仅组织你的组件。也可以延迟加载模块。这是什么意思呢?
Angular
程序的下载体积很大。根据你的用户场景,这是一个很大的问题。特别是在移动端,加载一个应用程序可能需要耗费很长时间。减少加载时间的一种方法是将应用程序拆分成模块。
当你以惰性方式加载模块时,它不会包含在初始的程序中。相反,它仅在需要的时候才下载。为啥要下载我们还没用得上的组件呢,是吧?
那么,它是怎么工作的?
我们用惰性加载方式更改下先前的例子。为了实现这点,我们要在应用中添加路由。
首先,我们用路由配置来配置路由模块。所以,我们创建一个名为 app.routing.ts
的文件,它跟 app.module
同级。
// src/app/app.routing.ts
import { ContentComponent } from './content/content.component'
import { Routes, RouterModule } from '@angular/router'
import { ModuleWithProviders } from '@angular/core'
export const routes: Routes = [
{ path: '', pathMatch: 'full', redirectTo: 'content' },
{ path: 'content', component: ContentComponent },
{
path: 'login',
loadChildren: './authentication/authentication.module#AuthenticationModule',
},
]
export const routing: ModuleWithProviders = RouterModule.forRoot(routes)
复制代码
非延迟加载的组件由路径和组件属性指定。如果我们想在特定的路由上延迟加载模块,我们可以使用 loadChildren
属性。这里我们指定模块的路径和名称,用 #
分隔开。
之后,我们可以在我们的 AppModule
中导入配置模块。我们还删除了 AuthenticationModule
的导入,因为它是延迟加载的。
// src/app/app.module.ts
import { routing } from './app.routing'
import { BrowserModule } from '@angular/platform-browser'
import { NgModule } from '@angular/core'
import { AppComponent } from './app.component'
import { ContentComponent } from './content/content.component'
@NgModule({
declarations: [AppComponent, ContentComponent],
imports: [
BrowserModule,
routing, //import routing
],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}
复制代码
接着,我们在程序的某个地方添加 router-outlet
。这里我们把它放在 AppComponent
中。
<!-- src/app/app.component.html -->
<router-outlet></router-outlet>
复制代码
如果我们进入那个路由,那个模块将被加载。但是此时屏幕上什么都没有。因为 Angular
还不知道现实哪个组件。为了解决这个问题,我们必须为 authentication module
定义子路由。这看起来集合和 app.routing
完全一样。不一样的是,我们调用的是 forChild()
而不是 forRoot()
,当然,路由也不同。
// src/app/authentication/authentication.routing.ts
import { AuthenticationComponent } from './authentication.component'
import { HelpComponent } from './help/help.component'
import { RegisterComponent } from './register/register.component'
import { LoginComponent } from './login/login.component'
import { Routes, RouterModule } from '@angular/router'
import { ModuleWithProviders } from '@angular/core'
export const routes: Routes = [
{ path: '', component: LoginComponent }, // default route of the module
{ path: 'login', component: LoginComponent },
{ path: 'register', component: RegisterComponent },
{ path: 'help', component: HelpComponent },
]
export const routing: ModuleWithProviders = RouterModule.forChild(routes)
复制代码
现在需要做的是,将路由导入到 AuthenticationModule
中。当我们再次进入路由后,登陆组件会被展示出来。这是因为我们配置其为默认路由。
// src/app/authentication/authentication.module.ts
import { AuthenticationComponent } from './authentication.component'
import { routing } from './authentication.routing'
import { HelpComponent } from './help/help.component'
import { RegisterComponent } from './register/register.component'
import { LoginComponent } from './login/login.component'
import { NgModule } from '@angular/core'
import { CommonModule } from '@angular/common'
@NgModule({
imports: [
CommonModule,
routing, // import routing
],
declarations: [
AuthenticationComponent,
LoginComponent,
RegisterComponent,
HelpComponent,
],
})
export class AuthenticationModule {}
复制代码
Angular 模块不是 JavaScript 模块
别把 Angular
模块和 JavaScript
模块混淆。Angular
模块是类,用 @NgModule
进行标识。另一方面,当我们使用 Typescript
关键字 import
导入模块时,我们在导入一个 JavaScript
模块。该模块可以包含 Angular
模块。
Angular 模块
代码语言:javascript复制// src/app/app.module.ts
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}
复制代码
JavaScript 模块
代码语言:javascript复制import { BrowserModule } from '@angular/platform-browser'
import { NgModule } from '@angular/core'
复制代码
本文是译文,采用的是意译的方式,其中加上个人的理解和注释,原文地址是:malcoded.com/posts/angul…