组件概述 #
一、什么是组件 #
组件是Angular应用的基本构建块。每个组件由三个部分组成:
- TypeScript类:处理数据和逻辑
- HTML模板:定义视图
- CSS样式:定义外观
text
┌─────────────────────────────────────┐
│ Component │
├─────────────────────────────────────┤
│ TypeScript类 (逻辑与数据) │
│ HTML模板 (视图结构) │
│ CSS样式 (外观表现) │
└─────────────────────────────────────┘
二、创建组件 #
2.1 使用CLI创建 #
bash
ng generate component user
# 简写
ng g c user
2.2 CLI选项 #
bash
# 生成内联模板
ng g c user --inline-template
# 简写
ng g c user -t
# 生成内联样式
ng g c user --inline-style
# 简写
ng g c user -s
# 指定选择器前缀
ng g c user --prefix my
# 跳过测试文件
ng g c user --skip-tests
# 指定变更检测策略
ng g c user --change-detection OnPush
# 创建独立组件
ng g c user --standalone
2.3 手动创建组件 #
typescript
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-user',
standalone: true,
imports: [CommonModule],
templateUrl: './user.component.html',
styleUrl: './user.component.css'
})
export class UserComponent {
name = 'Angular';
}
三、组件元数据 #
3.1 @Component装饰器 #
typescript
@Component({
selector: 'app-user', // 组件选择器
standalone: true, // 独立组件标志
imports: [CommonModule], // 导入模块
templateUrl: './user.component.html', // 模板URL
template: `<p>内联模板</p>`, // 内联模板
styleUrl: './user.component.css', // 样式URL
styles: ['p { color: red; }'], // 内联样式
styleUrls: ['./user.component.css', './shared.css'], // 多样式文件
changeDetection: ChangeDetectionStrategy.OnPush, // 变更检测策略
encapsulation: ViewEncapsulation.Emulated, // 样式封装
providers: [UserService], // 组件级服务
host: { // 宿主元素绑定
'[class.active]': 'isActive',
'(click)': 'onClick()'
}
})
export class UserComponent { }
3.2 元数据配置项说明 #
| 配置项 | 说明 |
|---|---|
selector |
组件的CSS选择器 |
standalone |
是否为独立组件 |
imports |
导入的模块(独立组件) |
template/templateUrl |
组件模板 |
styles/styleUrls/styleUrl |
组件样式 |
changeDetection |
变更检测策略 |
encapsulation |
样式封装模式 |
providers |
组件级服务提供者 |
host |
宿主元素绑定 |
四、选择器类型 #
4.1 元素选择器(推荐) #
typescript
@Component({
selector: 'app-user',
template: '<p>User Component</p>'
})
html
<app-user></app-user>
4.2 属性选择器 #
typescript
@Component({
selector: '[appUser]',
template: '<p>User Component</p>'
})
html
<div appUser></div>
4.3 类选择器 #
typescript
@Component({
selector: '.app-user',
template: '<p>User Component</p>'
})
html
<div class="app-user"></div>
4.4 不推荐的选择器 #
typescript
// 不推荐:ID选择器
selector: '#app-user'
// 不推荐:组合选择器
selector: 'div.app-user'
五、模板定义方式 #
5.1 外部模板文件 #
typescript
@Component({
selector: 'app-user',
templateUrl: './user.component.html'
})
5.2 内联模板 #
typescript
@Component({
selector: 'app-user',
template: `
<div class="user">
<h2>{{ name }}</h2>
<p>{{ description }}</p>
</div>
`
})
5.3 选择建议 #
| 场景 | 推荐方式 |
|---|---|
| 模板超过3行 | 外部模板文件 |
| 模板少于3行 | 内联模板 |
| 复杂组件 | 外部模板文件 |
| 简单组件 | 内联模板 |
六、样式定义方式 #
6.1 外部样式文件 #
typescript
@Component({
selector: 'app-user',
styleUrl: './user.component.css'
})
6.2 多个样式文件 #
typescript
@Component({
selector: 'app-user',
styleUrls: ['./user.component.css', './shared.css']
})
6.3 内联样式 #
typescript
@Component({
selector: 'app-user',
styles: [`
.user {
padding: 10px;
border: 1px solid #ccc;
}
h2 {
color: blue;
}
`]
})
七、样式封装 #
7.1 ViewEncapsulation选项 #
typescript
import { Component, ViewEncapsulation } from '@angular/core';
@Component({
selector: 'app-user',
template: '<p class="text">Hello</p>',
styles: ['.text { color: red; }'],
encapsulation: ViewEncapsulation.Emulated // 默认值
})
7.2 封装模式对比 #
| 模式 | 说明 | 效果 |
|---|---|---|
Emulated |
模拟封装(默认) | 样式仅作用于组件内部 |
None |
无封装 | 样式全局生效 |
ShadowDom |
使用Shadow DOM | 原生样式隔离 |
7.3 示例对比 #
typescript
// Emulated - 模拟封装
@Component({
selector: 'app-user',
template: '<p class="text">Hello</p>',
styles: ['.text { color: red; }'],
encapsulation: ViewEncapsulation.Emulated
})
// 生成的HTML: <p class="text _ngcontent-abc">Hello</p>
// 样式只影响本组件
// None - 无封装
@Component({
encapsulation: ViewEncapsulation.None
})
// 样式会影响整个应用
// ShadowDom - 原生隔离
@Component({
encapsulation: ViewEncapsulation.ShadowDom
})
// 使用浏览器原生Shadow DOM
八、变更检测策略 #
8.1 Default策略 #
typescript
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
selector: 'app-user',
template: '{{ name }}',
changeDetection: ChangeDetectionStrategy.Default
})
export class UserComponent {
name = 'Angular';
}
8.2 OnPush策略 #
typescript
@Component({
selector: 'app-user',
template: '{{ user.name }}',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class UserComponent {
@Input() user: User;
}
8.3 策略对比 #
| 策略 | 触发检测时机 | 性能 | 适用场景 |
|---|---|---|---|
Default |
任何异步事件 | 较低 | 简单组件 |
OnPush |
输入属性变化、手动触发 | 较高 | 性能敏感组件 |
8.4 OnPush最佳实践 #
typescript
import { Component, ChangeDetectionStrategy, Input, ChangeDetectorRef } from '@angular/core';
@Component({
selector: 'app-user',
template: '{{ name }}',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class UserComponent {
@Input() user: User;
constructor(private cdr: ChangeDetectorRef) {}
updateName(name: string) {
this.user.name = name;
this.cdr.markForCheck();
}
}
九、组件交互 #
9.1 输入属性 #
typescript
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-user-card',
template: `
<div class="card">
<h3>{{ user.name }}</h3>
<p>{{ user.email }}</p>
</div>
`
})
export class UserCardComponent {
@Input() user: User;
@Input() isHighlighted: boolean = false;
}
9.2 输出属性 #
typescript
import { Component, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-user-card',
template: `
<button (click)="onSelect()">选择</button>
`
})
export class UserCardComponent {
@Output() userSelected = new EventEmitter<User>();
onSelect() {
this.userSelected.emit(this.user);
}
}
9.3 使用示例 #
typescript
@Component({
selector: 'app-user-list',
template: `
<app-user-card
[user]="selectedUser"
[isHighlighted]="isHighlighted"
(userSelected)="onUserSelected($event)">
</app-user-card>
`
})
export class UserListComponent {
selectedUser: User;
isHighlighted = true;
onUserSelected(user: User) {
console.log('Selected:', user);
}
}
十、组件最佳实践 #
10.1 单一职责 #
typescript
// 好的做法:职责单一
@Component({
selector: 'app-user-avatar'
})
export class UserAvatarComponent {
@Input() avatarUrl: string;
}
@Component({
selector: 'app-user-info'
})
export class UserInfoComponent {
@Input() user: User;
}
// 不好的做法:职责过多
@Component({
selector: 'app-user'
})
export class UserComponent {
// 包含头像、信息、编辑、删除等功能
}
10.2 使用OnPush优化性能 #
typescript
@Component({
selector: 'app-user-list',
template: `
<app-user-card
*ngFor="let user of users"
[user]="user">
</app-user-card>
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class UserListComponent {
@Input() users: User[];
}
10.3 合理使用输入输出 #
typescript
// 好的做法
@Component({
selector: 'app-button',
template: `
<button
[disabled]="disabled"
[class]="variant"
(click)="onClick()">
<ng-content></ng-content>
</button>
`
})
export class ButtonComponent {
@Input() disabled = false;
@Input() variant: 'primary' | 'secondary' = 'primary';
@Output() buttonClick = new EventEmitter<void>();
onClick() {
if (!this.disabled) {
this.buttonClick.emit();
}
}
}
十一、总结 #
| 概念 | 说明 |
|---|---|
| 组件选择器 | 定义组件的使用方式 |
| 模板 | 定义组件的视图结构 |
| 样式 | 定义组件的外观 |
| 元数据 | 配置组件的行为 |
| 输入属性 | 接收父组件数据 |
| 输出属性 | 向父组件发送事件 |
下一步:模板与数据绑定
最后更新:2026-03-26