Skip to content

Angular 开发指南

简介

Angular 是一个由 Google 维护的开源 Web 应用框架,用于构建单页应用程序。本文将介绍 Angular 的核心概念、组件系统和最佳实践。

基础概念

1. 组件

typescript
// app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <h1>{{ title }}</h1>
    <button (click)="increment()">Count: {{ count }}</button>
  `,
  styles: [`
    h1 { color: blue; }
    button { padding: 10px; }
  `]
})
export class AppComponent {
  title = 'My Angular App';
  count = 0;

  increment() {
    this.count++;
  }
}

2. 模块

typescript
// app.module.ts
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 { }

数据绑定

1. 属性绑定

typescript
// 组件
@Component({
  selector: 'app-user',
  template: `
    <div [class.active]="isActive">
      <img [src]="user.avatar" [alt]="user.name">
      <p [style.color]="textColor">{{ user.name }}</p>
    </div>
  `
})
export class UserComponent {
  isActive = true;
  textColor = 'blue';
  user = {
    name: 'John Doe',
    avatar: '/assets/avatar.jpg'
  };
}

2. 事件绑定

typescript
@Component({
  selector: 'app-counter',
  template: `
    <button (click)="increment()">+</button>
    <span>{{ count }}</span>
    <button (click)="decrement()">-</button>
  `
})
export class CounterComponent {
  count = 0;

  increment() {
    this.count++;
  }

  decrement() {
    this.count--;
  }
}

服务

1. 服务定义

typescript
// user.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class UserService {
  private apiUrl = 'api/users';

  constructor(private http: HttpClient) {}

  getUsers(): Observable<User[]> {
    return this.http.get<User[]>(this.apiUrl);
  }

  getUser(id: number): Observable<User> {
    return this.http.get<User>(`${this.apiUrl}/${id}`);
  }

  createUser(user: User): Observable<User> {
    return this.http.post<User>(this.apiUrl, user);
  }
}

2. 服务使用

typescript
// user-list.component.ts
import { Component, OnInit } from '@angular/core';
import { UserService } from './user.service';

@Component({
  selector: 'app-user-list',
  template: `
    <ul>
      <li *ngFor="let user of users">
        {{ user.name }}
      </li>
    </ul>
  `
})
export class UserListComponent implements OnInit {
  users: User[] = [];

  constructor(private userService: UserService) {}

  ngOnInit() {
    this.userService.getUsers().subscribe(
      users => this.users = users
    );
  }
}

路由

1. 路由配置

typescript
// app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HomeComponent } from './home.component';
import { UserListComponent } from './user-list.component';
import { UserDetailComponent } from './user-detail.component';

const routes: Routes = [
  { path: '', component: HomeComponent },
  { path: 'users', component: UserListComponent },
  { path: 'users/:id', component: UserDetailComponent },
  { path: '**', redirectTo: '' }
];

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

2. 路由使用

typescript
// app.component.ts
@Component({
  selector: 'app-root',
  template: `
    <nav>
      <a routerLink="/">Home</a>
      <a routerLink="/users">Users</a>
    </nav>
    <router-outlet></router-outlet>
  `
})
export class AppComponent { }

// user-detail.component.ts
@Component({
  selector: 'app-user-detail',
  template: `
    <div *ngIf="user">
      <h2>{{ user.name }}</h2>
      <p>{{ user.email }}</p>
    </div>
  `
})
export class UserDetailComponent implements OnInit {
  user: User;

  constructor(
    private route: ActivatedRoute,
    private userService: UserService
  ) {}

  ngOnInit() {
    this.route.params.subscribe(params => {
      this.userService.getUser(params['id']).subscribe(
        user => this.user = user
      );
    });
  }
}

状态管理

1. NgRx

typescript
// user.actions.ts
import { createAction, props } from '@ngrx/store';

export const loadUsers = createAction('[User] Load Users');
export const loadUsersSuccess = createAction(
  '[User] Load Users Success',
  props<{ users: User[] }>()
);

// user.reducer.ts
import { createReducer, on } from '@ngrx/store';
import * as UserActions from './user.actions';

export const userFeatureKey = 'user';

export interface State {
  users: User[];
  loading: boolean;
}

export const initialState: State = {
  users: [],
  loading: false
};

export const reducer = createReducer(
  initialState,
  on(UserActions.loadUsers, state => ({
    ...state,
    loading: true
  })),
  on(UserActions.loadUsersSuccess, (state, { users }) => ({
    ...state,
    users,
    loading: false
  }))
);

2. 使用 Store

typescript
// user-list.component.ts
import { Component, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { loadUsers } from './user.actions';
import { selectUsers, selectLoading } from './user.selectors';

@Component({
  selector: 'app-user-list',
  template: `
    <div *ngIf="loading$ | async">Loading...</div>
    <ul>
      <li *ngFor="let user of users$ | async">
        {{ user.name }}
      </li>
    </ul>
  `
})
export class UserListComponent implements OnInit {
  users$: Observable<User[]>;
  loading$: Observable<boolean>;

  constructor(private store: Store) {
    this.users$ = this.store.select(selectUsers);
    this.loading$ = this.store.select(selectLoading);
  }

  ngOnInit() {
    this.store.dispatch(loadUsers());
  }
}

表单处理

1. 模板驱动表单

typescript
// user-form.component.ts
@Component({
  selector: 'app-user-form',
  template: `
    <form #userForm="ngForm" (ngSubmit)="onSubmit(userForm)">
      <div>
        <label for="name">Name:</label>
        <input
          id="name"
          name="name"
          [(ngModel)]="user.name"
          required
          #name="ngModel"
        >
        <div *ngIf="name.invalid && (name.dirty || name.touched)">
          Name is required
        </div>
      </div>
      
      <div>
        <label for="email">Email:</label>
        <input
          id="email"
          name="email"
          [(ngModel)]="user.email"
          required
          email
          #email="ngModel"
        >
        <div *ngIf="email.invalid && (email.dirty || email.touched)">
          Valid email is required
        </div>
      </div>
      
      <button type="submit" [disabled]="userForm.invalid">
        Submit
      </button>
    </form>
  `
})
export class UserFormComponent {
  user = {
    name: '',
    email: ''
  };

  onSubmit(form: NgForm) {
    if (form.valid) {
      console.log(this.user);
    }
  }
}

2. 响应式表单

typescript
// user-form.component.ts
@Component({
  selector: 'app-user-form',
  template: `
    <form [formGroup]="userForm" (ngSubmit)="onSubmit()">
      <div>
        <label for="name">Name:</label>
        <input
          id="name"
          formControlName="name"
        >
        <div *ngIf="name.invalid && (name.dirty || name.touched)">
          <div *ngIf="name.errors?.['required']">
            Name is required
          </div>
        </div>
      </div>
      
      <div>
        <label for="email">Email:</label>
        <input
          id="email"
          formControlName="email"
        >
        <div *ngIf="email.invalid && (email.dirty || email.touched)">
          <div *ngIf="email.errors?.['required']">
            Email is required
          </div>
          <div *ngIf="email.errors?.['email']">
            Valid email is required
          </div>
        </div>
      </div>
      
      <button type="submit" [disabled]="userForm.invalid">
        Submit
      </button>
    </form>
  `
})
export class UserFormComponent implements OnInit {
  userForm: FormGroup;

  constructor(private fb: FormBuilder) {}

  ngOnInit() {
    this.userForm = this.fb.group({
      name: ['', Validators.required],
      email: ['', [Validators.required, Validators.email]]
    });
  }

  onSubmit() {
    if (this.userForm.valid) {
      console.log(this.userForm.value);
    }
  }
}

性能优化

1. 变更检测

typescript
// 使用 OnPush 策略
@Component({
  selector: 'app-user',
  template: `...`,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class UserComponent {
  @Input() user: User;
}

// 使用 trackBy
@Component({
  selector: 'app-user-list',
  template: `
    <ul>
      <li *ngFor="let user of users; trackBy: trackByFn">
        {{ user.name }}
      </li>
    </ul>
  `
})
export class UserListComponent {
  users: User[] = [];

  trackByFn(index: number, user: User): number {
    return user.id;
  }
}

2. 懒加载

typescript
// app-routing.module.ts
const routes: Routes = [
  {
    path: 'users',
    loadChildren: () => import('./users/users.module')
      .then(m => m.UsersModule)
  }
];

// users.module.ts
@NgModule({
  declarations: [
    UserListComponent,
    UserDetailComponent
  ],
  imports: [
    CommonModule,
    RouterModule.forChild([
      { path: '', component: UserListComponent },
      { path: ':id', component: UserDetailComponent }
    ])
  ]
})
export class UsersModule { }

最佳实践

1. 项目结构

bash
src/
  app/
    core/
      services/
      guards/
      interceptors/
    shared/
      components/
      directives/
      pipes/
    features/
      users/
        components/
        services/
        models/
    app.component.ts
    app.module.ts
    app-routing.module.ts
  assets/
  environments/

2. 代码组织

typescript
// 使用接口定义模型
export interface User {
  id: number;
  name: string;
  email: string;
}

// 使用枚举
export enum UserRole {
  Admin = 'admin',
  User = 'user'
}

// 使用常量
export const API_URL = 'api/v1';
export const DEFAULT_PAGE_SIZE = 10;

工具链

1. 开发工具

json
{
  "dependencies": {
    "@angular/core": "^15.0.0",
    "@angular/common": "^15.0.0",
    "@angular/forms": "^15.0.0",
    "@angular/router": "^15.0.0",
    "@ngrx/store": "^15.0.0",
    "@ngrx/effects": "^15.0.0"
  },
  "devDependencies": {
    "@angular/cli": "^15.0.0",
    "@angular/compiler-cli": "^15.0.0",
    "@angular/language-service": "^15.0.0",
    "typescript": "~4.8.0"
  }
}

2. 测试工具

json
{
  "devDependencies": {
    "@angular/core/testing": "^15.0.0",
    "@angular/platform-browser-dynamic/testing": "^15.0.0",
    "jasmine-core": "~4.5.0",
    "karma": "~6.4.0",
    "karma-chrome-launcher": "~3.1.0",
    "karma-coverage": "~2.2.0",
    "karma-jasmine": "~5.1.0",
    "karma-jasmine-html-reporter": "~2.0.0"
  }
}

总结

Angular 提供了完整的框架解决方案,包括组件系统、依赖注入、路由、表单处理等功能。通过合理使用这些特性和遵循最佳实践,可以构建出可扩展、可维护的企业级应用程序。

参考资源