2.Angular 核心概念


文档摘要

2.Angular 核心概念 Angular 核心概念 2.1 组件 (Components) 组件是 Angular 应用程序的基本构建块。可以将组件视为具有自己模板和逻辑的可重用 UI 元素。每个 Angular 应用程序至少有一个根组件,通常命名为 ,它是组件树的起点。 概念详解: 封装性: 组件封装了 HTML 模板、CSS 样式和 TypeScript 代码,实现了关注点分离,使得代码更易于理解和维护。 可重用性: 组件被设计为可重用的,可以在应用程序的不同部分甚至不同的应用程序中重复使用,提高开发效率和代码一致性。 模块化: 组件化的架构使得应用程序更易于模块化,大型应用可以分解为多个小的、独立的组件,便于团队协作和代码管理。

# 2.Angular 核心概念 ## 2. Angular 核心概念 ### 2.1 组件 (Components) 组件是 Angular 应用程序的基本构建块。可以将组件视为具有自己模板和逻辑的可重用 UI 元素。每个 Angular 应用程序至少有一个根组件,通常命名为 `AppComponent`,它是组件树的起点。 **概念详解:** * **封装性:** 组件封装了 HTML 模板、CSS 样式和 TypeScript 代码,实现了关注点分离,使得代码更易于理解和维护。 * **可重用性:** 组件被设计为可重用的,可以在应用程序的不同部分甚至不同的应用程序中重复使用,提高开发效率和代码一致性。 * **模块化:** 组件化的架构使得应用程序更易于模块化,大型应用可以分解为多个小的、独立的组件,便于团队协作和代码管理。 * **层次结构:** 组件可以嵌套在其他组件中,形成组件树,这种层次结构反映了应用程序的 UI 结构,并方便数据在组件之间传递。 **代码实践:** 让我们创建一个简单的 Angular 组件: ```typescript // src/app/hello.component.ts import { Component } from '@angular/core'; @Component({ selector: 'app-hello', // 组件的选择器,用于在模板中引用 template: `

你好,{{ name }}!

`, // 组件的 HTML 模板 styles: [` p { color: blue; font-weight: bold; } `] // 组件的 CSS 样式 (可选) }) export class HelloComponent { name = 'Angular 世界'; // 组件的数据属性 } ``` **代码详解:** * **`@Component` 装饰器:** `@Component` 是一个装饰器,用于将一个 TypeScript 类标记为 Angular 组件。它接收一个元数据对象,用于配置组件的行为。 * **`selector`:** 定义了组件的选择器,即在 HTML 模板中如何使用这个组件。这里 `'app-hello'` 意味着可以在 HTML 中使用 `` 标签来引用这个组件。 * **`template`:** 定义了组件的 HTML 模板,它描述了组件在 UI 中的呈现方式。可以使用模板语法(例如 `{{ name }}`)来绑定组件的数据。 * **`styles` (可选):** 定义了组件的 CSS 样式,这些样式只应用于该组件及其子组件,实现了样式的封装。 * **`HelloComponent` 类:** 这是一个 TypeScript 类,包含了组件的逻辑和数据。 * **`name` 属性:** 定义了一个名为 `name` 的属性,并在模板中通过 `{{ name }}` 进行了数据绑定。 **使用组件:** 要在应用程序中使用 `HelloComponent`,需要在其他组件的模板中使用其选择器 ``,例如在 `AppComponent` 的模板中: ```html // src/app/app.component.html

欢迎来到我的 Angular 应用!

``` **Mermaid 图表:** ```mermaid graph TD A[AppComponent] --> B[HelloComponent] style A fill:#f9f,stroke:#333,stroke-width:2px style B fill:#ccf,stroke:#333,stroke-width:2px subgraph 组件结构 A B end ``` **图表解释:** 这个简单的 `graph TD` 图展示了组件的层次结构。`AppComponent` 是父组件,`HelloComponent` 是子组件,它们之间存在父子关系,构成了组件树的一部分。 ### 2.2 模块 (Modules) 模块是组织 Angular 应用程序的另一种关键方式。模块用于将相关的组件、服务、指令和管道等组织在一起,形成功能独立的单元。每个 Angular 应用程序至少有一个根模块 `AppModule`。 **概念详解:** * **组织代码:** 模块将应用程序分解为逻辑功能块,例如特性模块、共享模块等,使得代码结构更清晰、更易于管理。 * **延迟加载:** 模块支持延迟加载 (Lazy Loading),可以按需加载模块,提高应用程序的初始加载速度,优化用户体验。 * **命名空间:** 模块创建了命名空间,避免了组件、服务等之间的命名冲突。 * **依赖注入:** 模块配置了依赖注入系统,可以声明模块提供的服务,并供模块内的组件使用。 **代码实践:** 创建一个简单的特性模块 `FeatureModule`: ```typescript // src/app/feature/feature.module.ts import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FeatureComponent } from './feature.component'; @NgModule({ declarations: [ FeatureComponent // 声明模块拥有的组件 ], imports: [ CommonModule // 导入其他模块,例如 CommonModule 提供通用的指令和管道 ], exports: [ FeatureComponent // 导出模块的组件,使其可以在其他模块中使用 ] }) export class FeatureModule { } ``` ```typescript // src/app/feature/feature.component.ts import { Component } from '@angular/core'; @Component({ selector: 'app-feature', template: `

这是特性模块组件!

`, }) export class FeatureComponent { } ``` **代码详解:** * **`@NgModule` 装饰器:** `@NgModule` 装饰器用于将一个 TypeScript 类标记为 Angular 模块。它接收一个元数据对象,用于配置模块的行为。 * **`declarations`:** 声明模块拥有的组件、指令和管道。这里声明了 `FeatureComponent`。 * **`imports`:** 导入其他模块,使当前模块可以使用导入模块提供的组件、服务等。 `CommonModule` 是 Angular 提供的常用模块,包含 `ngIf`、`ngFor` 等指令。 * **`exports`:** 导出模块的组件、指令和管道,使其可以在其他模块中使用。这里导出了 `FeatureComponent`,使得其他模块可以引入 `FeatureModule` 并使用 `` 组件。 **在 `AppModule` 中引入 `FeatureModule`:** ```typescript // src/app/app.module.ts import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { AppComponent } from './app.component'; import { HelloComponent } from './hello.component'; import { FeatureModule } from './feature/feature.module'; // 导入 FeatureModule @NgModule({ declarations: [ AppComponent, HelloComponent ], imports: [ BrowserModule, // 浏览器模块,提供浏览器运行环境所需的服务 FeatureModule // 导入 FeatureModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { } ``` **在 `AppComponent` 模板中使用 `FeatureComponent`:** ```html // src/app/app.component.html

欢迎来到我的 Angular 应用!

``` **Mermaid 图表:** ```mermaid graph TD A[AppModule] --> B[BrowserModule] A --> C[FeatureModule] C --> D[CommonModule] C --> E[FeatureComponent] A --> F[AppComponent] A --> G[HelloComponent] style A fill:#f9f,stroke:#333,stroke-width:2px style B fill:#ccf,stroke:#333,stroke-width:2px style C fill:#ccf,stroke:#333,stroke-width:2px style D fill:#ccf,stroke:#333,stroke-width:2px style E fill:#ccf,stroke:#333,stroke-width:2px style F fill:#ccf,stroke:#333,stroke-width:2px style G fill:#ccf,stroke:#333,stroke-width:2px subgraph 模块结构 A B C D end subgraph 组件结构 E F G end ``` **图表解释:** 这个图表展示了模块之间的关系以及模块与组件的关系。`AppModule` 导入了 `BrowserModule` 和 `FeatureModule`,`FeatureModule` 又导入了 `CommonModule` 并声明了 `FeatureComponent`。`AppModule` 自身声明了 `AppComponent` 和 `HelloComponent`。 模块像容器一样组织了相关的组件和其他模块。 ### 2.3 模板 (Templates) 模板是组件视图的定义,它使用 HTML 语法并扩展了 Angular 的模板语法,用于动态地渲染 UI。模板可以包含 HTML 元素、组件、指令和绑定。 **概念详解:** * **声明式 UI:** 模板使用声明式的方式描述 UI,而不是命令式地操作 DOM,使得代码更易于理解和维护。 * **数据绑定:** 模板允许通过数据绑定将组件的数据模型与视图连接起来,实现数据的动态更新。 * **指令:** 模板可以使用指令来增强 HTML 元素的功能,例如条件渲染、循环渲染和 DOM 操作。 * **模板语法:** Angular 提供了丰富的模板语法,例如插值、属性绑定、事件绑定和双向数据绑定,用于实现复杂的 UI 交互。 **代码实践:** 在 `HelloComponent` 的模板中,我们已经看到了插值绑定 `{{ name }}`。 现在我们来演示其他模板语法: ```typescript // src/app/template-example.component.ts import { Component } from '@angular/core'; @Component({ selector: 'app-template-example', template: `

模板语法示例

插值绑定

你好,{{ userName }}!

属性绑定

示例图片 点击我

事件绑定

点击按钮

双向数据绑定

你输入的内容是: {{ inputValue }}

结构型指令

这段内容只有在 isShow 为 true 时显示。

  • {{ i + 1 }}. {{ item }}

属性型指令

这段文字应用了 ngStyle 指令。

`, }) export class TemplateExampleComponent { userName = '张三'; imageUrl = 'https://via.placeholder.com/150'; isDisabled = false; inputValue = ''; isShow = true; items = ['苹果', '香蕉', '橙子']; textColor = 'green'; onClickMe() { alert('按钮被点击了!'); } } ``` **代码详解:** * **插值绑定 `{{ userName }}`:** 将组件的 `userName` 属性的值渲染到模板中。 * **属性绑定 `[src]="imageUrl"` 和 `[disabled]="isDisabled"`:** 将组件的属性值绑定到 HTML 元素的属性。 `[]` 表示属性绑定。 * **事件绑定 `(click)="onClickMe()"`:** 监听 HTML 元素的事件,当事件发生时执行组件的方法。 `()` 表示事件绑定。 * **双向数据绑定 `[(ngModel)]="inputValue"`:** 实现数据模型和视图之间的双向同步。当输入框的值改变时,`inputValue` 属性会更新,反之亦然。 `[()]` 表示双向数据绑定。 需要导入 `FormsModule` 才能使用 `ngModel`。 * **结构型指令 `*ngIf` 和 `*ngFor`:** 结构型指令用于添加或移除 DOM 元素。 `*ngIf` 根据条件决定是否渲染元素, `*ngFor` 循环渲染元素。 `*` 是语法糖,背后会转换成 `` 语法。 * **属性型指令 `[ngStyle]="{'color': textColor, 'font-weight': 'bold'}"`:** 属性型指令用于改变 DOM 元素的外观或行为,但不改变 DOM 结构。 `ngStyle` 用于动态设置元素的样式。 **Mermaid 图表:** ```mermaid graph TD A[Template] --> B[插值绑定 {{ }}] A --> C[属性绑定 []] A --> D[事件绑定 ()] A --> E[双向数据绑定 [()]] A --> F[结构型指令 *ngIf, *ngFor] A --> G[属性型指令 ngStyle, ngClass] style A fill:#f9f,stroke:#333,stroke-width:2px style B fill:#ccf,stroke:#333,stroke-width:2px style C fill:#ccf,stroke:#333,stroke-width:2px style D fill:#ccf,stroke:#333,stroke-width:2px style E fill:#ccf,stroke:#333,stroke-width:2px style F fill:#ccf,stroke:#333,stroke-width:2px style G fill:#ccf,stroke:#333,stroke-width:2px subgraph 模板语法 A B C D E F G end ``` **图表解释:** 这个图表概括了 Angular 模板中常用的模板语法,包括各种类型的绑定和指令。理解这些语法是编写动态 Angular 模板的关键。 ### 2.4 数据绑定 (Data Binding) 数据绑定是 Angular 核心概念之一,它实现了组件数据模型和模板视图之间的自动同步。当数据模型发生变化时,视图会自动更新;当视图发生交互(例如用户输入)时,数据模型也会自动更新。 **概念详解:** * **单向数据绑定:** 数据从组件模型流向模板视图,或者从模板视图流向组件模型,但只有一个方向的数据流动。 * **从模型到视图 (Model-to-View):** 使用插值绑定 `{{ }}` 和属性绑定 `[]`。 当组件数据模型更新时,模板视图会自动更新。 * **从视图到模型 (View-to-Model):** 使用事件绑定 `()`。 当用户在视图中触发事件时,组件模型中的方法会被调用,从而更新模型数据。 * **双向数据绑定:** 数据模型和视图之间双向同步。 使用双向数据绑定 `[()]`。 当数据模型或视图任何一方发生变化时,另一方都会自动更新。 **代码实践:** 我们已经在 `TemplateExampleComponent` 中演示了各种数据绑定方式。 让我们更清晰地展示单向和双向数据绑定的区别。 ```typescript // src/app/data-binding.component.ts import { Component } from '@angular/core'; @Component({ selector: 'app-data-binding', template: `

数据绑定示例

单向数据绑定 (模型 -> 视图)

当前计数器值: {{ counter }}

增加计数器

单向数据绑定 (视图 -> 模型)

更新名字

你的名字是: {{ userName }}

双向数据绑定

双向绑定值: {{ twoWayValue }}

`, }) export class DataBindingComponent { counter = 0; userName = ''; twoWayValue = ''; incrementCounter() { this.counter++; } updateName(name: string) { this.userName = name; } } ``` **代码详解:** * **单向数据绑定 (模型 -> 视图):** `{{ counter }}` 插值绑定显示 `counter` 的值。 `incrementCounter()` 方法更新 `counter` 的值,视图会自动更新。 * **单向数据绑定 (视图 -> 模型):** `#nameInput` 创建了一个模板引用变量,`nameInput.value` 可以访问输入框的值。 `updateName()` 方法接收输入框的值并更新 `userName` 属性,视图会自动更新显示新的名字。 * **双向数据绑定:** `[(ngModel)]="twoWayValue"` 实现 `twoWayValue` 属性和输入框的双向绑定。 **Mermaid 图表:** ```mermaid graph TD A[Component Model] -- 插值绑定 {{ }} & 属性绑定 [] --> B[Template View] B -- 事件绑定 () --> C[Component Model] D[Component Model] <--> 双向数据绑定 [()] <--> E[Template View] style A fill:#f9f,stroke:#333,stroke-width:2px style B fill:#ccf,stroke:#333,stroke-width:2px style C fill:#f9f,stroke:#333,stroke-width:2px style D fill:#f9f,stroke:#333,stroke-width:2px style E fill:#ccf,stroke:#333,stroke-width:2px subgraph 数据绑定类型 B --> C D <--> E end subgraph 单向数据绑定 (模型 -> 视图) A --> B end subgraph 单向数据绑定 (视图 -> 模型) B --> C end subgraph 双向数据绑定 D <--> E end ``` **图表解释:** 这个图表清晰地展示了单向数据绑定和双向数据绑定的数据流动方向。单向数据绑定分为模型到视图和视图到模型两个方向,而双向数据绑定则实现了模型和视图之间的双向同步。 ### 2.5 指令 (Directives) 指令是 Angular 模板语法的强大扩展,用于增强 HTML 元素的功能。指令分为三种类型:组件、结构型指令和属性型指令。 **概念详解:** * **组件 (Component):** 实际上,组件也是一种特殊的指令。 它们拥有模板,并且是 Angular 应用的主要构建块。我们已经在 2.1 节详细讨论过。 * **结构型指令 (Structural Directives):** 用于添加或移除 DOM 元素,改变 DOM 结构。 例如 `*ngIf`, `*ngFor`, `*ngSwitch`. * **属性型指令 (Attribute Directives):** 用于改变 DOM 元素的外观或行为,但不改变 DOM 结构。 例如 `NgStyle`, `NgClass`, `ngModel`. **代码实践:** 我们已经在 `TemplateExampleComponent` 中使用了一些指令。 现在我们创建一个专门的指令示例组件,并自定义一个属性型指令。 ```typescript // src/app/directives-example.component.ts import { Component } from '@angular/core'; @Component({ selector: 'app-directives-example', template: `

指令示例

结构型指令

这段内容使用 *ngIf 指令控制显示。

切换显示/隐藏
  • {{ item }}

属性型指令

这段文字使用了自定义的 appHighlight 指令。

这段文字也使用了 appHighlight 指令,颜色为淡蓝色。

这段文字使用了 ngStyle 指令。

这段文字使用了 ngClass 指令,需要定义 CSS 类 'bold-text'。

`, styles: [` .bold-text { font-weight: bold; } `] }) export class DirectivesExampleComponent { isVisible = true; items = ['指令示例 1', '指令示例 2', '指令示例 3']; toggleVisibility() { this.isVisible = !this.isVisible; } } ``` ```typescript // src/app/highlight.directive.ts (自定义属性型指令) import { Directive, ElementRef, Input, OnInit } from '@angular/core'; @Directive({ selector: '[appHighlight]' // 指令的选择器,使用属性选择器 }) export class HighlightDirective implements OnInit { @Input('appHighlight') highlightColor: string; // 输入属性,允许从外部设置高亮颜色 constructor(private el: ElementRef) { // ElementRef 提供了对宿主 DOM 元素的访问 } ngOnInit() { this.el.nativeElement.style.backgroundColor = this.highlightColor || 'lightgreen'; // 默认高亮颜色为浅绿色 } } ``` **代码详解:** * **结构型指令:** `*ngIf` 和 `*ngFor` 的使用示例,控制 DOM 元素的显示和循环渲染。 * **属性型指令:** * **`ngStyle` 和 `ngClass`:** Angular 提供的内置属性型指令,用于动态设置样式和 CSS 类。 * **`appHighlight` (自定义指令):** * `@Directive({ selector: '[appHighlight]' })`: 定义指令,选择器 `[appHighlight]` 表示该指令通过属性 `appHighlight` 应用于元素。 * `ElementRef`: 在构造函数中注入 `ElementRef`,用于访问指令宿主的 DOM 元素。 * `@Input('appHighlight') highlightColor: string;`: 定义输入属性 `highlightColor`,允许在模板中通过 `[appHighlight]="colorValue"` 传递颜色值。 `'appHighlight'` 是输入属性的别名,如果不指定别名,默认输入属性名为 `highlightColor`。 * `ngOnInit()`: 生命周期钩子,在指令初始化时调用。 在这里设置宿主元素的背景颜色。 **Mermaid 图表:** ```mermaid graph TD A[指令 (Directives)] --> B[组件 (Component)] A --> C[结构型指令 (Structural Directives)] A --> D[属性型指令 (Attribute Directives)] C --> E[*ngIf] C --> F[*ngFor] D --> G[NgStyle] D --> H[NgClass] D --> I[自定义属性型指令 (appHighlight)] style A fill:#f9f,stroke:#333,stroke-width:2px style B fill:#ccf,stroke:#333,stroke-width:2px style C fill:#ccf,stroke:#333,stroke-width:2px style D fill:#ccf,stroke:#333,stroke-width:2px style E fill:#ccf,stroke:#333,stroke-width:2px style F fill:#ccf,stroke:#333,stroke-width:2px style G fill:#ccf,stroke:#333,stroke-width:2px style H fill:#ccf,stroke:#333,stroke-width:2px style I fill:#ccf,stroke:#333,stroke-width:2px subgraph 指令类型 A B C D end subgraph 结构型指令示例 E F end subgraph 属性型指令示例 G H I end ``` **图表解释:** 这个图表展示了指令的分类和一些常见的指令示例,包括内置指令和自定义指令。理解指令的不同类型和作用,可以帮助你更灵活地控制 Angular 模板的渲染和行为。 ### 2.6 服务和依赖注入 (Services & Dependency Injection) 服务是 Angular 中用于封装可重用逻辑的类。服务通常用于处理与组件视图无关的任务,例如数据获取、日志记录、业务逻辑等。 依赖注入 (Dependency Injection, DI) 是 Angular 的核心设计模式,用于管理组件和服务之间的依赖关系,提高代码的模块化和可测试性。 **概念详解:** * **服务 (Services):** * **封装逻辑:** 服务封装了可重用的逻辑,避免在组件中编写重复的代码,提高代码复用率。 * **关注点分离:** 服务将业务逻辑与组件的视图逻辑分离,使得代码结构更清晰,更易于维护和测试。 * **单例模式 (默认):** Angular 默认将服务注册为单例,即在整个应用程序中只有一个服务实例,方便共享状态和资源。 * **依赖注入 (Dependency Injection):** * **解耦:** DI 解耦了组件和服务之间的依赖关系,组件不需要直接创建或管理依赖的服务,而是通过 DI 容器自动注入依赖的服务。 * **可测试性:** DI 使得组件更容易进行单元测试,因为可以轻松地 mock 或 stub 组件的依赖服务。 * **配置灵活:** DI 系统允许在模块级别配置服务提供商,可以根据不同的环境或需求提供不同的服务实现。 **代码实践:** 创建一个简单的日志服务 `LogService` 和一个使用该服务的组件 `ServiceExampleComponent`。 ```typescript // src/app/log.service.ts import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' // 将服务注册为根级别服务,整个应用只有一个实例 }) export class LogService { log(message: string) { console.log(`[LogService]: ${message}`); } } ``` ```typescript // src/app/service-example.component.ts import { Component } from '@angular/core'; import { LogService } from './log.service'; @Component({ selector: 'app-service-example', template: `

服务和依赖注入示例

记录日志 `, }) export class ServiceExampleComponent { constructor(private logService: LogService) { // 通过构造函数注入 LogService } logMessage() { this.logService.log('按钮被点击了!'); } } ``` **代码详解:** * **`LogService`:** * `@Injectable({ providedIn: 'root' })`: `@Injectable` 装饰器将 `LogService` 类标记为可注入的服务。 `providedIn: 'root'` 将服务注册在根注入器中,使其在整个应用程序中可用,并创建单例实例。 * `log(message: string)` 方法: 服务提供的日志记录功能。 * **`ServiceExampleComponent`:** * `constructor(private logService: LogService)`: 通过构造函数参数声明依赖,Angular DI 系统会自动将 `LogService` 的实例注入到 `logService` 属性中。 * `logMessage()` 方法: 调用注入的 `logService` 的 `log()` 方法记录日志。 **Mermaid 图表:** ```mermaid graph TD A[Component (ServiceExampleComponent)] --> B[Dependency Injection (DI) Container] B --> C[Service (LogService)] A -- 依赖注入 --> C style A fill:#f9f,stroke:#333,stroke-width:2px style B fill:#ccf,stroke:#333,stroke-width:2px style C fill:#ccf,stroke:#333,stroke-width:2px subgraph 依赖注入流程 A --> B --> C end subgraph 组件和服务关系 A -- 依赖注入 --> C end ``` **图表解释:** 这个图表展示了依赖注入的基本流程。 组件 `ServiceExampleComponent` 依赖于 `LogService`。 当 Angular 创建 `ServiceExampleComponent` 的实例时,DI 容器会查找 `LogService` 的提供商,并将其注入到组件的构造函数中。 组件无需关心如何创建 `LogService` 实例,DI 系统负责管理依赖关系。 ### 2.7 路由 (Routing) 路由是 Angular 中用于实现单页应用程序 (SPA) 导航的关键特性。路由允许在不重新加载整个页面的情况下,根据 URL 的变化显示不同的组件视图,提供流畅的用户体验。 **概念详解:** * **URL 导航:** 路由根据浏览器的 URL 变化,将用户导航到不同的组件视图。 * **组件视图切换:** 路由负责加载和卸载组件,实现不同视图之间的切换。 * **路由配置:** 路由配置定义了 URL 路径与组件之间的映射关系,以及路由守卫、路由参数等路由行为。 * **模块化路由:** Angular 支持模块化路由,可以将路由配置分散到不同的模块中,提高代码组织性和可维护性。 **代码实践:** 配置简单的路由,实现两个组件之间的导航:`HomeComponent` 和 `AboutComponent`。 ```typescript // src/app/home.component.ts import { Component } from '@angular/core'; @Component({ selector: 'app-home', template: `

首页

欢迎来到首页!

`, }) export class HomeComponent { } ``` ```typescript // src/app/about.component.ts import { Component } from '@angular/core'; @Component({ selector: 'app-about', template: `

关于我们

这是关于我们页面。

`, }) export class AboutComponent { } ```

发布者: 作者: 转发
评论区 (0)
U