第1章:Angular核心概念——模块化、依赖注入、TypeScript基础、RxJS入门
大家好,我是你们的前端老司机。今天咱们聊聊Angular。
说实话,Angular在圈子里一直是个「争议人物」。有人嫌它重,有人嫌它学习曲线陡。但我个人觉得,它其实是框架界的「瑞士军刀」——功能齐全,但得会用。
这一章,我带你拆解Angular的四个核心概念。搞懂了它们,你基本就拿到了Angular的入场券。
1.1 模块化:Angular的「乐高积木」
Angular的模块化,说白了就是「分而治之」。你想想看,一个大型应用动辄几十万行代码,全塞在一起谁受得了?
Angular用 NgModule 来组织代码。每个模块就是一个功能单元,有自己的组件、服务、指令。
核心要点:一个Angular应用至少有一个根模块(AppModule),其他功能模块按需引入。
我在项目中遇到过最典型的场景:一个电商后台,订单管理、商品管理、用户管理各占一个模块。这样团队分工明确,互不干扰。
// 一个典型的模块定义
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
@NgModule({
declarations: [AppComponent], // 声明组件
imports: [BrowserModule], // 引入其他模块
providers: [], // 注册服务
bootstrap: [AppComponent] // 启动组件
})
export class AppModule { }
嗯,这里要注意:declarations 只放当前模块的组件,别把别的模块的组件也塞进来。我曾经见过一个新手把所有组件都堆在一个模块里,结果编译慢得像蜗牛。
1.2 依赖注入:Angular的「后勤部长」
依赖注入(DI)是Angular最强大的特性之一。它解决了一个核心问题:对象之间的依赖关系谁来管?
你想想看,如果每个组件都自己去new一个服务,那代码耦合度得多高?改个服务构造函数,所有组件都得跟着改。
我的习惯:把可复用的逻辑(如API请求、数据缓存)都封装成服务,通过DI注入到组件中。这样组件只关心UI,服务只关心业务。
Angular的DI分三步:
- 提供服务:在模块或组件的
providers里注册 - 注入依赖:在组件的构造函数里声明
- 使用服务:直接调用服务的方法
// 定义一个服务
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root' // 全局单例
})
export class DataService {
getData() {
return ['Angular', 'React', 'Vue'];
}
}
// 在组件中注入
import { Component } from '@angular/core';
import { DataService } from './data.service';
@Component({
selector: 'app-list',
template: `<ul><li *ngFor="let item of items">{{item}}</li></ul>`
})
export class ListComponent {
items: string[];
constructor(private dataService: DataService) {
this.items = this.dataService.getData();
}
}
避坑指南:我曾经在大型项目中遇到过「循环依赖」的问题——服务A依赖服务B,服务B又依赖服务A。Angular会直接报错。解决办法是用 Injector 手动注入,或者重构代码消除循环。
1.3 TypeScript基础:Angular的「骨架」
Angular从出生就拥抱TypeScript。为什么?因为类型系统能帮你提前发现80%的运行时错误。
我个人觉得,TypeScript对Angular来说不是「可选项」,而是「必需品」。你想想看,没有类型约束,你怎么保证一个组件接收的参数一定是字符串而不是数字?
Angular中常用的TypeScript特性:
| 特性 | 说明 | Angular中的典型用法 |
|---|---|---|
| 接口(Interface) | 定义数据结构 | 定义组件输入输出的类型 |
| 装饰器(Decorator) | 给类添加元数据 | @Component、@Injectable |
| 泛型(Generics) | 类型参数化 | HttpClient的响应类型 |
| 访问修饰符 | 控制成员可见性 | private、public |
// 接口定义
export interface User {
id: number;
name: string;
email: string;
role: 'admin' | 'user';
}
// 组件中使用
@Component({...})
export class UserProfileComponent {
@Input() user: User; // 类型约束
updateEmail(newEmail: string) {
// 类型安全
this.user.email = newEmail;
}
}
小技巧:我习惯在项目根目录建一个 types 文件夹,专门放全局类型定义。这样所有模块都能复用,不用到处重复定义。
1.4 RxJS入门:Angular的「异步引擎」
RxJS是Angular处理异步操作的基石。说白了,它就是「用观察者模式处理数据流」。
你可能会问:为什么不用Promise?Promise只能处理一次异步结果,而RxJS的Observable可以处理「持续的数据流」——比如用户输入、WebSocket消息、HTTP请求的多次响应。
RxJS的核心概念就三个:
- Observable:数据生产者,可以持续发出值
- Observer:数据消费者,订阅Observable
- Operators:操作符,对数据流进行变换、过滤、组合
// 一个简单的RxJS示例
import { Observable } from 'rxjs';
import { map, filter } from 'rxjs/operators';
// 创建Observable
const source$ = new Observable<number>(observer => {
observer.next(1);
observer.next(2);
observer.next(3);
observer.complete();
});
// 使用操作符处理数据流
const processed$ = source$.pipe(
filter(x => x > 1), // 过滤掉1
map(x => x * 10) // 乘以10
);
// 订阅
processed$.subscribe({
next: value => console.log(value), // 输出: 20, 30
error: err => console.error(err),
complete: () => console.log('完成')
});
Angular中的RxJS应用场景:
- HttpClient的请求返回Observable
- 表单控件的值变化(valueChanges)
- 路由参数的变化(params)
- 组件间的通信(Subject)
我记得刚开始用RxJS时,最头疼的就是「什么时候该取消订阅」。后来养成了习惯:在组件销毁时调用 unsubscribe(),或者用 async 管道让Angular自动管理。
避坑指南:我曾经在项目中遇到「内存泄漏」问题——一个组件订阅了某个Observable,但组件销毁时没取消订阅。结果这个组件被反复创建销毁,订阅越来越多,最后浏览器直接卡死。解决方案:用 takeUntil 操作符,配合一个Subject来统一管理取消订阅。
// 安全的订阅方式
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
export class MyComponent implements OnDestroy {
private destroy$ = new Subject<void>();
ngOnInit() {
this.http.get('/api/data')
.pipe(takeUntil(this.destroy$))
.subscribe(data => {
// 处理数据
});
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
}
好了,这一章的内容就到这里。模块化让你组织代码,依赖注入让你解耦,TypeScript让你安全,RxJS让你处理异步。这四个概念就像Angular的「四根柱子」,撑起了整个框架。
下一章,我们聊聊Angular的组件通信——父子组件、兄弟组件、跨层级组件,各种姿势都有。到时候见!
公众号:蓝海资料掘金营,微信deep3321