Angular 生态完整指南

HTMLPAGE 团队
8 分钟阅读

详细讲解 Angular 框架的核心概念、依赖注入系统、装饰器、RxJS 基础、模块系统和常用库生态。适合想要系统学习 Angular 的开发者。

#Angular #TypeScript #依赖注入 #RxJS #框架生态

Angular 生态完整指南

什么是 Angular

Angular 是由 Google 开发的一个完整的 TypeScript 框架。与 React 和 Vue 不同,Angular 是一个全功能框架,提供了开发企业级应用所需的所有工具和模式。

Angular 的核心特点:

┌─────────────────────────────────┐
│ 完整的框架                      │
├─────────────────────────────────┤
│ ✅ 依赖注入(DI)               │
│ ✅ 装饰器(Decorators)         │
│ ✅ RxJS 响应式编程              │
│ ✅ 模块系统(Modules)          │
│ ✅ 路由系统                      │
│ ✅ 表单系统                      │
│ ✅ HTTP 客户端                   │
│ ✅ 测试框架                      │
└─────────────────────────────────┘

1. 核心架构

1.1 模块系统

Angular 应用由模块组成。每个模块是一个用 @NgModule 装饰器标记的类。

// app.module.ts
import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { AppComponent } from './app.component'
import { HomeComponent } from './pages/home.component'
import { RouterModule } from '@angular/router'

@NgModule({
  // 声明该模块拥有的组件
  declarations: [
    AppComponent,
    HomeComponent
  ],
  
  // 导入其他模块的功能
  imports: [
    BrowserModule,
    RouterModule.forRoot([
      { path: '', component: HomeComponent }
    ])
  ],
  
  // 提供的服务(依赖注入)
  providers: [
    // 默认为单例,应用级别
  ],
  
  // 启动组件
  bootstrap: [AppComponent]
})
export class AppModule {}

1.2 组件和模板

// 组件定义
@Component({
  selector: 'app-counter',
  template: `
    <div>
      <p>Count: {{ count }}</p>
      <button (click)="increment()">+1</button>
    </div>
  `,
  styles: [`
    div { padding: 20px; }
    button { background: #007bff; color: white; }
  `]
})
export class CounterComponent {
  count = 0
  
  increment() {
    this.count++
  }
}

2. 依赖注入(DI)系统

2.1 DI 基础

依赖注入是 Angular 的核心,允许类声明它们需要什么依赖,而不需要自己创建。

// service.ts - 服务定义
import { Injectable } from '@angular/core'

@Injectable({
  providedIn: 'root'  // 在根模块提供,应用级单例
})
export class UserService {
  getUser(id: number) {
    return { id, name: 'John', email: 'john@example.com' }
  }
}

// component.ts - 组件注入服务
import { Component } from '@angular/core'

@Component({
  selector: 'app-user',
  template: `
    <div>
      <p>用户:{{ user?.name }}</p>
      <p>邮箱:{{ user?.email }}</p>
    </div>
  `
})
export class UserComponent {
  user: any
  
  // 通过构造函数注入 UserService
  constructor(private userService: UserService) {
    this.user = this.userService.getUser(1)
  }
}

2.2 DI 提供器

// 不同的提供器类型

// 1. 类提供器
providers: [
  UserService  // 简写方式,等同于 { provide: UserService, useClass: UserService }
]

// 2. 工厂提供器
providers: [
  {
    provide: 'USER_SERVICE',
    useFactory: () => {
      // 运行时决定返回什么
      return new UserService()
    }
  }
]

// 3. 值提供器
const appConfig = { apiUrl: 'https://api.example.com' }
providers: [
  { provide: 'APP_CONFIG', useValue: appConfig }
]

// 4. 别名提供器
providers: [
  OldUserService,
  { provide: NewUserService, useExisting: OldUserService }
]

// 使用 @Inject 获取值提供器
import { Inject } from '@angular/core'

constructor(@Inject('APP_CONFIG') config) {
  console.log(config.apiUrl)
}

3. 装饰器(Decorators)

3.1 类装饰器

// @Component
@Component({
  selector: 'app-button',
  template: '<button><ng-content></ng-content></button>'
})
export class ButtonComponent {}

// @Directive
@Directive({
  selector: '[appHighlight]'
})
export class HighlightDirective {
  constructor(el: ElementRef) {
    el.nativeElement.style.backgroundColor = 'yellow'
  }
}

// @Injectable
@Injectable()
export class DataService {
  constructor(private http: HttpClient) {}
}

// @NgModule
@NgModule({
  declarations: [],
  imports: [],
  providers: []
})
export class FeatureModule {}

3.2 属性装饰器

@Component({
  selector: 'app-list-item',
  template: '<p>{{ title }} - ${{ price }}</p>'
})
export class ListItemComponent {
  // 输入属性 - 接收父组件的值
  @Input() title: string = ''
  @Input() price: number = 0
  
  // 输出属性 - 向父组件发送事件
  @Output() selected = new EventEmitter<string>()
  
  onClick() {
    this.selected.emit(this.title)
  }
}

// 使用
<app-list-item 
  [title]="'产品 A'"
  [price]="99"
  (selected)="onSelected($event)">
</app-list-item>

4. RxJS 响应式编程

4.1 Observable 基础

import { Observable } from 'rxjs'

// 创建 Observable
const observable = new Observable(subscriber => {
  subscriber.next('值 1')
  subscriber.next('值 2')
  subscriber.complete()
})

// 订阅 Observable
observable.subscribe({
  next: (value) => console.log(value),
  error: (err) => console.error(err),
  complete: () => console.log('完成')
})

// 从数组创建
import { of, from } from 'rxjs'

of(1, 2, 3).subscribe(v => console.log(v))
from([1, 2, 3]).subscribe(v => console.log(v))

4.2 常用操作符

import { map, filter, switchMap, debounceTime, tap } from 'rxjs/operators'

// 数据转换
source$.pipe(
  map(x => x * 2),
  filter(x => x > 5)
).subscribe(result => console.log(result))

// 搜索功能
searchInput$.pipe(
  debounceTime(300),  // 等待 300ms
  map(term => this.searchService.search(term)),
  switchMap(result => result)  // 切换到最新搜索
).subscribe(results => {
  this.results = results
})

// 调试
data$.pipe(
  tap(value => console.log('值:', value)),
  map(value => value * 2),
  tap(value => console.log('处理后:', value))
).subscribe()

4.3 在 Angular 中的应用

// 数据流管理
@Injectable()
export class ProductService {
  private products$ = this.http.get('/api/products').pipe(
    map(data => data.products),
    tap(products => console.log('产品加载:', products)),
    catchError(err => {
      console.error('错误:', err)
      return of([])
    })
  )
  
  constructor(private http: HttpClient) {}
  
  getProducts() {
    return this.products$
  }
}

// 在组件中使用
@Component({
  template: `
    <div>
      <div *ngFor="let product of products$ | async">
        {{ product.name }} - ${{ product.price }}
      </div>
    </div>
  `
})
export class ProductListComponent {
  products$ = this.productService.getProducts()
  
  constructor(private productService: ProductService) {}
}

5. 路由系统

5.1 基本路由

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

const routes: Routes = [
  { 
    path: '', 
    component: HomeComponent,
    data: { title: '首页' }
  },
  { 
    path: 'about', 
    component: AboutComponent 
  },
  { 
    path: 'product/:id', 
    component: ProductDetailComponent 
  },
  // 重定向
  { 
    path: 'old-page', 
    redirectTo: '/new-page' 
  },
  // 懒加载
  { 
    path: 'feature',
    loadChildren: () => import('./feature/feature.module')
      .then(m => m.FeatureModule)
  },
  // 通配符(必须最后)
  { 
    path: '**', 
    component: NotFoundComponent 
  }
]

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule {}

5.2 路由守卫

import { Injectable } from '@angular/core'
import { CanActivate, Router } from '@angular/router'

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private authService: AuthService, private router: Router) {}
  
  canActivate() {
    if (this.authService.isLoggedIn()) {
      return true
    }
    this.router.navigate(['/login'])
    return false
  }
}

// 使用守卫
const routes: Routes = [
  { 
    path: 'admin', 
    component: AdminComponent,
    canActivate: [AuthGuard]
  }
]

6. 表单系统

6.1 模板驱动表单

@Component({
  template: `
    <form (ngSubmit)="submit(form)" #form="ngForm">
      <input 
        name="username" 
        [(ngModel)]="user.username"
        required
      >
      
      <input 
        name="email" 
        [(ngModel)]="user.email"
        type="email"
        required
      >
      
      <button type="submit">提交</button>
      <p *ngIf="form.invalid">表单有效性:{{ form.valid }}</p>
    </form>
  `
})
export class LoginComponent {
  user = { username: '', email: '' }
  
  submit(form: NgForm) {
    if (form.valid) {
      console.log('提交:', this.user)
    }
  }
}

6.2 响应式表单

import { FormBuilder, FormGroup, Validators } from '@angular/forms'

@Component({
  template: `
    <form [formGroup]="form" (ngSubmit)="submit()">
      <input formControlName="username">
      <input formControlName="email" type="email">
      <button type="submit" [disabled]="form.invalid">提交</button>
    </form>
  `
})
export class LoginComponent {
  form: FormGroup
  
  constructor(private fb: FormBuilder) {
    this.form = this.fb.group({
      username: ['', Validators.required],
      email: ['', [Validators.required, Validators.email]]
    })
  }
  
  submit() {
    console.log(this.form.value)
  }
}

7. HTTP 通信

7.1 HttpClient

import { HttpClient, HttpErrorResponse } from '@angular/common/http'
import { catchError } from 'rxjs/operators'
import { throwError } from 'rxjs'

@Injectable()
export class ApiService {
  constructor(private http: HttpClient) {}
  
  // GET 请求
  getUsers() {
    return this.http.get<User[]>('/api/users')
  }
  
  // POST 请求
  createUser(user: User) {
    return this.http.post<User>('/api/users', user)
  }
  
  // PUT 请求
  updateUser(id: number, user: User) {
    return this.http.put<User>(`/api/users/${id}`, user)
  }
  
  // DELETE 请求
  deleteUser(id: number) {
    return this.http.delete(`/api/users/${id}`)
  }
  
  // 错误处理
  private handleError(error: HttpErrorResponse) {
    let errorMessage = '发生错误'
    if (error.error instanceof ErrorEvent) {
      errorMessage = error.error.message
    } else {
      errorMessage = `错误代码: ${error.status}`
    }
    return throwError(() => new Error(errorMessage))
  }
}

7.2 HTTP 拦截器

import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http'
import { Injectable } from '@angular/core'

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  intercept(req: HttpRequest<any>, next: HttpHandler) {
    // 添加认证令牌
    const authToken = localStorage.getItem('token')
    
    if (authToken) {
      const clonedReq = req.clone({
        setHeaders: {
          Authorization: `Bearer ${authToken}`
        }
      })
      return next.handle(clonedReq)
    }
    
    return next.handle(req)
  }
}

// 注册拦截器
@NgModule({
  providers: [
    { 
      provide: HTTP_INTERCEPTORS, 
      useClass: AuthInterceptor, 
      multi: true 
    }
  ]
})
export class AppModule {}

8. Angular 生态常用库

作用说明
@angular/materialUI 组件库官方 Material Design 组件
@ng-bootstrap/ng-bootstrapBootstrap 组件原生 Angular 组件
ngx-translate国际化多语言支持
ngx-pagination分页表格分页
ngx-toastr通知轻提示
angular-in-memory-web-api模拟 API开发测试
cypressE2E 测试端到端测试
karma + jasmine单元测试默认测试框架

9. Angular 最佳实践

// ✅ 最佳实践清单

// 1. 使用强类型
interface User {
  id: number
  name: string
  email: string
}

// 2. 使用 OnPush 变更检测提高性能
@Component({
  selector: 'app-user',
  template: '<p>{{ user.name }}</p>',
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class UserComponent {
  @Input() user: User
}

// 3. 适当取消订阅避免内存泄漏
export class DataComponent implements OnDestroy {
  private destroy$ = new Subject<void>()
  
  ngOnInit() {
    this.data$
      .pipe(takeUntil(this.destroy$))
      .subscribe(data => this.data = data)
  }
  
  ngOnDestroy() {
    this.destroy$.next()
    this.destroy$.complete()
  }
}

// 4. 使用异步管道简化
@Component({
  template: '<p>{{ data$ | async }}</p>'
})
export class DataComponent {
  data$ = this.dataService.getData()
  constructor(private dataService: DataService) {}
}

// 5. 分离关注点
// ✅ 服务处理数据,组件处理展示
@Injectable()
export class UserService {
  getUsers() { return this.http.get<User[]>('/api/users') }
}

@Component({
  template: '<div *ngFor="let user of users">{{ user.name }}</div>'
})
export class UserListComponent {
  users: User[] = []
  
  constructor(private userService: UserService) {
    this.userService.getUsers().subscribe(users => this.users = users)
  }
}

10. Angular vs React vs Vue

特性AngularReactVue
学习曲线
框架完整度完全最小中等
性能良好优秀优秀
生态完整最丰富良好
适用场景大型企业应用各种规模中小型项目
社区较小最大中等
就业需求中等最高中等

总结

Angular 作为一个完整的框架:

  • ✅ 提供了开发大型应用所需的所有工具
  • ✅ 强大的依赖注入系统
  • ✅ 完整的路由和表单系统
  • ✅ RxJS 响应式编程
  • ❌ 学习曲线陡峭
  • ❌ 对小型项目过度设计

Angular 特别适合:

  • 大型企业级应用
  • 需要强类型和模块化的项目
  • 团队已有 Java 或 .NET 背景

推荐资源