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