angular初步使用
<h2 id="创建项目">创建项目</h2><pre><code class="language-bash">npm install -g @angular/cli
ng new my-angular-project
cd my-angular-project
ng serve
ng serve --port 8081
# access in localhost:4200
</code></pre>
<h2 id="angular-cli">angular-cli</h2>
<h3 id="文件生成">文件生成</h3>
<p>Angular CLI 提供了快捷命令来生成各种文件,保持项目结构的一致性。常用的生成命令包括:</p>
<ul>
<li>
<p><strong>生成组件</strong>:</p>
<pre><code class="language-bash">ng generate component component-name
# 或者简写
ng g c component-name
</code></pre>
<p>这将创建一个包含 <code>HTML</code>、<code>CSS</code>、<code>TypeScript</code> 和 <code>测试</code>文件的组件。</p>
</li>
<li>
<p><strong>生成服务</strong>:</p>
<pre><code class="language-bash">ng generate service service-name
# 简写
ng g s service-name
</code></pre>
<p>生成的服务文件默认包含注入器,并能方便地被组件使用。</p>
</li>
<li>
<p><strong>生成模块</strong>:</p>
<pre><code class="language-bash">ng generate module module-name
# 简写
ng g m module-name
</code></pre>
<p>生成的模块可以帮助你将代码逻辑按功能模块划分,便于代码管理和懒加载。</p>
</li>
</ul>
<h3 id="构建项目"><strong>构建项目</strong></h3>
<p>使用 <code>ng build</code> 命令来构建项目:</p>
<pre><code class="language-bash">ng build
</code></pre>
<p>构建后的文件会存放在 <code>dist</code> 目录下。为了发布到生产环境,你可以使用以下命令:</p>
<pre><code class="language-bash">ng build --prod
</code></pre>
<ul>
<li><code>-prod</code> 选项会启用生产环境的优化,如代码压缩、混淆、去除调试信息等。</li>
</ul>
<h3 id="测试"><strong>测试</strong></h3>
<p>Angular CLI 支持单元测试和端到端测试:</p>
<ul>
<li>
<p><strong>单元测试</strong>:使用 <code>ng test</code> 命令运行单元测试,默认使用 Karma 和 Jasmine。</p>
<pre><code class="language-bash">ng test
</code></pre>
<p>这会在浏览器中打开测试界面,并实时更新测试结果。</p>
</li>
<li>
<p><strong>端到端测试</strong>:使用 <code>ng e2e</code> 命令运行端到端测试,默认使用 Protractor。</p>
<pre><code class="language-bash">ng e2e
</code></pre>
<p>端到端测试模拟用户行为,确保应用整体功能正常。</p>
</li>
</ul>
<h3 id="生成服务管道指令等其他结构"><strong>生成服务、管道、指令等其他结构</strong></h3>
<p>除了组件和模块,CLI 还支持生成其他 Angular 结构:</p>
<ul>
<li>
<p><strong>生成指令</strong>:</p>
<pre><code class="language-bash">ng generate directive directive-name
# 简写
ng g d directive-name
</code></pre>
</li>
<li>
<p><strong>生成管道</strong>:</p>
<pre><code class="language-bash">ng generate pipe pipe-name
# 简写
ng g p pipe-name
</code></pre>
</li>
</ul>
<h3 id="配置和优化"><strong>配置和优化</strong></h3>
<ul>
<li><strong>环境配置</strong></li>
</ul>
<p>Angular 支持不同环境的配置文件,默认包括 <code>environment.ts</code>(开发环境)和 <code>environment.prod.ts</code>(生产环境)。你可以在 <code>angular.json</code> 文件中定义更多的环境配置,并在构建时选择不同的环境:</p>
<pre><code class="language-bash">ng build --configuration production
</code></pre>
<ul>
<li><strong>调试和监控</strong></li>
</ul>
<p>使用 <code>ng serve</code> 时,Angular CLI 会实时监听文件更改,并自动重新构建应用程序。你可以使用 <code>--source-map</code> 选项生成调试信息,以便在浏览器中调试代码:</p>
<pre><code class="language-bash">ng serve --source-map
</code></pre>
<ul>
<li><strong>预加载和懒加载模块</strong></li>
</ul>
<p>在大型应用中,使用预加载和懒加载模块可以提高性能。Angular CLI 自动支持懒加载,帮助你按需加载模块,减少初次加载时间。</p>
<h3 id="其他有用的-cli-命令"><strong>其他有用的 CLI 命令</strong></h3>
<ul>
<li>
<p><strong>更新 Angular 项目或依赖</strong>:</p>
<pre><code class="language-bash">ng update
</code></pre>
<p>该命令会检查并更新 Angular 和相关依赖项。</p>
</li>
<li>
<p><strong>分析构建包</strong>:<br>
使用 <code>-stats-json</code> 选项构建应用,可以生成分析文件,用于检查和优化打包内容。</p>
<pre><code class="language-bash">ng build --prod --stats-json
</code></pre>
</li>
<li>
<p><strong>添加第三方库或工具</strong>:<br>
使用 <code>ng add</code> 命令,可以快速添加第三方库和插件。例如,添加 Angular Material:</p>
<pre><code class="language-bash">ng add @angular/material
</code></pre>
</li>
</ul>
<h3 id="angularjson-配置文件"><strong>angular.json 配置文件</strong></h3>
<p><code>angular.json</code> 是 Angular 项目的全局配置文件,包含项目的所有配置信息。你可以在这里调整构建路径、环境配置、样式和脚本的引入顺序等。</p>
<h2 id="angular-项目的基础结构">Angular 项目的基础结构</h2>
<h3 id="项目目录结构">项目目录结构</h3>
<p>当你使用 <code>ng new</code> 创建一个新的 Angular 项目后,项目结构会像这样:</p>
<pre><code class="language-perl">my-angular-app/
├── e2e/ # 端到端测试目录
├── node_modules/ # 项目依赖包目录
├── src/ # 应用源代码目录
│ ├── app/ # 核心应用目录
│ │ ├── app.component.ts # 根组件的逻辑文件
│ │ ├── app.component.html # 根组件的模板文件
│ │ ├── app.component.css # 根组件的样式文件
│ │ └── app.module.ts # 根模块文件
│ ├── assets/ # 静态资源目录
│ ├── environments/ # 环境配置目录
│ ├── index.html # 主 HTML 文件
│ ├── main.ts # 应用的主入口文件
│ ├── polyfills.ts # 浏览器兼容代码
│ ├── styles.css # 全局样式文件
│ └── test.ts # 单元测试入口文件
├── angular.json # Angular 项目配置文件
├── package.json # 项目依赖和脚本
├── tsconfig.json # TypeScript 配置文件
└── README.md # 项目说明文件
</code></pre>
<h3 id="目录和文件详解">目录和文件详解</h3>
<ol>
<li>
<p><strong><code>src/</code> 目录</strong></p>
<p><code>src/</code> 是存放应用源代码的主要目录,Angular 应用的所有核心代码都在这里。</p>
<ul>
<li><strong><code>app/</code> 目录</strong>:这是应用程序的主要目录,包含了根模块和根组件。随着项目的开发,你会在这里创建更多的组件、服务、模块等。
<ul>
<li><strong><code>app.component.ts</code></strong>:定义了根组件的逻辑,是整个应用的起点。</li>
<li><strong><code>app.component.html</code></strong>:根组件的模板文件,用于定义根组件的 HTML 结构。</li>
<li><strong><code>app.component.css</code></strong>:根组件的样式文件。</li>
<li><strong><code>app.module.ts</code></strong>:根模块文件,应用启动时会加载的模块。每个 Angular 应用至少有一个根模块。</li>
</ul>
</li>
<li><strong><code>assets/</code> 目录</strong>:存放静态资源(如图片、字体等),构建时会直接复制到构建目录中,可以通过相对路径访问这些资源。</li>
<li><strong><code>environments/</code> 目录</strong>:包含不同环境的配置文件,例如开发环境和生产环境。默认包含 <code>environment.ts</code>(开发环境配置)和 <code>environment.prod.ts</code>(生产环境配置)。你可以根据环境条件加载不同的配置。</li>
<li><strong><code>index.html</code></strong>:应用的主 HTML 文件。它是页面的入口,Angular 会把所有组件渲染到这个页面内。</li>
<li><strong><code>main.ts</code></strong>:应用的主入口文件,Angular 应用从这里开始执行。<code>main.ts</code> 会引导根模块 <code>AppModule</code>,并启动 Angular 应用。</li>
<li><strong><code>polyfills.ts</code></strong>:用于加载不同浏览器的兼容性代码,确保应用在所有浏览器中一致运行。</li>
<li><strong><code>styles.css</code></strong>:全局样式文件,可以在这里定义整个应用的通用样式。</li>
<li><strong><code>test.ts</code></strong>:测试入口文件,用于配置和初始化单元测试。</li>
</ul>
</li>
<li>
<p><strong><code>e2e/</code> 目录</strong></p>
<p><code>e2e/</code> 目录用于存放端到端测试代码。默认使用 Protractor 框架来运行这些测试,用于模拟用户行为并测试整个应用的功能。</p>
</li>
<li>
<p><strong>根目录中的其他文件</strong></p>
<ul>
<li><strong><code>angular.json</code></strong>:Angular 项目的配置文件,包含构建和开发服务器的相关配置。可以在这里调整构建输出路径、环境配置、样式和脚本的引入顺序等。</li>
<li><strong><code>package.json</code></strong>:Node.js 项目的配置文件,包含依赖项和运行脚本。所有的依赖库和 CLI 命令都在这里定义和管理。</li>
<li><strong><code>tsconfig.json</code></strong>:TypeScript 的配置文件,定义了编译 TypeScript 代码的规则。</li>
<li><strong><code>README.md</code></strong>:项目的说明文件,可以写入项目的描述、安装步骤和使用说明。</li>
</ul>
</li>
<li>
<p><strong><code>node_modules/</code> 目录</strong></p>
<p><code>node_modules/</code> 目录用于存放项目的依赖包,由 <code>npm</code> 安装。所有 Angular、TypeScript、编译器等库的代码都在这里。</p>
</li>
</ol>
<h2 id="组件和模块">组件和模块</h2>
<p>在 Angular 中,<strong>组件</strong>和<strong>模块</strong>是应用的核心架构。组件负责构建页面的各个部分,而模块则帮助组织和管理这些组件。</p>
<h3 id="组件component"><strong>组件(Component)</strong></h3>
<p>组件是 Angular 应用的基本构建块。一个组件通常包括三个部分:</p>
<ul>
<li><strong>模板(Template)</strong>:定义组件的 HTML 结构。</li>
<li><strong>样式(Styles)</strong>:定义组件的 CSS 样式。</li>
<li><strong>逻辑(Class)</strong>:定义组件的行为和数据。</li>
</ul>
<ol>
<li>
<p>创建组件</p>
<p>Angular CLI 提供了生成组件的命令:</p>
<pre><code class="language-bash">ng generate component component-name
# 或者简写
ng g c component-name
</code></pre>
<p>这个命令会在 <code>app</code> 目录下创建一个新的组件目录,包含以下文件:</p>
<ul>
<li><code>component-name.component.ts</code>:组件的逻辑文件,包含组件类和装饰器。</li>
<li><code>component-name.component.html</code>:组件的模板文件。</li>
<li><code>component-name.component.css</code>:组件的样式文件。</li>
<li><code>component-name.component.spec.ts</code>:组件的测试文件。</li>
</ul>
</li>
<li>
<p>组件的结构</p>
<p>在 <code>component-name.component.ts</code> 文件中,组件使用 <code>@Component</code> 装饰器来定义,结构如下:</p>
<pre><code class="language-tsx">import { Component } from '@angular/core';
@Component({
selector: 'app-component-name', // 组件的选择器,用于在模板中引用组件
templateUrl: './component-name.component.html',// 组件的模板文件
styleUrls: ['./component-name.component.css'] // 组件的样式文件
})
export class ComponentNameComponent {
title = 'Hello, Angular'; // 组件的属性和方法
}
</code></pre>
<ul>
<li><strong><code>selector</code></strong>:组件的选择器,用于在其他模板中引用该组件。比如,<code><app-component-name></app-component-name></code>。</li>
<li><strong><code>templateUrl</code></strong> 和 <strong><code>styleUrls</code></strong>:分别定义组件的模板和样式文件路径。</li>
</ul>
</li>
<li>
<p>组件的数据绑定</p>
<p>Angular 提供了多种数据绑定方式:</p>
<ul>
<li><strong>插值表达式</strong>:用于展示组件属性的值,例如 <code>{{ title }}</code>。</li>
<li><strong>属性绑定</strong>:通过 <code>[]</code> 绑定属性,例如 <code><img ="imageUrl"></code>。</li>
<li><strong>事件绑定</strong>:通过 <code>()</code> 绑定事件,例如 <code><button (click)="onClick()">Click</button></code>。</li>
<li><strong>双向数据绑定</strong>:通过 <code>[(ngModel)]</code> 实现数据和视图的双向绑定(需要导入 <code>FormsModule</code>)。</li>
</ul>
</li>
</ol>
<h3 id="模块module"><strong>模块(Module)</strong></h3>
<p>模块用于组织和管理应用的组件、指令、管道和服务。每个 Angular 应用至少有一个根模块,称为 <code>AppModule</code>,它在 <code>app.module.ts</code> 文件中定义。</p>
<ol>
<li>
<p>模块的结构</p>
<p>在 <code>app.module.ts</code> 文件中,模块使用 <code>@NgModule</code> 装饰器定义,结构如下:</p>
<pre><code class="language-tsx">import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { ComponentNameComponent } from './component-name/component-name.component';
@NgModule({
declarations: [
AppComponent,
ComponentNameComponent // 声明模块中的组件
],
imports: [
BrowserModule // 导入其他模块
],
providers: [], // 声明服务的提供者
bootstrap: // 定义根组件,Angular 启动时会加载
})
export class AppModule { }
</code></pre>
<ul>
<li><strong><code>declarations</code></strong>:模块中声明的组件、指令和管道,只有声明在模块中的组件才能被使用。</li>
<li><strong><code>imports</code></strong>:导入其他模块,例如 <code>BrowserModule</code> 是用于浏览器应用的核心模块。</li>
<li><strong><code>providers</code></strong>:声明应用中使用的服务提供者。</li>
<li><strong><code>bootstrap</code></strong>:定义应用启动时加载的根组件,通常是 <code>AppComponent</code>。</li>
</ul>
</li>
<li>
<p>特性模块</p>
<p>在较大的应用中,可以创建多个特性模块(Feature Modules)来组织代码,便于分离不同功能和实现懒加载。</p>
<pre><code class="language-bash">ng generate module feature-module
# 或者简写
ng g m feature-module
</code></pre>
</li>
</ol>
<h3 id="组件与模块的关系"><strong>组件与模块的关系</strong></h3>
<ul>
<li><strong>模块用于组织组件</strong>:在 Angular 中,模块用于管理和组织组件,每个模块可以包含多个组件。</li>
<li><strong>组件的复用性</strong>:一个组件可以在多个模块中声明和使用,但必须先导入包含该组件的模块。</li>
<li><strong>根模块和特性模块</strong>:根模块(如 <code>AppModule</code>)负责启动应用,而特性模块用于组织应用中的具体功能。</li>
</ul>
<h2 id="数据绑定">数据绑定</h2>
<h3 id="插值绑定interpolation"><strong>插值绑定(Interpolation)</strong></h3>
<p>插值绑定使用 <strong><code>{{ }}</code></strong> 语法来绑定数据,常用于在 HTML 中显示组件的属性值。</p>
<p><strong>示例</strong>: 在组件类中定义一个属性 <strong><code>title</code></strong>,然后通过插值绑定将其显示在模板中。</p>
<pre><code class="language-tsx">// 在组件的 TypeScript 文件中
export class MyComponent {
title = 'Hello, Angular!';
}
</code></pre>
<pre><code class="language-html"><!-- 在组件的 HTML 模板中 -->
<h1>{{ title }}</h1>
</code></pre>
<p>这里,<strong><code>{{ title }}</code></strong> 会被替换为 <strong><code>Hello, Angular!</code></strong>。插值绑定通常用于文本内容的展示。</p>
<h3 id="属性绑定property-binding"><strong>属性绑定(Property Binding)</strong></h3>
<p>属性绑定使用方括号 <strong><code>[]</code></strong> 语法,将组件属性的值绑定到 HTML 元素的属性上,例如 <strong><code>src</code></strong>, <strong><code>href</code></strong>, <strong><code>disabled</code></strong> 等。</p>
<p><strong>示例</strong>: 假设我们有一个图片链接,可以使用属性绑定将其绑定到 <strong><code>img</code></strong> 元素的 <strong><code>src</code></strong> 属性上。</p>
<pre><code class="language-tsx">// 在组件的 TypeScript 文件中
export class MyComponent {
imageUrl = 'https://example.com/image.jpg';
}
</code></pre>
<pre><code class="language-html"><!-- 在组件的 HTML 模板中 -->
<img ="imageUrl" alt="Example Image">
</code></pre>
<p>这里 <strong><code>="imageUrl"</code></strong> 表示将 <strong><code>imageUrl</code></strong> 的值绑定到 <strong><code>img</code></strong> 的 <strong><code>src</code></strong> 属性上。</p>
<h3 id="事件绑定event-binding"><strong>事件绑定(Event Binding)</strong></h3>
<p>事件绑定使用圆括号 <strong><code>()</code></strong> 语法,将视图中的事件(如 <strong><code>click</code></strong>、<strong><code>mouseover</code></strong>)绑定到组件中的方法上,从而触发特定逻辑。</p>
<p><strong>示例</strong>: 我们可以在按钮上绑定一个 <strong><code>click</code></strong> 事件,触发组件中的 <strong><code>onClick</code></strong> 方法。</p>
<pre><code class="language-tsx">// 在组件的 TypeScript 文件中
export class MyComponent {
onClick() {
console.log('Button clicked!');
}
}
</code></pre>
<pre><code class="language-html"><!-- 在组件的 HTML 模板中 -->
<button (click)="onClick()">Click Me</button>
</code></pre>
<p>这里,<strong><code>(click)="onClick()"</code></strong> 绑定了 <strong><code>click</code></strong> 事件,点击按钮时会执行 <strong><code>onClick</code></strong> 方法,并输出 <strong><code>"Button clicked!"</code></strong>。</p>
<h3 id="双向数据绑定two-way-binding"><strong>双向数据绑定(Two-way Binding)</strong></h3>
<p>双向数据绑定使用 <strong><code>[(ngModel)]</code></strong> 语法,将数据和视图双向同步。它允许用户输入的数据自动更新组件属性,组件属性的变化也会自动反映到视图中。双向绑定通常用于表单输入和用户输入场景。</p>
<p><strong>示例</strong>: 在输入框中使用双向数据绑定,将 <strong><code>name</code></strong> 属性与 <strong><code>input</code></strong> 输入框的值同步。需要导入 <strong><code>FormsModule</code></strong>。</p>
<pre><code class="language-tsx">// 在组件的 TypeScript 文件中
export class MyComponent {
name = '';
}
</code></pre>
<pre><code class="language-html"><!-- 在组件的 HTML 模板中 -->
<input [(ngModel)]="name" placeholder="Enter your name">
<p>Hello, {{ name }}!</p>
</code></pre>
<p>在这里,<strong><code>[(ngModel)]="name"</code></strong> 实现了双向数据绑定,用户在输入框中的输入会立即更新 <strong><code>name</code></strong>,并且 <strong><code>name</code></strong> 的值会立即显示在页面上。</p>
<blockquote>
<p>注意:使用双向绑定需要在应用模块中导入 FormsModule,否则会报错。</p>
</blockquote>
<pre><code class="language-tsx">import { FormsModule } from '@angular/forms';
@NgModule({
imports: ,
...
})
export class AppModule { }
</code></pre>
<h3 id="数据绑定的好处"><strong>数据绑定的好处</strong></h3>
<ul>
<li><strong>简化代码</strong>:通过绑定可以减少手动更新 DOM 的代码。</li>
<li><strong>实时同步</strong>:数据和视图保持同步,使得应用更加动态。</li>
<li><strong>提高可维护性</strong>:将数据和视图分离,更容易维护和扩展应用。</li>
</ul>
<h2 id="指令directives">指令(Directives)</h2>
<p>指令(Directives)是 Angular 中非常重要的一个特性,允许我们在模板中操作 DOM 元素,控制样式、结构和行为。指令使得 Angular 的模板变得更加动态和灵活。</p>
<h3 id="指令的类型">指令的类型</h3>
<p>Angular 中有三种主要的指令类型:</p>
<ol>
<li><strong>组件指令</strong>:组件本质上也是一种指令,它拥有模板、样式和逻辑,是指令的扩展形式。</li>
<li><strong>结构型指令(Structural Directives)</strong>:用于添加或移除 DOM 元素,控制页面结构。常见的有 <code>ngIf</code> 和 <code>ngFor</code>。</li>
<li><strong>属性型指令(Attribute Directives)</strong>:用于更改元素的外观或行为,常见的有 <code>ngClass</code> 和 <code>ngStyle</code>。</li>
</ol>
<h3 id="结构型指令">结构型指令</h3>
<p>结构型指令用于添加、移除或替换 DOM 元素。使用时需要在指令前加上 <code>*</code> 符号。</p>
<ul>
<li>
<p><code>ngIf</code> 根据条件来显示或隐藏 DOM 元素。</p>
<pre><code class="language-html"><div *ngIf="isVisible">This is visible only if isVisible is true.</div>
</code></pre>
<p>在上面的示例中,<code>*ngIf="isVisible"</code> 控制 <code>div</code> 元素是否显示,只有当 <code>isVisible</code> 为 <code>true</code> 时才会渲染该元素。</p>
</li>
<li>
<p><code>ngFor</code> 用于遍历数组并生成一组 DOM 元素。</p>
<pre><code class="language-html"><ul>
<li *ngFor="let item of items">{{ item }}</li>
</ul>
</code></pre>
<p>在上面的示例中,<code>*ngFor</code> 会遍历 <code>items</code> 数组,为每个元素生成一个 <code><li></code>。</p>
</li>
<li>
<p><code>ngSwitch</code> 可以根据不同的条件来渲染不同的元素,通常用于多条件判断。</p>
<pre><code class="language-html"><div ="value">
<p *ngSwitchCase="'one'">Value is one</p>
<p *ngSwitchCase="'two'">Value is two</p>
<p *ngSwitchDefault>Value is unknown</p>
</div>
</code></pre>
<p><code>ngSwitchCase</code> 用于匹配特定的值,<code>ngSwitchDefault</code> 表示默认的情况。</p>
</li>
</ul>
<h3 id="属性型指令">属性型指令</h3>
<p>属性型指令用于改变 DOM 元素的外观或行为,它不会创建或移除元素,而是修改现有元素的属性。</p>
<ul>
<li>
<p><code>ngClass</code> 用于动态设置元素的 CSS 类。</p>
<pre><code class="language-html"><div ="{ 'active': isActive, 'highlight': isHighlighted }">Styled div</div>
</code></pre>
<p>在上面的示例中,根据 <code>isActive</code> 和 <code>isHighlighted</code> 的值动态添加 <code>active</code> 和 <code>highlight</code> 类。</p>
</li>
<li>
<p><code>ngStyle</code> 用于动态设置元素的内联样式。</p>
<pre><code class="language-html"><div ="{ 'color': textColor, 'font-size': fontSize + 'px' }">Styled div</div>
</code></pre>
<p>在上面的示例中,<code>textColor</code> 和 <code>fontSize</code> 会动态控制 <code>div</code> 元素的颜色和字体大小。</p>
</li>
</ul>
<h3 id="自定义指令">自定义指令</h3>
<p>除了 Angular 提供的内置指令,你还可以创建自定义指令来实现特定的行为。通常,自定义指令是属性型指令,用于扩展元素的功能。</p>
<ul>
<li>
<p>创建一个自定义指令</p>
<p>使用 Angular CLI 创建一个指令,命令如下:</p>
<pre><code class="language-bash">ng generate directive highlight
# 或简写
ng g d highlight
</code></pre>
<p>这个命令会生成一个指令文件 <code>highlight.directive.ts</code>,初始内容如下:</p>
<pre><code class="language-tsx">import { Directive, ElementRef, Renderer2, HostListener } from '@angular/core';
@Directive({
selector: ''
})
export class HighlightDirective {
constructor(private el: ElementRef, private renderer: Renderer2) {}
@HostListener('mouseenter') onMouseEnter() {
this.highlight('yellow');
}
@HostListener('mouseleave') onMouseLeave() {
this.highlight(null);
}
private highlight(color: string) {
this.renderer.setStyle(this.el.nativeElement, 'backgroundColor', color);
}
}
</code></pre>
<p>在这个示例中,自定义的 <code>HighlightDirective</code> 指令会在鼠标移到元素上时设置背景颜色为黄色,移出时移除颜色。</p>
<ul>
<li><strong><code>@Directive</code> 装饰器</strong>:定义了一个指令,<code>selector: ''</code> 表示它是一个属性型指令,使用时通过 <code>appHighlight</code> 属性添加到元素上。</li>
<li><strong><code>ElementRef</code></strong>:用于访问指令应用的 DOM 元素。</li>
<li><strong><code>Renderer2</code></strong>:用于安全地操作 DOM 样式,避免直接修改 DOM。</li>
<li><strong><code>@HostListener</code></strong>:监听元素的事件,<code>mouseenter</code> 表示鼠标移入,<code>mouseleave</code> 表示鼠标移出。</li>
</ul>
</li>
<li>
<p>使用自定义指令</p>
<p>在模板中使用 <code>appHighlight</code> 指令:</p>
<pre><code class="language-html"><p appHighlight>Hover over this text to see the highlight effect.</p>
</code></pre>
<p>添加 <code>appHighlight</code> 属性后,指令会在元素上生效。当鼠标悬停时,背景颜色会变成黄色,移开时恢复原样。</p>
</li>
</ul>
<h2 id="服务services和依赖注入dependency-injection">服务(Services)和依赖注入(Dependency Injection)</h2>
<p>在 Angular 中,<strong>服务</strong>(Service)用于封装和共享应用中的逻辑和数据,而<strong>依赖注入</strong>(DI)系统负责管理和提供这些服务。通过使用服务,你可以将应用的业务逻辑从组件中分离出来,提升代码的复用性和可维护性。依赖注入系统则确保服务可以被组件或其他服务轻松使用。</p>
<h3 id="服务">服务</h3>
<p>服务在 Angular 中通常是一个类,用于封装不属于任何组件的逻辑和数据。比如获取数据、处理业务逻辑、管理状态等,服务可以在多个组件之间共享。</p>
<ol>
<li>创建一个服务</li>
</ol>
<p>使用 Angular CLI 可以快速生成服务:</p>
<pre><code class="language-bash">ng generate service my-service
# 或者简写
ng g s my-service
</code></pre>
<p>生成的服务文件 <code>my-service.service.ts</code> 大致如下:</p>
<pre><code class="language-tsx">import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'// 将服务注册在根注入器中,全局可用
})
export class MyService {
constructor() { }
getData() {
return 'Hello from MyService!';
}
}
</code></pre>
<ul>
<li><strong><code>@Injectable</code> 装饰器</strong>:标记一个类可以作为依赖被注入到组件或其他服务中。</li>
<li><strong><code>providedIn: 'root'</code></strong>:表示该服务在根注入器中提供,整个应用都可以访问。这样可以避免在 <code>providers</code> 中手动注册服务。</li>
</ul>
<ol>
<li>在组件中使用服务</li>
</ol>
<p>服务通常通过依赖注入的方式使用。你可以将服务注入到组件的构造函数中,以便在组件中调用服务方法。</p>
<p>假设我们已经创建了一个名为 <code>MyService</code> 的服务,以下是在组件中使用该服务的示例:</p>
<pre><code class="language-tsx">import { Component, OnInit } from '@angular/core';
import { MyService } from './my-service.service';
@Component({
selector: 'app-my-component',
template: `<p>{{ message }}</p>`
})
export class MyComponent implements OnInit {
message: string;
// 在构造函数中注入服务
constructor(private myService: MyService) {}
ngOnInit(): void {
this.message = this.myService.getData();
}
}
</code></pre>
<ul>
<li><strong>构造函数注入</strong>:通过在组件的构造函数中定义一个私有变量 <code>myService</code> 来注入服务。</li>
<li><strong>调用服务方法</strong>:在 <code>ngOnInit</code> 生命周期钩子中调用服务方法 <code>getData()</code> 并将返回值赋给 <code>message</code>。</li>
</ul>
<ol>
<li>服务的作用域和提供方式</li>
</ol>
<p>Angular 的服务提供方式有多种,不同的提供方式会影响服务的作用域和生命周期:</p>
<ul>
<li>根级提供方式</li>
</ul>
<p>使用 <code>providedIn: 'root'</code> 在根注入器中提供服务,服务实例在整个应用程序生命周期中是单例的。这种方式通常用于全局共享的数据或逻辑。</p>
<pre><code class="language-tsx">@Injectable({
providedIn: 'root'
})
export class MyService { }
</code></pre>
<ul>
<li>模块级提供方式</li>
</ul>
<p>如果你希望服务仅在特定模块中可用,可以在该模块的 <code>providers</code> 数组中注册服务。这会让服务的生命周期与模块一致,适合局部共享的数据或逻辑。</p>
<pre><code class="language-tsx">import { NgModule } from '@angular/core';
import { MyService } from './my-service.service';
@NgModule({
providers: // 在模块级提供服务
})
export class MyModule { }
</code></pre>
<ul>
<li>组件级提供方式</li>
</ul>
<p>如果你希望服务的实例仅在单个组件或该组件的子组件中可用,可以在组件的 <code>providers</code> 数组中注册服务。这会让每个组件实例有自己独立的服务实例,适合仅在单个组件内使用的逻辑。</p>
<pre><code class="language-tsx">import { Component } from '@angular/core';
import { MyService } from './my-service.service';
@Component({
selector: 'app-my-component',
template: `<p>My Component</p>`,
providers: // 在组件级提供服务
})
export class MyComponent { }
</code></pre>
<h3 id="依赖注入dependency-injection">依赖注入(Dependency Injection)</h3>
<p>依赖注入是一种设计模式,通过将依赖项(例如服务)注入到组件或其他服务中,避免硬编码依赖。Angular 的 DI 系统自动管理依赖项的创建和提供,简化了应用的结构。</p>
<ol>
<li>DI 系统的工作方式</li>
</ol>
<p>当 Angular 检测到某个类需要特定的依赖(比如 <code>MyService</code>),它会在注入器中查找该依赖的实例。如果实例不存在,它会创建实例并返回给组件或服务。</p>
<ol>
<li>自定义注入器</li>
</ol>
<p>Angular 支持在组件中自定义注入器,从而控制依赖的提供方式。这在大多数应用中不常用,但在需要控制服务作用域或实现特殊依赖时非常有用。</p>
<h3 id="服务的实际应用场景">服务的实际应用场景</h3>
<ul>
<li><strong>数据共享</strong>:将共享的数据存储在服务中,以便多个组件可以访问和更新这些数据。</li>
<li><strong>HTTP 请求</strong>:通过 <code>HttpClient</code> 服务从后端 API 获取数据,并将逻辑封装在服务中,便于复用和测试。</li>
<li><strong>全局状态管理</strong>:在服务中管理应用状态,比如用户认证信息或主题设置。</li>
</ul>
<h3 id="示例创建一个简单的数据服务">示例:创建一个简单的数据服务</h3>
<p>假设我们要创建一个简单的数据服务 <code>DataService</code>,用于管理一组用户数据,并提供增删改查功能。</p>
<ul>
<li>创建服务</li>
</ul>
<pre><code class="language-tsx">import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class DataService {
private users = ['Alice', 'Bob', 'Charlie'];
getUsers() {
return this.users;
}
addUser(user: string) {
this.users.push(user);
}
}
</code></pre>
<ul>
<li>在组件中使用服务</li>
</ul>
<pre><code class="language-tsx">import { Component } from '@angular/core';
import { DataService } from './data.service';
@Component({
selector: 'app-user-list',
template: `
<ul>
<li *ngFor="let user of users">{{ user }}</li>
</ul>
<input [(ngModel)]="newUser" placeholder="Enter name">
<button (click)="addUser()">Add User</button>
`
})
export class UserListComponent {
users: string[];
newUser = '';
constructor(private dataService: DataService) {
this.users = this.dataService.getUsers();
}
addUser() {
if (this.newUser) {
this.dataService.addUser(this.newUser);
this.newUser = '';
}
}
}
</code></pre>
<p>在这个示例中,<code>DataService</code> 提供了用户数据的管理逻辑,<code>UserListComponent</code> 组件从服务中获取数据并展示在视图中,同时可以通过服务添加新用户。</p>
<h2 id="路由routing和导航">路由(Routing)和导航</h2>
<p>Angular 的路由系统让我们可以构建单页应用(SPA),通过 URL 控制页面的不同视图。路由允许应用根据用户导航在页面之间切换,并且支持参数传递、懒加载和路由守卫等高级功能。</p>
<h3 id="什么是路由">什么是路由?</h3>
<p>在单页应用中,虽然只有一个实际页面,但用户通过不同的 URL 可以导航到应用的不同部分。Angular 的路由系统让你可以定义 URL 路径与组件之间的映射关系,点击导航链接时会加载对应的组件,而不会刷新整个页面。</p>
<h3 id="设置-angular-路由">设置 Angular 路由</h3>
<ol>
<li>导入 <code>RouterModule</code></li>
</ol>
<p>在 Angular 应用中设置路由,需要在应用的根模块或相关特性模块中导入 <code>RouterModule</code>,并定义路由配置。</p>
<p>例如,在 standalone 模式中,可以在 <code>main.ts</code> 中配置路由:</p>
<pre><code class="language-tsx">// main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { provideRouter } from '@angular/router';
import { HomeComponent } from './app/home/home.component';
import { AboutComponent } from './app/about/about.component';
bootstrapApplication(AppComponent, {
providers: [
provideRouter([
{ path: '', component: HomeComponent }, // 默认路由
{ path: 'about', component: AboutComponent } // /about 路由
])
]
});
</code></pre>
<p>在这个例子中,我们定义了两个路由:</p>
<ul>
<li><code>''</code> 表示根路径(<code>/</code>),对应 <code>HomeComponent</code>。</li>
<li><code>'about'</code> 表示 <code>/about</code> 路径,对应 <code>AboutComponent</code>。</li>
</ul>
<ol>
<li>创建组件</li>
</ol>
<p>如果还没有相关组件,可以使用以下命令生成组件:</p>
<pre><code class="language-bash">ng generate component home --standalone
ng generate component about --standalone
</code></pre>
<ol>
<li>在模板中添加路由链接</li>
</ol>
<p>Angular 提供了 <code>routerLink</code> 指令,用于创建路由链接。可以在 <code>AppComponent</code> 的模板中添加导航链接:</p>
<pre><code class="language-html"><!-- app.component.html -->
<nav>
<a routerLink="/">Home</a> |
<a routerLink="/about">About</a>
</nav>
<router-outlet></router-outlet>
</code></pre>
<ul>
<li><strong><code>routerLink</code></strong>:用于指定导航链接的路径。例如,<code>routerLink="/"</code> 指向根路径。</li>
<li><strong><code><router-outlet></router-outlet></code></strong>:路由出口,指定路由组件在页面中显示的位置。<code><router-outlet></code> 是路由系统的占位符,Angular 会根据当前 URL 加载对应的组件并显示在此处。</li>
</ul>
<ol>
<li>路由参数传递</li>
</ol>
<p>Angular 路由支持向 URL 传递参数,并在组件中接收和处理这些参数。例如,我们可以定义一个显示用户详情的路由:</p>
<ul>
<li>定义带参数的路由</li>
</ul>
<p>在路由配置中使用 <code>:id</code> 作为占位符定义参数:</p>
<pre><code class="language-tsx">{ path: 'user/:id', component: UserComponent }
</code></pre>
<ul>
<li>在模板中添加链接</li>
</ul>
<p>在模板中可以使用 <code>routerLink</code> 传递参数:</p>
<pre><code class="language-html"><a ="['/user', 1]">User 1</a>
<a ="['/user', 2]">User 2</a>
</code></pre>
<ul>
<li>
<p>在组件中获取参数</p>
<p>在 <code>UserComponent</code> 中,可以使用 <code>ActivatedRoute</code> 来获取路由参数:</p>
<pre><code class="language-tsx">import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-user',
standalone: true,
template: `<p>User ID: {{ userId }}</p>`
})
export class UserComponent implements OnInit {
userId: string;
constructor(private route: ActivatedRoute) {}
ngOnInit(): void {
this.userId = this.route.snapshot.paramMap.get('id')!;
}
}
</code></pre>
<ul>
<li><strong><code>ActivatedRoute</code></strong>:Angular 路由服务,用于获取路由信息。</li>
<li><strong><code>paramMap</code></strong>:包含了路由参数的键值对。<code>get('id')</code> 获取当前路由中的 <code>id</code> 参数。</li>
</ul>
</li>
</ul>
<h3 id="路由守卫route-guards">路由守卫(Route Guards)</h3>
<p>路由守卫用于保护路由,确保用户具备访问权限。常用的路由守卫包括:</p>
<ul>
<li><strong><code>CanActivate</code></strong>:在导航到某个路由前检查,决定是否允许访问。</li>
<li><strong><code>CanDeactivate</code></strong>:在离开某个路由时检查,决定是否允许离开。</li>
</ul>
<p>例如,创建一个简单的 <code>AuthGuard</code> 守卫,确保用户登录后才能访问某个路由。</p>
<ol>
<li>生成守卫</li>
</ol>
<p>使用 CLI 生成守卫:</p>
<pre><code class="language-bash">ng generate guard auth
</code></pre>
<ol>
<li>实现 <code>AuthGuard</code> 的逻辑</li>
</ol>
<p>在生成的 <code>auth.guard.ts</code> 文件中编写认证逻辑:</p>
<pre><code class="language-tsx">import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';
@Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate {
constructor(private router: Router) {}
canActivate(): boolean {
const isAuthenticated = false; // 替换为实际认证逻辑
if (!isAuthenticated) {
this.router.navigate(['/login']);
return false;
}
return true;
}
}
</code></pre>
<ol>
<li>将守卫应用到路由上</li>
</ol>
<p>在路由配置中使用 <code>canActivate</code> 属性应用守卫:</p>
<pre><code class="language-tsx">{ path: 'protected', component: ProtectedComponent, canActivate: }
</code></pre>
<h3 id="懒加载lazy-loading">懒加载(Lazy Loading)</h3>
<p>懒加载让特定模块在需要时才加载,优化了应用的初始加载速度。可以通过 <code>loadComponent</code> 轻松实现组件的懒加载:</p>
<pre><code class="language-tsx">{ path: 'lazy', loadComponent: () => import('./lazy/lazy.component').then(m => m.LazyComponent) }
</code></pre>
<h2 id="表单forms处理"><strong>表单(Forms)处理</strong></h2>
<p>Angular 提供了强大的表单处理功能,支持创建和验证表单。Angular 表单主要有两种方式:<strong>模板驱动表单</strong> 和 <strong>响应式表单</strong>。这两种方式各有优势,适用于不同场景。</p>
<h3 id="模板驱动表单template-driven-forms"><strong>模板驱动表单(Template-driven Forms)</strong></h3>
<p>模板驱动表单主要通过 HTML 模板来定义表单结构和验证逻辑,适合简单的表单。它使用 Angular 的 <strong><code>FormsModule</code></strong> 来提供数据绑定和验证支持。</p>
<ul>
<li><strong>导入 <code>FormsModule</code></strong></li>
</ul>
<p>首先,在模块中导入 <strong><code>FormsModule</code></strong>。如果你的项目是模块化的,打开 <strong><code>app.module.ts</code></strong> 或相应模块文件,添加 <strong><code>FormsModule</code></strong>。</p>
<pre><code class="language-tsx">import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
@NgModule({
declarations: ,
imports: [
BrowserModule,
FormsModule// 导入 FormsModule
],
bootstrap:
})
export class AppModule {}
</code></pre>
<ul>
<li><strong>定义模板驱动表单</strong></li>
</ul>
<p>在模板中创建一个简单的表单,使用 <strong><code>ngModel</code></strong> 指令来实现双向数据绑定。通过 <strong><code>#name="ngModel"</code></strong> 创建一个模板变量,用于访问输入的验证状态。</p>
<pre><code class="language-html"><!-- app.component.html -->
<form #myForm="ngForm">
<label for="name">Name:</label>
<input id="name" name="name" [(ngModel)]="user.name" required>
<div *ngIf="!myForm.controls.name?.valid && myForm.controls.name?.touched">
Name is required.
</div>
<label for="email">Email:</label>
<input id="email" name="email" [(ngModel)]="user.email" required email>
<div *ngIf="!myForm.controls.email?.valid && myForm.controls.email?.touched">
Valid email is required.
</div>
<button ="!myForm.valid">Submit</button>
</form>
</code></pre>
<ul>
<li><strong><code>[(ngModel)]</code></strong>:双向数据绑定,用于同步表单字段和组件中的数据。</li>
<li><strong><code>#myForm="ngForm"</code></strong>:创建一个模板引用变量 <strong><code>myForm</code></strong>,可以访问表单的状态。</li>
<li><strong><code>required</code> 和 <code>email</code> 验证</strong>:Angular 自动提供了 HTML5 的验证规则。</li>
<li><strong>在组件中定义数据模型</strong></li>
</ul>
<p>在 <strong><code>app.component.ts</code></strong> 中定义 <strong><code>user</code></strong> 数据模型,与表单绑定。</p>
<pre><code class="language-tsx">// app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent {
user = {
name: '',
email: ''
};
}
</code></pre>
<h3 id="响应式表单reactive-forms"><strong>响应式表单(Reactive Forms)</strong></h3>
<p>响应式表单在组件类中定义表单的结构和验证逻辑,更加灵活,适合复杂的动态表单。它使用 <strong><code>ReactiveFormsModule</code></strong> 模块来提供表单控制和验证支持。</p>
<ul>
<li><strong>导入 <code>ReactiveFormsModule</code></strong></li>
</ul>
<p>在模块中导入 <strong><code>ReactiveFormsModule</code></strong>。</p>
<pre><code class="language-tsx">import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { ReactiveFormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
@NgModule({
declarations: ,
imports: [
BrowserModule,
ReactiveFormsModule// 导入 ReactiveFormsModule
],
bootstrap:
})
export class AppModule {}
</code></pre>
<ul>
<li><strong>定义响应式表单</strong></li>
</ul>
<p>在组件中使用 <strong><code>FormBuilder</code></strong> 定义表单结构和验证规则。</p>
<pre><code class="language-tsx">// app.component.ts
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent {
userForm: FormGroup;
constructor(private fb: FormBuilder) {
this.userForm = this.fb.group({
name: ['', Validators.required],
email: ['', ]
});
}
onSubmit() {
if (this.userForm.valid) {
console.log(this.userForm.value);
}
}
}
</code></pre>
<ul>
<li><strong><code>FormBuilder</code></strong>:Angular 提供的服务,用于简化表单结构的创建。</li>
<li><strong><code>Validators</code></strong>:用于设置表单字段的验证规则。</li>
<li><strong>在模板中绑定响应式表单</strong></li>
</ul>
<p>在模板中使用 <strong><code></code></strong> 绑定整个表单,使用 <strong><code>formControlName</code></strong> 绑定每个表单控件。</p>
<pre><code class="language-html"><!-- app.component.html -->
<form ="userForm" (ngSubmit)="onSubmit()">
<label for="name">Name:</label>
<input id="name" formControlName="name">
<div *ngIf="userForm.controls.name.invalid && userForm.controls.name.touched">
Name is required.
</div>
<label for="email">Email:</label>
<input id="email" formControlName="email">
<div *ngIf="userForm.controls.email.invalid && userForm.controls.email.touched">
Valid email is required.
</div>
<button ="userForm.invalid">Submit</button>
</form>
</code></pre>
<ul>
<li><strong><code></code></strong>:将组件中的 <strong><code>userForm</code></strong> 绑定到模板中的表单。</li>
<li><strong><code>formControlName</code></strong>:将表单控件绑定到 <strong><code>userForm</code></strong> 中对应的控件。</li>
</ul>
<h3 id="表单验证"><strong>表单验证</strong></h3>
<p>Angular 提供了多种内置的验证器,如 <strong><code>Validators.required</code></strong>, <strong><code>Validators.email</code></strong>,还可以自定义验证器。</p>
<ul>
<li><strong>自定义验证器</strong></li>
</ul>
<p>可以在组件中定义一个自定义验证器,并将其应用到表单控件中。</p>
<pre><code class="language-tsx">import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms';
export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
const forbidden = nameRe.test(control.value);
return forbidden ? { forbiddenName: { value: control.value } } : null;
};
}
</code></pre>
<p>然后在创建表单时应用自定义验证器:</p>
<pre><code class="language-tsx">this.userForm = this.fb.group({
name: ['', ],
email: ['', ]
});
</code></pre>
<h2 id="http-客户端和-api-通信">HTTP 客户端和 API 通信</h2>
<p>在现代 Web 应用中,与后端 API 通信是必不可少的部分。Angular 提供了 <code>HttpClient</code> 模块,简化了与后端 API 的交互。使用 <code>HttpClient</code> 可以轻松地发送 HTTP 请求、处理响应数据、管理错误以及添加拦截器来控制请求和响应的行为。</p>
<h3 id="设置-httpclient">设置 <code>HttpClient</code></h3>
<p>在 Angular 应用中使用 <code>HttpClient</code>,需要在模块中导入 <code>HttpClientModule</code>。</p>
<ul>
<li>导入 <code>HttpClientModule</code></li>
</ul>
<p>在根模块或特性模块中导入 <code>HttpClientModule</code>:</p>
<pre><code class="language-tsx">import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
import { AppComponent } from './app.component';
@NgModule({
declarations: ,
imports: [
BrowserModule,
HttpClientModule// 导入 HttpClientModule
],
bootstrap:
})
export class AppModule {}
</code></pre>
<ul>
<li>使用 <code>HttpClient</code> 发送请求</li>
</ul>
<p><code>HttpClient</code> 提供了多种方法来发送 HTTP 请求,包括 <code>get</code>、<code>post</code>、<code>put</code>、<code>delete</code> 等,适用于不同类型的请求。</p>
<ol>
<li>发送 GET 请求</li>
</ol>
<p>假设我们要从 API 获取用户列表,可以使用 <code>HttpClient.get</code> 方法发送 GET 请求:</p>
<pre><code class="language-tsx">import { HttpClient } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-user-list',
template: `
<ul>
<li *ngFor="let user of users">{{ user.name }}</li>
</ul>
`
})
export class UserListComponent implements OnInit {
users: any[] = [];
constructor(private http: HttpClient) {}
ngOnInit(): void {
this.http.get<any[]>('<https://jsonplaceholder.typicode.com/users>')
.subscribe(data => {
this.users = data;
});
}
}
</code></pre>
<p>在这个例子中:</p>
<ul>
<li><code>this.http.get</code> 发送一个 GET 请求。</li>
<li><code>subscribe</code> 方法用于处理响应数据,接收的 <code>data</code> 是一个用户列表,赋值给组件的 <code>users</code> 属性。</li>
<li>发送 POST 请求</li>
</ul>
<p>假设我们要向服务器发送数据,可以使用 <code>HttpClient.post</code> 方法发送 POST 请求。</p>
<pre><code class="language-tsx">addUser(newUser: any) {
this.http.post('<https://jsonplaceholder.typicode.com/users>', newUser)
.subscribe(response => {
console.log('User added:', response);
});
}
</code></pre>
<p>这里的 <code>post</code> 方法发送一个 POST 请求,将 <code>newUser</code> 数据传递给 API。<code>subscribe</code> 中的 <code>response</code> 包含服务器返回的结果。</p>
<h3 id="错误处理">错误处理</h3>
<p>在实际应用中,API 请求可能会遇到各种错误,如网络超时、服务器错误等。可以通过 <code>catchError</code> 操作符来捕获和处理错误。</p>
<ul>
<li>错误处理示例</li>
</ul>
<p>使用 <code>catchError</code> 操作符处理请求中的错误:</p>
<pre><code class="language-tsx">import { HttpErrorResponse } from '@angular/common/http';
import { catchError } from 'rxjs/operators';
import { throwError } from 'rxjs';
this.http.get('<https://jsonplaceholder.typicode.com/users>')
.pipe(
catchError(this.handleError)
)
.subscribe(
data => console.log('Data:', data),
error => console.error('Error:', error)
);
handleError(error: HttpErrorResponse) {
let errorMessage = 'Unknown error!';
if (error.error instanceof ErrorEvent) {
// Client-side error
errorMessage = `Error: ${error.error.message}`;
} else {
// Server-side error
errorMessage = `Error Code: ${error.status}\\nMessage: ${error.message}`;
}
return throwError(errorMessage);
}
</code></pre>
<p>在这里:</p>
<ul>
<li><code>catchError</code> 操作符捕获错误并调用 <code>handleError</code> 方法。</li>
<li><code>handleError</code> 方法根据错误类型生成错误信息并通过 <code>throwError</code> 返回。</li>
</ul>
<h3 id="http-拦截器interceptors">HTTP 拦截器(Interceptors)</h3>
<p>HTTP 拦截器允许我们在请求或响应处理前插入逻辑,例如添加认证 token、记录日志等。</p>
<ul>
<li>创建拦截器</li>
</ul>
<p>使用 Angular CLI 创建拦截器:</p>
<pre><code class="language-bash">ng generate interceptor auth
</code></pre>
<p>拦截器文件可能如下所示:</p>
<pre><code class="language-tsx">import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler } from '@angular/common/http';
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler) {
// 克隆请求并添加认证 token
const authReq = req.clone({
setHeaders: {
Authorization: `Bearer YOUR_TOKEN_HERE`
}
});
return next.handle(authReq);
}
}
</code></pre>
<ul>
<li><code>HttpInterceptor</code> 接口的 <code>intercept</code> 方法在请求发送之前执行。</li>
<li><code>req.clone</code> 用于克隆请求对象并添加认证 token。</li>
<li>注册拦截器</li>
</ul>
<p>在模块中将拦截器注册为 <code>HTTP_INTERCEPTORS</code> 的多重提供者:</p>
<pre><code class="language-tsx">import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { AuthInterceptor } from './auth.interceptor';
@NgModule({
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }
]
})
export class AppModule {}
</code></pre>
<h3 id="使用-rxjs-进行异步处理">使用 RxJS 进行异步处理</h3>
<p><code>HttpClient</code> 的方法会返回一个 Observable,通过 <code>subscribe</code> 方法订阅数据。可以使用 RxJS 操作符(如 <code>map</code>、<code>switchMap</code>)来处理异步数据流。</p>
<ul>
<li>RxJS 操作符示例</li>
</ul>
<p>假设你希望在发送请求后处理数据,可以使用 <code>map</code> 操作符:</p>
<pre><code class="language-tsx">import { map } from 'rxjs/operators';
this.http.get<any[]>('<https://jsonplaceholder.typicode.com/users>')
.pipe(
map(users => users.filter(user => user.isActive)) // 过滤用户列表
)
.subscribe(data => {
this.users = data;
});
</code></pre>
<h2 id="rxjs-和响应式编程">RxJS 和响应式编程</h2>
<p>RxJS 是 Angular 的核心库之一,用于处理异步事件和数据流。RxJS 的强大之处在于它提供了丰富的操作符,可以高效地处理流式数据和复杂的异步操作。在 Angular 中,RxJS 广泛应用于 HTTP 请求、表单处理、路由、组件间通信等场景。</p>
<p><strong>RxJS(Reactive Extensions for JavaScript)</strong> 是一个用于处理异步数据流的库,提供了可观察对象(Observable)、观察者(Observer)、操作符(Operators)等功能。响应式编程的核心思想是将数据视为流(Stream),应用一系列操作符来组合、过滤和转换数据,从而实现对数据变化的响应。</p>
<h3 id="observable可观察对象">Observable(可观察对象)</h3>
<ul>
<li><strong>Observable</strong> 是数据流的核心概念。它代表一个异步的数据源,可以是 HTTP 请求、事件、计时器等。</li>
<li>使用 <code>Observable.subscribe()</code> 来订阅数据流,当数据到达时观察者会被通知。</li>
</ul>
<hr>
<h3 id="常用-rxjs-操作符">常用 RxJS 操作符</h3>
<p>RxJS 提供了许多操作符来处理数据流。以下是一些常用操作符及其应用场景:</p>
<ol>
<li><strong><code>map</code></strong> - 数据转换</li>
</ol>
<p><code>map</code> 操作符用于将 Observable 数据流中的每个数据项转换为新的值。</p>
<p><strong>示例</strong>:将获取到的用户数组中的每个用户对象映射为用户名。</p>
<pre><code class="language-tsx">import { map } from 'rxjs/operators';
this.http.get<any[]>('<https://api.example.com/users>')
.pipe(
map(users => users.map(user => user.name))
)
.subscribe(names => console.log(names));
</code></pre>
<ol>
<li><strong><code>filter</code></strong> - 数据过滤</li>
</ol>
<p><code>filter</code> 用于过滤数据流中不符合条件的数据项。</p>
<p><strong>示例</strong>:筛选出 <code>isActive</code> 为 <code>true</code> 的用户。</p>
<pre><code class="language-tsx">import { filter } from 'rxjs/operators';
this.http.get<any[]>('<https://api.example.com/users>')
.pipe(
map(users => users.filter(user => user.isActive))
)
.subscribe(activeUsers => console.log(activeUsers));
</code></pre>
<ol>
<li><strong><code>switchMap</code></strong> - 取消旧的订阅,处理最新数据</li>
</ol>
<p><code>switchMap</code> 用于在每次接收到新数据时取消上一个未完成的 Observable,常用于处理嵌套请求或连续事件(如表单输入、路由参数变化)。</p>
<p><strong>示例</strong>:根据用户输入的关键字自动搜索,并取消上一次未完成的请求。</p>
<pre><code class="language-tsx">import { switchMap, debounceTime } from 'rxjs/operators';
import { FormControl } from '@angular/forms';
searchControl = new FormControl();
this.searchControl.valueChanges
.pipe(
debounceTime(300),// 防抖,避免过于频繁的请求
switchMap(query => this.http.get(`https://api.example.com/search?q=${query}`))
)
.subscribe(results => console.log(results));
</code></pre>
<ol>
<li><strong><code>mergeMap</code></strong> - 并行处理</li>
</ol>
<p><code>mergeMap</code> 可以将每个数据项映射到一个新的 Observable,并行处理每个 Observable。</p>
<p><strong>示例</strong>:并行请求多个用户的详细信息。</p>
<pre><code class="language-tsx">import { mergeMap } from 'rxjs/operators';
const userIds = ;
from(userIds)
.pipe(
mergeMap(id => this.http.get(`https://api.example.com/users/${id}`))
)
.subscribe(user => console.log(user));
</code></pre>
<ol>
<li><strong><code>catchError</code></strong> - 错误处理</li>
</ol>
<p><code>catchError</code> 用于捕获数据流中的错误,进行相应处理。</p>
<p><strong>示例</strong>:请求失败时返回一个默认值。</p>
<pre><code class="language-tsx">import { catchError } from 'rxjs/operators';
import { of } from 'rxjs';
this.http.get('<https://api.example.com/data>')
.pipe(
catchError(error => {
console.error('Error occurred:', error);
return of([]);// 返回一个空数组作为默认值
})
)
.subscribe(data => console.log(data));
</code></pre>
<hr>
<h3 id="rxjs-中的流控制">RxJS 中的流控制</h3>
<p>RxJS 提供了一些流控制操作符,例如 <code>debounceTime</code> 和 <code>distinctUntilChanged</code>,可以帮助处理用户输入、点击等事件流。</p>
<ol>
<li><strong><code>debounceTime</code></strong> - 防抖</li>
</ol>
<p><code>debounceTime</code> 用于控制数据的流速,在一定时间内没有新的数据到达时才发送数据,通常用于处理快速连续的输入。</p>
<p><strong>示例</strong>:在用户停止输入 500 毫秒后执行搜索请求。</p>
<pre><code class="language-tsx">searchControl.valueChanges
.pipe(
debounceTime(500)
)
.subscribe(value => console.log('Search:', value));
</code></pre>
<ol>
<li><strong><code>distinctUntilChanged</code></strong> - 去重</li>
</ol>
<p><code>distinctUntilChanged</code> 会忽略与上一次相同的数据项,避免重复处理。</p>
<p><strong>示例</strong>:用户输入相同内容时,不重复发送请求。</p>
<pre><code class="language-tsx">searchControl.valueChanges
.pipe(
debounceTime(500),
distinctUntilChanged()
)
.subscribe(value => console.log('Unique search:', value));
</code></pre>
<h3 id="rxjs-在-angular-中的应用场景">RxJS 在 Angular 中的应用场景</h3>
<ul>
<li><strong>HTTP 请求</strong>:通过 <code>HttpClient</code> 的返回值使用 RxJS 操作符,方便地处理请求数据和错误。</li>
<li><strong>路由参数变化</strong>:监听路由参数变化,进行依赖请求。</li>
<li><strong>表单输入处理</strong>:处理用户输入,进行防抖、去重等操作。</li>
<li><strong>组件间通信</strong>:通过 Subject 实现组件之间的事件或数据传递。</li>
</ul>
<h3 id="示例基于-rxjs-构建实时搜索">示例:基于 RxJS 构建实时搜索</h3>
<p>以下是一个完整示例,展示了如何使用 <code>switchMap</code>、<code>debounceTime</code> 和 <code>distinctUntilChanged</code> 构建一个实时搜索功能:</p>
<pre><code class="language-tsx">import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';
import { HttpClient } from '@angular/common/http';
import { debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators';
@Component({
selector: 'app-search',
template: `
<input ="searchControl" placeholder="Search...">
<ul>
<li *ngFor="let result of results">{{ result.name }}</li>
</ul>
`
})
export class SearchComponent {
searchControl = new FormControl();
results: any[] = [];
constructor(private http: HttpClient) {
this.searchControl.valueChanges
.pipe(
debounceTime(300),
distinctUntilChanged(),
switchMap(query => this.http.get<any[]>(`https://api.example.com/search?q=${query}`))
)
.subscribe(data => this.results = data);
}
}
</code></pre>
<p>在这个示例中:</p>
<ul>
<li><code>debounceTime(300)</code> 防抖,用户停止输入 300 毫秒后才会发送请求。</li>
<li><code>distinctUntilChanged()</code> 避免重复请求相同的搜索词。</li>
<li><code>switchMap</code> 每次有新输入时会取消之前的请求,避免频繁网络请求的性能消耗。</li>
</ul>
<h2 id="状态管理">状态管理</h2>
<p>在复杂的 Angular 应用中,状态管理是一个重要的主题。良好的状态管理能够帮助应用在不同组件和模块之间共享数据、同步状态、简化数据流、提高应用的可维护性。Angular 提供了多种方式进行状态管理,最常见的方式包括通过服务共享数据、使用 <code>@ngrx/store</code> 等库进行集中式管理。</p>
<hr>
<p><strong>状态管理</strong> 是一种在应用中管理和共享状态(数据)的方法,帮助我们更好地控制数据流、更新应用中的 UI。当应用变得复杂,包含多个交互式页面、模块、用户操作等,不同组件可能需要访问和更新相同的数据(例如用户信息、购物车数据等),状态管理可以帮助我们保持数据一致性并减少数据同步的复杂性。</p>
<h3 id="angular-中的状态管理方式">Angular 中的状态管理方式</h3>
<ol>
<li>使用服务进行状态共享</li>
</ol>
<p>Angular 的服务在应用中是单例的,可以通过服务在多个组件之间共享状态,适合不需要复杂状态逻辑的小型应用。</p>
<p><strong>示例</strong>:创建一个简单的 <code>UserService</code>,用于管理用户信息。</p>
<pre><code class="language-tsx">import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class UserService {
private userSource = new BehaviorSubject<User | null>(null); // 存储用户信息
user$ = this.userSource.asObservable(); // 公开用户信息为 Observable
setUser(user: User) {
this.userSource.next(user); // 更新用户信息
}
clearUser() {
this.userSource.next(null); // 清除用户信息
}
}
</code></pre>
<p>在组件中,可以通过 <code>UserService</code> 访问和更新用户状态:</p>
<pre><code class="language-tsx">@Component({
selector: 'app-profile',
template: `<div *ngIf="user$ | async as user">{{ user.name }}</div>`
})
export class ProfileComponent {
user$ = this.userService.user$;
constructor(private userService: UserService) {}
}
</code></pre>
<p>在另一个组件中更新用户信息:</p>
<pre><code class="language-tsx">@Component({
selector: 'app-login',
template: `<button (click)="login()">Login</button>`
})
export class LoginComponent {
constructor(private userService: UserService) {}
login() {
const user = { name: 'Alice', email: 'alice@example.com' };
this.userService.setUser(user); // 更新用户信息
}
}
</code></pre>
<ol>
<li>使用 <code>@ngrx/store</code> 进行集中式状态管理</li>
</ol>
<p>对于大型应用,可以使用 <code>@ngrx/store</code> 库进行集中式状态管理。<code>@ngrx/store</code> 实现了 Redux 模式,能够将所有应用状态集中管理,并通过单一数据源来同步和更新状态。</p>
<ul>
<li><code>@ngrx/store</code> 的核心概念
<ul>
<li><strong>Store</strong>:存储应用的全局状态。所有组件都可以从 <code>Store</code> 获取和更新数据。</li>
<li><strong>Actions</strong>:触发状态改变的事件,用于描述要进行的状态更新。</li>
<li><strong>Reducers</strong>:处理 <code>Actions</code> 的逻辑,根据不同的 <code>Actions</code> 更新 <code>Store</code> 中的状态。</li>
<li><strong>Selectors</strong>:从 <code>Store</code> 中获取所需的状态数据。</li>
</ul>
</li>
<li>安装 <code>@ngrx/store</code></li>
</ul>
<p>首先,使用 Angular CLI 安装 <code>@ngrx/store</code>:</p>
<pre><code class="language-bash">ng add @ngrx/store
</code></pre>
<ul>
<li>创建一个状态管理示例</li>
</ul>
<p>假设我们要管理一个简单的计数器状态,包括 <code>increment</code> 和 <code>decrement</code> 两个操作。</p>
<ol>
<li>
<p><strong>定义 Action</strong></p>
<p>在 <code>counter.actions.ts</code> 文件中定义计数器的 Action:</p>
<pre><code class="language-tsx">import { createAction } from '@ngrx/store';
export const increment = createAction(' Increment');
export const decrement = createAction(' Decrement');
export const reset = createAction(' Reset');
</code></pre>
</li>
<li>
<p><strong>定义 Reducer</strong></p>
<p>在 <code>counter.reducer.ts</code> 文件中定义计数器的 Reducer:</p>
<pre><code class="language-tsx">import { createReducer, on } from '@ngrx/store';
import { increment, decrement, reset } from './counter.actions';
export const initialState = 0;
const _counterReducer = createReducer(
initialState,
on(increment, state => state + 1),
on(decrement, state => state - 1),
on(reset, state => initialState)
);
export function counterReducer(state: any, action: any) {
return _counterReducer(state, action);
}
</code></pre>
</li>
<li>
<p><strong>注册 Reducer</strong></p>
<p>在应用的 <code>app.module.ts</code> 中,将 Reducer 注册到 Store:</p>
<pre><code class="language-tsx">import { StoreModule } from '@ngrx/store';
import { counterReducer } from './counter.reducer';
@NgModule({
imports: [
StoreModule.forRoot({ count: counterReducer })
],
bootstrap:
})
export class AppModule {}
</code></pre>
</li>
<li>
<p><strong>使用 Store 在组件中管理状态</strong></p>
<p>在组件中使用 Store 获取和更新计数器状态:</p>
<pre><code class="language-tsx">import { Component } from '@angular/core';
import { Store } from '@ngrx/store';
import { increment, decrement, reset } from './counter.actions';
@Component({
selector: 'app-counter',
template: `
<p>Count: {{ count$ | async }}</p>
<button (click)="increment()">Increment</button>
<button (click)="decrement()">Decrement</button>
<button (click)="reset()">Reset</button>
`
})
export class CounterComponent {
count$ = this.store.select('count'); // 获取 count 状态
constructor(private store: Store<{ count: number }>) {}
increment() {
this.store.dispatch(increment());
}
decrement() {
this.store.dispatch(decrement());
}
reset() {
this.store.dispatch(reset());
}
}
</code></pre>
</li>
</ol>
<p>在这个示例中:</p>
<ul>
<li><strong>Actions</strong> 定义了增减和重置计数器的操作。</li>
<li><strong>Reducer</strong> 根据不同的 <code>Action</code> 更新状态。</li>
<li><strong>Store</strong> 提供了 <code>count</code> 状态,组件可以从 Store 订阅状态数据并进行相应的操作。</li>
</ul>
<hr>
<h3 id="状态管理的最佳实践">状态管理的最佳实践</h3>
<ol>
<li><strong>集中管理应用状态</strong>:将共享状态集中存储在服务或 Store 中,避免状态在不同组件中重复存储。</li>
<li><strong>避免直接修改状态</strong>:通过 Action 和 Reducer 进行状态更新,确保状态变化是可追溯的。</li>
<li><strong>分离 UI 和业务逻辑</strong>:组件负责 UI 呈现,服务或 Store 负责管理业务逻辑和数据状态。</li>
<li><strong>使用 Selectors</strong>:使用 Selectors 获取 Store 中的数据,保持数据访问的简单性和一致性。</li>
</ol>
<hr>
<h2 id="优化和性能调优">优化和性能调优</h2>
<p>在构建和部署 Angular 应用时,性能优化是确保应用快速加载和响应的关键步骤。Angular 提供了多种优化技术,如懒加载、AOT(提前编译)、Tree Shaking、变化检测策略等。本节将介绍这些优化技巧,帮助你提高应用的性能。</p>
<hr>
<h3 id="懒加载lazy-loading-1">懒加载(Lazy Loading)</h3>
<p><strong>懒加载</strong>是一种按需加载模块的技术。它允许应用在用户需要时才加载特定模块,避免应用启动时加载所有内容,从而减少首屏加载时间,提升应用的启动速度。</p>
<p>实现懒加载</p>
<p>假设有一个 <code>AdminModule</code> 模块,我们可以在路由中配置懒加载来实现按需加载。</p>
<p><strong>示例</strong>:</p>
<pre><code class="language-tsx">import { Routes } from '@angular/router';
const routes: Routes = [
{ path: 'admin', loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule) },
];
</code></pre>
<p>在这里,当用户导航到 <code>/admin</code> 路径时,Angular 才会加载 <code>AdminModule</code> 模块。</p>
<hr>
<h3 id="aot-编译ahead-of-time-compilation">AOT 编译(Ahead-Of-Time Compilation)</h3>
<p><strong>AOT 编译</strong>将 Angular 的模板在构建时提前编译为 JavaScript 代码,而不是在浏览器中实时编译。这可以减少浏览器的工作量,减少应用包大小并提高运行速度。</p>
<p>使用 AOT 编译</p>
<p>使用 Angular CLI 构建应用时,AOT 编译是默认开启的,可以通过 <code>ng build --prod</code> 命令构建生产环境版本,启用 AOT 和其他优化。</p>
<pre><code class="language-bash">ng build --prod
</code></pre>
<p>AOT 编译的好处:</p>
<ul>
<li><strong>提高运行速度</strong>:减少浏览器的编译时间。</li>
<li><strong>更小的包大小</strong>:只打包已编译的模板代码。</li>
<li><strong>检测模板错误</strong>:在构建时发现模板语法错误,增加代码的安全性。</li>
</ul>
<hr>
<h3 id="tree-shaking">Tree Shaking</h3>
<p><strong>Tree Shaking</strong> 是一种在构建过程中移除未使用代码的技术。Angular 使用 Webpack 构建,它会自动移除未使用的模块和代码,减小应用的包体积。</p>
<p>如何优化 Tree Shaking</p>
<ol>
<li><strong>使用 ES6 模块</strong>:确保代码使用 ES6 模块语法(<code>import</code> 和 <code>export</code>)。</li>
<li><strong>移除不必要的依赖</strong>:确保只引入和使用必要的库或模块。</li>
<li><strong>优化 RxJS 导入</strong>:RxJS 库可以按需引入。例如,使用 <code>import { map } from 'rxjs/operators'</code> 而不是导入整个 <code>rxjs</code> 库。</li>
</ol>
<hr>
<h3 id="变化检测策略change-detection-strategy">变化检测策略(Change Detection Strategy)</h3>
<p>Angular 默认的变化检测机制会检测所有组件的变化,可能导致性能开销。可以使用 <code>OnPush</code> 策略优化变化检测,使组件只在输入数据发生变化或组件内部事件触发时才进行检测。</p>
<p>使用 <code>OnPush</code> 策略</p>
<p>在组件中设置 <code>changeDetection: ChangeDetectionStrategy.OnPush</code> 来启用 <code>OnPush</code> 策略。</p>
<pre><code class="language-tsx">import { ChangeDetectionStrategy, Component } from '@angular/core';
@Component({
selector: 'app-my-component',
templateUrl: './my-component.component.html',
changeDetection: ChangeDetectionStrategy.OnPush// 启用 OnPush 策略
})
export class MyComponent {}
</code></pre>
<p>使用 <code>OnPush</code> 策略的好处:</p>
<ul>
<li><strong>减少不必要的变化检测</strong>:只有在输入数据或事件触发时才会重新渲染。</li>
<li><strong>提高性能</strong>:适合纯展示型组件,不依赖父组件频繁更新的数据。</li>
</ul>
<hr>
<h3 id="使用-service-workers-进行缓存">使用 Service Workers 进行缓存</h3>
<p>Angular 支持将应用配置为 <strong>渐进式 Web 应用(PWA)</strong>,利用 Service Worker 缓存资源以提高离线支持和性能。</p>
<p>添加 Service Worker 支持</p>
<p>使用 Angular CLI 添加 Service Worker:</p>
<pre><code class="language-bash">ng add @angular/pwa
</code></pre>
<p>这会自动生成 Service Worker 配置文件并注册到应用中。在生产环境中,Service Worker 会自动缓存静态资源,加快页面加载速度。</p>
<hr>
<h3 id="优化图片和静态资源">优化图片和静态资源</h3>
<p>在 Web 应用中,图片和静态资源通常占据较大体积,可以通过压缩和延迟加载等方式优化资源。</p>
<p>图片压缩和延迟加载</p>
<ul>
<li><strong>使用压缩格式</strong>:尽量使用体积较小的图片格式(如 WebP)。</li>
<li><strong>懒加载图片</strong>:可以通过 <code>loading="lazy"</code> 属性实现延迟加载,只有当图片进入视口时才加载。</li>
</ul>
<pre><code class="language-html"><img src="image.webp" loading="lazy" alt="Example Image">
</code></pre>
<hr>
<h3 id="使用-angular-的内置优化工具">使用 Angular 的内置优化工具</h3>
<p>Angular CLI 提供了许多内置工具,帮助优化应用的包体积和性能。通过以下方式可以进一步优化构建:</p>
<p><code>ng build --prod</code> 的默认优化</p>
<p>使用 <code>ng build --prod</code> 时,Angular CLI 会自动应用多种优化,包括:</p>
<ul>
<li><strong>AOT 编译</strong>:提前编译模板。</li>
<li><strong>Tree Shaking</strong>:移除未使用代码。</li>
<li><strong>Minification</strong>:压缩代码。</li>
<li><strong>Bundle Splitting</strong>:分割代码,减少单个包的大小。</li>
</ul>
<hr>
<h3 id="提升-angular-性能的最佳实践">提升 Angular 性能的最佳实践</h3>
<ol>
<li><strong>按需加载模块</strong>:利用懒加载按需加载特定模块,减少主包大小。</li>
<li><strong>使用纯展示型组件</strong>:在没有状态依赖的组件上启用 <code>OnPush</code> 策略,减少变化检测次数。</li>
<li><strong>避免在模板中进行复杂计算</strong>:在组件类中进行计算,将结果传递到模板中。</li>
<li><strong>优化第三方库的引入</strong>:使用按需导入的方式引入第三方库(如 RxJS 操作符)。</li>
<li><strong>使用 Web Workers</strong>:将计算密集型任务交给 Web Worker,避免阻塞主线程。</li>
</ol>
<hr>
<h2 id="pwa-和国际化">PWA 和国际化</h2>
<p>在本节中,我们将介绍两个重要的进阶主题:<strong>渐进式 Web 应用(PWA)</strong> 和 <strong>国际化(i18n)</strong>。PWA 可以让 Angular 应用具备离线支持和原生应用的体验,而国际化则使应用能够支持多语言,方便在全球范围内使用。</p>
<hr>
<h3 id="渐进式-web-应用pwa">渐进式 Web 应用(PWA)</h3>
<p><strong>渐进式 Web 应用(PWA)</strong> 是一种利用现代 Web 技术构建的应用,使得 Web 应用能够像原生应用一样流畅运行,并具有离线功能。Angular 提供了内置的 PWA 支持,可以帮助开发者轻松构建具有离线支持、推送通知等功能的 Web 应用。</p>
<ul>
<li>将 Angular 应用转换为 PWA</li>
</ul>
<p>Angular CLI 提供了简便的命令来将现有项目转换为 PWA 应用。</p>
<ol>
<li>
<p>添加 PWA 支持</p>
<p>在项目目录中运行以下命令:</p>
<pre><code class="language-bash">ng add @angular/pwa
</code></pre>
<p>执行命令后,Angular 会自动生成 Service Worker 配置文件(<code>ngsw-config.json</code>)以及应用的图标(<code>manifest.webmanifest</code> 文件)。这些文件可以配置应用的离线缓存和图标等 PWA 设置。</p>
</li>
<li>
<p>配置 <code>ngsw-config.json</code></p>
<p><code>ngsw-config.json</code> 是 Service Worker 的配置文件,用于定义哪些文件会被缓存。默认情况下,Angular 会缓存应用的主要资源(如 JavaScript 文件、CSS 文件、HTML 文件等)。可以根据需要自定义缓存策略。</p>
</li>
<li>
<p>构建生产版本</p>
<p>使用 PWA 时,应用需要以生产模式运行:</p>
<pre><code class="language-bash">ng build --prod
</code></pre>
</li>
<li>
<p>部署</p>
<p>将应用部署到服务器后,浏览器会自动检测并注册 Service Worker,使应用具备离线功能。</p>
</li>
</ol>
<ul>
<li>验证 PWA 功能</li>
</ul>
<ol>
<li>打开应用后,在浏览器的开发者工具中检查是否成功注册了 Service Worker。</li>
<li>可以尝试关闭网络,然后刷新页面,应用仍然应该能够加载离线缓存的内容。</li>
</ol>
<hr>
<h3 id="国际化">国际化</h3>
<p><strong>国际化</strong> 是将应用中的文本、日期格式、数字格式等内容转换为不同语言和地区格式的过程,使应用可以适应不同的语言和文化背景。Angular 提供了内置的国际化支持,帮助开发者轻松添加多语言。</p>
<ul>
<li>使用 Angular 的 i18n 功能</li>
</ul>
<p>Angular 的 i18n 功能可以帮助将应用文本进行多语言翻译。以下是配置步骤:</p>
<ol>
<li>标记文本</li>
</ol>
<p>在模板中使用 <code>i18n</code> 属性标记需要翻译的文本。</p>
<pre><code class="language-html"><h1 i18n="@@welcome">Welcome to our app!</h1>
<p i18n="@@intro">This is an example of internationalized content.</p>
</code></pre>
<p>在这里,<code>i18n="@@key"</code> 用于给文本内容添加一个唯一的翻译键 <code>key</code>,方便后续查找和翻译。</p>
<ol>
<li>提取翻译文件</li>
</ol>
<p>使用 Angular CLI 提取翻译文件。运行以下命令会在 <code>src/locale</code> 目录下生成一个 <code>messages.xlf</code> 文件:</p>
<pre><code class="language-bash">ng extract-i18n
</code></pre>
<p>生成的 <code>messages.xlf</code> 是一个 XML 文件,包含应用中所有标记的翻译内容。</p>
<ol>
<li>翻译内容</li>
</ol>
<p>在 <code>messages.xlf</code> 文件中为每种语言创建翻译文件,翻译内容并保存文件。例如,可以创建 <code>messages.fr.xlf</code> 文件用于法语翻译。</p>
<pre><code class="language-xml"><trans-unit id="welcome" datatype="html">
<source>Welcome to our app!</source>
<target>Bienvenue dans notre application!</target>
</trans-unit>
<trans-unit id="intro" datatype="html">
<source>This is an example of internationalized content.</source>
<target>Ceci est un exemple de contenu internationalisé.</target>
</trans-unit>
</code></pre>
<ol>
<li>配置多语言编译</li>
</ol>
<p>在 <code>angular.json</code> 文件中添加多语言配置。例如:</p>
<pre><code class="language-json">"projects": {
"your-app-name": {
"i18n": {
"sourceLocale": "en",
"locales": {
"fr": "src/locale/messages.fr.xlf"
}
}
}
}
</code></pre>
<ol>
<li>构建多语言版本</li>
</ol>
<p>在构建应用时,可以为不同语言生成不同版本:</p>
<pre><code class="language-bash">ng build --prod --localize
</code></pre>
<p>这样会自动为每个语言生成对应的版本(例如 <code>dist/your-app-name/fr</code> 目录下会生成法语版本的应用)。</p>
<ul>
<li>动态切换语言(可选)</li>
</ul>
<p>如果希望在运行时动态切换语言,可以使用第三方库(如 <code>ngx-translate</code>)实现更灵活的国际化支持。</p><br><br>
来源:https://www.cnblogs.com/dreaife/p/18543217
頁:
[1]