模块化开发 Angular 应用 [含懒加载]

2022-09-19 16:55:57 浏览数 (1)

想要更好地理解 Angular 应用程序所有的不同构建的模块?

在这篇文章中,我们将走进模块的内容。

angular 应用中,模块是共享和重用代码的好方法。

共享模块不仅让你的应用联系紧密,而且可以对你的应用进行瘦身。

在这个教程中,我们将创建自定义的模块,并发掘它的组件。

然后,我们将学习怎么使用我们的模块来启用延迟加载,从而使应用更小,使用户体验更好。

我们开始吧!

App Module

Angular 里面,一切皆可组织成模块。所以,即使你不知道哪些是模块或者怎么使用它们,你已经无行在应用中使用它们了。其中最突出的是 AppModule

AppModule 是你应用中的根模块,并且对于运行我们的应用程序是必要的模块。在这里,我们可以定义应用程序使用哪些组件或者哪些模块。那么它长什么样呢?我们通过 angular-cli 来生成一个基本的 AppModule

代码语言:javascript复制
// 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-ClientForms-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 请求。

代码语言:javascript复制
AuthenticationService
复制代码

由于这些页面是完全独立的,并且与我们应用程序的内容页面无关。我们决定将它们捆绑到一个单独的模块中。我们称这个模块为 AuthentictionModule

代码语言:javascript复制
// src/app/authentication/authentication.module.ts

import { NgModule } from '@angular/core'
import { CommonModule } from '@angular/common'

@NgModule({
  imports: [CommonModule],
})
export class AuthenticationModule {}
复制代码

现在,将我们的组件添加到 declarations 部分。同时,我们将它们放在 exports 部分,因为我们想在模块外部使用它们。

代码语言:javascript复制
// 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 中使用。

代码语言:javascript复制
// 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 同级。

代码语言:javascript复制
// 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 的导入,因为它是延迟加载的。

代码语言:javascript复制
// 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 中。

代码语言:javascript复制
<!-- src/app/app.component.html -->

<router-outlet></router-outlet>
复制代码

如果我们进入那个路由,那个模块将被加载。但是此时屏幕上什么都没有。因为 Angular 还不知道现实哪个组件。为了解决这个问题,我们必须为 authentication module 定义子路由。这看起来集合和 app.routing 完全一样。不一样的是,我们调用的是 forChild() 而不是 forRoot(),当然,路由也不同。

代码语言:javascript复制
// 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 中。当我们再次进入路由后,登陆组件会被展示出来。这是因为我们配置其为默认路由。

代码语言:javascript复制
// 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…

0 人点赞