组件概述 #

一、什么是组件 #

组件是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