65.9K
CodeProject 正在变化。 阅读更多。
Home

2021 年 Angular 最佳实践

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.45/5 (7投票s)

2021 年 9 月 16 日

CPOL

10分钟阅读

viewsIcon

10885

本文介绍开发人员在构建 Angular 应用程序时应遵循的一些最佳实践。

Angular 由 Google 开发人员使用 TypeScript 构建,是一个开源 JavaScript 框架,旨在构建前端应用程序。

Angular 2+ 是 Angular.js 的后继者,使用 TypeScript 而不是 JavaScript 从头重写,这有助于避免许多与 JavaScript 相关的问题,并通过 TypeScript 的静态类型和基于类的面向对象特性确保遵循最佳实践并与 IDE 集成。

Angular 不仅仅是一个框架,它还是一个功能齐全的平台,使前端 Web 和移动开发更易于管理。此外,由于社区的项目,您可以为移动(Ionic 和 NativeScript)和桌面(Electron)设备构建原生应用程序。

Angular 与 React 和 Vue.js 等其他现代 JavaScript 库类似,并使用了许多共享概念。虽然 React 在全球 Web 开发人员中更受欢迎,但 Angular 更适合企业应用。

本文介绍开发人员在构建 Angular 应用程序时应遵循的一些最佳实践。它还探讨了葡萄城(GrapeCity)的 Wijmo(一套包含 100 多个 JavaScript UI 组件的动态集合)如何使开发人员能够使用 Angular 框架构建尖端网格、图表等。

使用 Angular CLI

开发 Web 应用程序时首先应考虑的是开发工具。如今,我们拥有现代工具,使前端 Web 开发更加简单。对于 Angular,我们有许多工具,其中最重要的是官方的 Angular CLI 和 Nx,一个智能且可扩展的构建框架。

尽管您可以 不使用官方 CLI 创建 Angular 项目,但这仅适用于学习目的。对于实际开发,您应该使用 Angular CLI。它是由 Angular 官方团队基于 Node.js 创建的命令行界面。它使得从一开始就初始化一个完全可用的 Angular 应用程序变得非常容易,无需配置 Webpack 等构建工具的麻烦。它通过提供用于脚手架模块和组件等构造、测试(单元、集成和端到端测试)、构建最终生产包甚至帮助您部署最终应用程序的命令来协助开发。

确保使用 Angular CLI 生成您的项目,因为它带有团队推荐的最佳实践,如果您正在构建全栈应用程序,甚至可以使用 Nx。

在安装 Angular CLI 之前,您必须安装最新版本的 Node.js 和 npm。如果没有,您可以使用以下方法之一

  • 官方网站 下载适用于您操作系统的安装程序
  • 使用目标系统的官方包管理器
  • 使用 Node 版本管理工具,例如 NVM,它使您能够在系统上管理 多个 Node 版本。它还有助于在您的机器上全局安装软件包,而无需在 Linux 或 MAC 上使用 sudo,也无需额外配置

现在,使用以下命令安装 Angular CLI

npm install -g @angular/cli

此命令将 CLI 全局安装到您的系统上。

您可以运行 ng 命令以获取所有可用命令,然后运行 ng 后跟特定命令和 --help 选项以显示该命令的帮助文件。

您可以使用以下命令检查已安装的版本

ng version

接下来,运行以下命令生成一个新项目

ng new angular-practices-demo

Angular 会询问您

  • 您想添加 Angular 路由吗?输入 "y"
  • 您想使用哪种样式表格式?使用箭头键选择 SCSS

使用可扩展和可维护的项目结构

如果您之前做过 Web 开发,您就会知道第一次尝试找到一个方便的项目结构或架构并非总是那么容易。但是,随着您在构建小型和大型应用程序方面获得更多经验,它会变得更容易。

对于小型应用程序,Angular CLI 生成的默认结构是可以的。但是,一旦您的项目增长,您会发现正确维护和扩展您的应用程序具有挑战性。

这里有一篇关于如何 组织应用程序文件夹结构 的优秀文章,您将从一个简陋的 Angular 项目开始,然后转向一个更具组织性的坚实文件夹结构,其中包含单独的组件和页面文件夹。页面只是一个路由组件。

此外,一个好的实践是使用一个核心模块、一个共享模块和每个应用程序功能的一个功能模块(加上引导应用程序的根应用程序模块)来构建您的应用程序。然后,您将应用程序模块中的导入移动到核心模块,并仅将应用程序模块保留用于应用程序引导。

您必须将所有单例服务(整个应用程序只有一个实例)放在核心模块中。例如,身份验证服务在每个应用程序中应该只有一个实例,作为核心模块的一部分。

在共享模块中,您应该放置在多个模块中使用的公共 artifacts(组件、指令、管道等),以便您可以导入共享模块以使用它们。共享模块也是 哑组件 和不注入服务但只能通过 props 接收数据的管道的好地方。

假设您正在使用 UI 组件库,例如 葡萄城(GrapeCity)的 Wijmo。在这种情况下,这是一个很好的地方来导入和重新导出您打算在整个应用程序中使用的组件,这样您就不需要在每个模块中重复导入。

为了继续我们之前生成的项目,运行以下命令创建核心模块和共享模块

ng generate module core

ng generate module shared

接下来,假设我们需要产品和购物车两个功能。

使用相同的命令为它们生成两个功能模块

ng generate module product

ng generate module cart

接下来,打开 _src/app/shared.module.ts_ 文件并按如下方式更新

import { NgModule } from '@angular/core';

import { CommonModule } from '@angular/common';

import { FormsModule } from '@angular/forms';

@NgModule({

    declarations: [],

imports: [

    CommonModule

],

exports: [

    CommonModule,

    FormsModule

]

})

export class SharedModule { }

在这里,我们将 FormsModule 添加到 exports 数组中,以便该数组将其导出到导入共享模块的其他模块,但我们没有将其添加到 imports 数组中。通过这种方式,我们可以让其他模块访问 FormsModule,而无需在共享的 `NgModule` 中直接导入它。

接下来,我们重新导出 `CommonModule` 和 `FormsModule`,以便从 `CommonModule` 使用 `NgIf` 和 `NgFor` 等常见指令,并从导入此 `SharedModule` 的模块中使用 `[(ngModel)]` 绑定组件属性。

接下来,打开 src/app/app.module.ts 文件并按如下方式导入核心模块和共享模块

import { NgModule } from '@angular/core';

import { BrowserModule } from '@angular/platform-browser';

import { AppRoutingModule } from './app-routing.module';

import { AppComponent } from './app.component';

import { CoreModule } from './core/core.module';

import { SharedModule } from './shared/shared.module';

@NgModule({

    declarations: [

        AppComponent

],

imports: [

    BrowserModule,

    AppRoutingModule,

    CoreModule,

    SharedModule

],

providers: [],

bootstrap: [AppComponent]

})

export class AppModule { }

此外,从 `ProductModule` 和 `CartModule` 中删除 `CommonModule` 并导入 `SharedModule`,因为它已经导出了 `CommonModule`。

保持更新

Angular 遵循语义版本控制,每六个月发布一个新主版本。

语义版本控制 是一种用于软件版本控制的约定。它采用 major.minor.patch 格式。Angular 在发布主要、次要或补丁更改时递增每个部分。

您可以从 CHANGELOG 关注有关最新版本 Angular 的新闻,并确保您的 Angular 版本保持最新,以确保您始终获得最新功能、错误修复和性能增强(如 Ivy)。

当您将项目从一个版本更新到下一个版本时,您还应该使用 此官方工具

严格模式

我们在引言中提到,Angular 2+ 在早期阶段就采用了 TypeScript,确保平台——包括框架和工具——遵循依赖注入等最佳实践,这使得测试更易于管理,并且具有性能预算。

Angular 团队已经 逐步应用严格模式,并在 Angular 10 中提供了一个选项,默认情况下为从 Angular 12 开始的所有项目启用严格模式。这现在是默认启用的最佳实践,但如果出于学习目的必须禁用它,您可以在创建新项目时使用 --no-strict 选项。

对于现有项目,您可以在 tsconfig.json 中启用严格模式,如下所示

{
  "compilerOptions": {
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
  },
  "angularCompilerOptions": {
    "enableI18nLegacyMessageIdFormat": false,
    "strictInjectionParameters": true,
    "strictInputAccessModifiers": true,
    "strictTemplates": true
  }
}

此外,由于 Ivy 编译器和语言服务,您只需将 strictTemplates 设置为 true,即可在模板中受益于 TypeScript 的类型系统。这是从 Angular 12 开始的默认设置。有关更多详细信息,请查看 官方文档

确保遵循 Angular 团队 推荐的安全实践,并避免使用 ElementRef 和 innerHTML,除非您确定自己知道自己在做什么!

使用惰性加载

使用 Angular 时,您应该构建所谓的 SPA,即单页应用程序。这是一种不同于我们以前创建的传统 Web 应用程序的现代应用程序类型。

Angular 一次性从服务器加载 SPA 包,并使用 JavaScript 或客户端路由来使用户在不同视图之间导航。

这是当今构建应用程序的现代方法,也是我们使用 Angular、React 和 Vue.js 等现代框架构建应用程序的方式。

Angular 提供了一个功能强大的路由器,具有大量功能可用于客户端路由。因此,一旦您掌握了必要的概念,构建 SPA 就很容易。然而,这会影响性能,因为我们必须从服务器下载完整的应用程序包。因此,当您的应用程序大小增长时,应用程序的下载时间会增加!

惰性加载的作用就在于此,它围绕着当应用程序用户访问特定模块时延迟加载这些模块的想法。这通过减少应用程序包的实际下载大小来使您受益。惰性加载还通过在应用程序首次启动时不加载未使用的模块,而仅在用户触发导航时才加载来缩短启动时间。

作为一项最佳实践,您必须在应用程序中尽可能地惰性加载 功能模块。您需要一个功能模块在应用程序启动时急切加载以显示初始内容。您应该惰性加载所有其他功能模块以提高性能并减小初始包大小。

您可以使用 Angular 路由器的 `loadChildren` 属性和 动态导入语法惰性加载模块。但由于 Ivy,您也可以惰性加载组件。让我们看一个例子!

首先,确保您有一个已设置 Angular 路由的项目。使用 Angular CLI,您可以通过在生成项目时为 ng new 命令设置 --routing 标志,或在提示“是否要添加 Angular 路由?”时回答“y”来处理这个问题。

打开 _src/app/app-routing.module.ts_ 文件并按如下方式惰性加载产品和购物车模块

import { NgModule } from '@angular/core';

import { RouterModule, Routes } from '@angular/router';

import { ProductModule } from './product/product.module';

import { CartModule } from './cart/cart.module';

const routes: Routes = [

    { path: 'product', loadChildren: () => import('./product/product.module').then(m => m.ProductModule) },

    { path: 'cart', loadChildren: () => import('./cart/cart.module').then(m => m.CartModule) }

];

@NgModule({

    imports: [RouterModule.forRoot(routes)],

    exports: [RouterModule]

})

export class AppRoutingModule { }

我们使用路由配置的 `loadChildren` 属性结合 import 语句来惰性加载模块。

现在,您添加到这些模块的任何组件都将是惰性加载的!然而,有了 Ivy,我们可以在不需要模块的情况下惰性加载 Angular 组件。

首先,使用以下命令生成一个组件

ng generate component header --module=core

核心模块导入了它。

打开 _src/app/app.component.html_ 文件并按如下方式更新

<button (click)="lazyLoadHeader()">Load header</button>

<ng-container #header></ng-container>

接下来,打开 src/app/app.component.ts 文件并进行如下更新

import { Component, ComponentFactoryResolver, ViewChild, ViewContainerRef } from '@angular/core';

@Component({

    selector: 'app-root',

    templateUrl: './app.component.html',

    styleUrls: ['./app.component.scss']

})

export class AppComponent {

    title = 'angular-practices-demo';

    @ViewChild('header', { read: ViewContainerRef }) headerContainer: ViewContainerRef | null = null;

    constructor(private  factoryResolver: ComponentFactoryResolver) { }

    async lazyLoadHeader() {

        const { HeaderComponent } = await import('./header/header.component');

        const factory = this.factoryResolver.resolveComponentFactory(HeaderComponent);

    this.headerContainer?.createComponent(factory);

    }

}

当您单击按钮时,您应该会看到“header works!”,这意味着组件按需惰性加载并渲染!

取消订阅 RxJS Observables

当您订阅组件到 RxJS Observables 时,您应该始终取消订阅。否则,这会导致不必要的内存泄漏,因为即使在使用它的组件被销毁后,可观察流仍然是打开的。

您可以通过多种方式实现此目的

  • 在组件销毁后,在 `ngOnDestory` 事件中取消订阅组件
  • 使用 **async pipe** 订阅 Observables 并在模板中自动取消订阅。

使用带 trackBy 的 ngFor

您使用 `ngFor` 指令在 Angular 模板中迭代数组。当您更改数组时,整个 DOM 树会重新渲染,这不利于性能。为了解决这个问题,您必须使用带有 trackBy 的 ngFor,它唯一地标识每个 DOM 元素,并使 Angular 只重新渲染修改过的元素

@Component({

    selector: 'my-app',

    template: `

    <li *ngFor="let product of products; trackBy:productById"></li>

    `

})

export class App {

    products:[];

    {id:0, name: "product 1"},

    {id:1, name: "product 2"}

];

productById(index, product){

    return product.id;

}

结论

Angular 团队从一开始就通过使用 TypeScript 进行 Angular 开发来采用最佳实践,确保类型安全、更好的错误处理和与 IDE 的集成。Angular 12 默认启用了严格模式,确保您遵循严格的规则,帮助您构建无错误且可靠的应用程序。在本文中,我们已经看到了一些可以遵循的最佳实践,以构建可扩展且易于维护的应用程序。

如果您想尝试一些对 Angular 有深度支持的与框架无关的 UI 组件——包括 数据网格、图表、仪表和输入控件——请立即尝试 Wijmo

© . All rights reserved.