1. @Component 装饰器
Angular 组件由三部分构成:TypeScript 类(逻辑)、HTML 模板(视图)、CSS 样式。它们通过 @Component 装饰器绑定在一起。
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-user-card', // CSS 选择器,在模板中用 <app-user-card>
standalone: true, // Standalone 模式(Angular 17+ 默认)
imports: [CommonModule], // 声明此组件使用的依赖
templateUrl: './user-card.component.html',
styleUrl: './user-card.component.scss',
})
export class UserCardComponent implements OnInit {
@Input() name: string = ''; // 父 → 子 输入属性
@Input() age: number = 0;
@Output() clicked = new EventEmitter<string>(); // 子 → 父 输出事件
ngOnInit(): void {
console.log(`UserCard 初始化:${this.name}`);
}
onClickHandler(): void {
this.clicked.emit(this.name);
}
}
@Component 关键元数据
- selector组件的 CSS 选择器,决定在模板中如何使用该组件(通常是自定义元素名)。
- standalone设为 true 表示该组件是 Standalone 组件,不属于任何 NgModule。Angular 17+ 新建项目默认为 true。
- importsStandalone 组件专有:声明模板中用到的其他组件、指令、管道(替代 NgModule 的 declarations)。
- template / templateUrl内联模板或外部 HTML 文件路径。小组件用内联,大组件用外部文件。
- styles / styleUrl组件样式,默认受 ViewEncapsulation 保护,不会泄漏到外部。
2. 模板语法:绑定一览
Angular 模板语法是对标准 HTML 的扩展,提供了四种核心绑定方式:
| 绑定类型 | 语法 | 说明 | 方向 |
|---|---|---|---|
| 插值 | {{ expression }} | 将表达式结果插入文本 | 组件 → 视图 |
| 属性绑定 | [property]="expression" | 绑定 DOM 属性 | 组件 → 视图 |
| 事件绑定 | (event)="handler()" | 监听 DOM 事件 | 视图 → 组件 |
| 双向绑定 | [(ngModel)]="property" | 属性+事件组合 | 双向 |
<!-- 插值:把组件属性渲染为文本 -->
<h1>{{ title }}</h1>
<p>总价:{{ price * quantity | currency:'CNY' }}</p>
<!-- 属性绑定:设置 DOM 属性(方括号) -->
<img [src]="user.avatarUrl" [alt]="user.name">
<button [disabled]="isLoading">提交</button>
<div [class.active]="isActive">状态</div>
<div [style.color]="isDanger ? 'red' : 'green'">颜色</div>
<!-- 事件绑定:监听事件(圆括号) -->
<button (click)="onSubmit()">提交</button>
<input (input)="onInputChange($event)">
<form (ngSubmit)="onFormSubmit()"></form>
<!-- 双向绑定:需要 FormsModule -->
<input [(ngModel)]="username" placeholder="用户名">
<p>你输入了:{{ username }}</p>
3. 新控制流语法(Angular 17+)
Angular 17 引入了全新的内置控制流语法,用 @if、@for、@switch 替代旧版的结构型指令 *ngIf 和 *ngFor。新语法性能提升约 90%,且无需导入 CommonModule。
@if 条件渲染
<!-- 新语法(Angular 17+)-->
@if (user.isLoggedIn) {
<div class="welcome">欢迎,{{ user.name }}!</div>
} @else if (user.isGuest) {
<div class="guest">访客模式</div>
} @else {
<button (click)="login()">立即登录</button>
}
<!-- 旧语法(仍有效,但不推荐用于新项目)-->
<div *ngIf="user.isLoggedIn; else loginBlock">欢迎,{{ user.name }}!</div>
<ng-template #loginBlock><button>登录</button></ng-template>
@for 列表渲染
<!-- 新语法:必须提供 track 表达式(性能关键)-->
<ul>
@for (item of products; track item.id) {
<li>{{ item.name }} - ¥{{ item.price }}</li>
} @empty {
<li>暂无商品</li>
}
</ul>
<!-- 可访问内置变量 -->
@for (user of users; track user.id; let i = $index, isLast = $last) {
<div [class.last]="isLast">{{ i + 1 }}. {{ user.name }}</div>
}
<!-- 旧语法 -->
<li *ngFor="let item of products; trackBy: trackById">{{ item.name }}</li>
@switch 多条件渲染
@switch (status) {
@case ('loading') {
<app-spinner />
}
@case ('success') {
<app-data-table [data]="data" />
}
@case ('error') {
<app-error-message [msg]="errorMsg" />
}
@default {
<p>未知状态</p>
}
}
@defer 懒加载(Angular 17+)
@defer 允许声明式地延迟加载某块内容,直到特定条件满足时才渲染,实现组件级别的代码分割。
<!-- 视口可见时才加载 -->
@defer (on viewport) {
<app-heavy-chart [data]="chartData" />
} @loading {
<p>图表加载中...</p>
} @placeholder {
<div class="chart-skeleton"></div>
}
<!-- 空闲时加载 -->
@defer (on idle) {
<app-footer />
}
<!-- 交互后加载 -->
@defer (on interaction(trigger)) {
<app-dialog />
}
<button #trigger>打开对话框</button>
4. Standalone Component 详解
Standalone Component 是 Angular 14 引入、17+ 默认的组件模式。它的关键特征是:组件自己声明自己的依赖,不再需要 NgModule 作为容器。
// 一个完整的 Standalone 组件示例
import { Component, signal, computed } from '@angular/core';
import { CurrencyPipe } from '@angular/common';
import { RouterLink } from '@angular/router';
import { ProductCardComponent } from './product-card.component';
@Component({
selector: 'app-shop',
standalone: true,
imports: [
CurrencyPipe, // Angular 内置管道
RouterLink, // Angular 路由指令
ProductCardComponent, // 自定义子组件
],
template: `
<h1>商品列表({{ total() }} 件)</h1>
@for (product of products(); track product.id) {
<app-product-card [product]="product" />
}
<p>总价:{{ totalPrice() | currency:'CNY' }}</p>
<a routerLink="/checkout">去结算</a>
`
})
export class ShopComponent {
products = signal([
{ id: 1, name: 'Angular 入门书', price: 89 },
{ id: 2, name: 'TypeScript 手册', price: 69 },
]);
total = computed(() => this.products().length);
totalPrice = computed(() =>
this.products().reduce((sum, p) => sum + p.price, 0)
);
}
5. 输入/输出属性
Angular 18 新增了函数式 API input() 和 output(),与 Signals 更好地集成,逐渐替代 @Input() 和 @Output() 装饰器。
import { Component, input, output } from '@angular/core';
@Component({
selector: 'app-button',
standalone: true,
template: `
<button [class]="variant()" (click)="click.emit()">
{{ label() }}
</button>
`
})
export class ButtonComponent {
// input() 返回一个 Signal,可直接在模板和代码中用 label()
label = input<string>('点击'); // 有默认值
variant = input.required<string>(); // 必填
click = output<void>(); // 替代 @Output
}
<!-- 父组件使用 -->
<app-button label="提交" variant="primary" (click)="handleClick()" />
6. 样式封装 ViewEncapsulation
Angular 组件的样式默认是局部作用域的——不会影响其他组件,也不会被外部样式覆盖。这是通过 ViewEncapsulation 实现的。
-
Emulated(默认)
Angular 为组件的 HTML 元素和 CSS 选择器添加唯一属性(如
_ngcontent-abc-c1),模拟 Shadow DOM 的样式隔离效果。兼容性好,推荐使用。 - ShadowDom 使用浏览器原生 Shadow DOM,样式完全隔离。适合可复用的 UI 组件库,但可能与某些第三方样式冲突。
- None 关闭样式封装,组件样式变为全局样式。适合定义全局样式,一般不推荐用于普通业务组件。
import { Component, ViewEncapsulation } from '@angular/core';
@Component({
selector: 'app-global-styles',
standalone: true,
template: `<div class="my-class">内容</div>`,
styles: [`.my-class { color: red; }`],
encapsulation: ViewEncapsulation.None // 全局生效
})
export class GlobalStylesComponent {}
本章小结:Angular 组件通过 @Component 装饰器将逻辑、模板、样式绑定在一起。新的 @if/@for 控制流语法性能更好,Standalone 组件让代码更简洁。下一章深入依赖注入系统。