angular-core
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseStandalone Components (REQUIRED)
独立组件(强制要求)
Components are standalone by default. Do NOT set .
standalone: truetypescript
@Component({
selector: 'app-user',
imports: [CommonModule],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `...`
})
export class UserComponent {}组件默认是独立的,不要设置。
standalone: truetypescript
@Component({
selector: 'app-user',
imports: [CommonModule],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `...`
})
export class UserComponent {}Input/Output Functions (REQUIRED)
输入/输出函数(强制要求)
typescript
// ✅ ALWAYS: Function-based
readonly user = input.required<User>();
readonly disabled = input(false);
readonly selected = output<User>();
readonly checked = model(false); // Two-way binding
// ❌ NEVER: Decorators
@Input() user: User;
@Output() selected = new EventEmitter<User>();typescript
// ✅ 推荐写法:基于函数
readonly user = input.required<User>();
readonly disabled = input(false);
readonly selected = output<User>();
readonly checked = model(false); // 双向绑定
// ❌ 禁止写法:装饰器
@Input() user: User;
@Output() selected = new EventEmitter<User>();Signals for State (REQUIRED)
使用Signals管理状态(强制要求)
typescript
readonly count = signal(0);
readonly doubled = computed(() => this.count() * 2);
// Update
this.count.set(5);
this.count.update(prev => prev + 1);
// Side effects
effect(() => localStorage.setItem('count', this.count().toString()));typescript
readonly count = signal(0);
readonly doubled = computed(() => this.count() * 2);
// 更新值
this.count.set(5);
this.count.update(prev => prev + 1);
// 副作用处理
effect(() => localStorage.setItem('count', this.count().toString()));NO Lifecycle Hooks (REQUIRED)
禁止使用生命周期钩子(强制要求)
Signals replace lifecycle hooks. Do NOT use , , .
ngOnInitngOnChangesngOnDestroytypescript
// ❌ NEVER: Lifecycle hooks
ngOnInit() {
this.loadUser();
}
ngOnChanges(changes: SimpleChanges) {
if (changes['userId']) {
this.loadUser();
}
}
// ✅ ALWAYS: Signals + effect
readonly userId = input.required<string>();
readonly user = signal<User | null>(null);
private userEffect = effect(() => {
// Runs automatically when userId() changes
this.loadUser(this.userId());
});
// ✅ For derived data, use computed
readonly displayName = computed(() => this.user()?.name ?? 'Guest');Signals可替代生命周期钩子,不要使用、、。
ngOnInitngOnChangesngOnDestroytypescript
// ❌ 禁止写法:生命周期钩子
ngOnInit() {
this.loadUser();
}
ngOnChanges(changes: SimpleChanges) {
if (changes['userId']) {
this.loadUser();
}
}
// ✅ 推荐写法:Signals + effect
readonly userId = input.required<string>();
readonly user = signal<User | null>(null);
private userEffect = effect(() => {
// 当userId()变化时自动执行
this.loadUser(this.userId());
});
// ✅ 派生数据使用computed
readonly displayName = computed(() => this.user()?.name ?? 'Guest');When to Use What
场景对应方案
| Need | Use |
|---|---|
| React to input changes | |
| Derived/computed state | |
| Side effects (API calls, localStorage) | |
| Cleanup on destroy | |
typescript
// Cleanup example
private readonly destroyRef = inject(DestroyRef);
constructor() {
const subscription = someObservable$.subscribe();
this.destroyRef.onDestroy(() => subscription.unsubscribe());
}| 需求 | 推荐使用 |
|---|---|
| 响应输入变化 | 用 |
| 派生/计算状态 | |
| 副作用处理(API调用、localStorage) | |
| 销毁时清理资源 | |
typescript
// 资源清理示例
private readonly destroyRef = inject(DestroyRef);
constructor() {
const subscription = someObservable$.subscribe();
this.destroyRef.onDestroy(() => subscription.unsubscribe());
}inject() Over Constructor (REQUIRED)
优先使用inject()而非构造函数(强制要求)
typescript
// ✅ ALWAYS
private readonly http = inject(HttpClient);
// ❌ NEVER
constructor(private http: HttpClient) {}typescript
// ✅ 推荐写法
private readonly http = inject(HttpClient);
// ❌ 禁止写法
constructor(private http: HttpClient) {}Native Control Flow (REQUIRED)
原生模板控制流(强制要求)
html
@if (loading()) {
<spinner />
} @else {
@for (item of items(); track item.id) {
<item-card [data]="item" />
} @empty {
<p>No items</p>
}
}
@switch (status()) {
@case ('active') { <span>Active</span> }
@default { <span>Unknown</span> }
}html
@if (loading()) {
<spinner />
} @else {
@for (item of items(); track item.id) {
<item-card [data]="item" />
} @empty {
<p>暂无数据</p>
}
}
@switch (status()) {
@case ('active') { <span>活跃</span> }
@default { <span>未知</span> }
}RxJS - Only When Needed
RxJS:仅在必要时使用
Signals are the default. Use RxJS ONLY for complex async operations.
| Use Signals | Use RxJS |
|---|---|
| Component state | Combining multiple streams |
| Derived values | Debounce/throttle |
| Simple async (single API call) | Race conditions |
| Input/Output | WebSockets, real-time |
| Complex error retry logic |
typescript
// ✅ Simple API call - use signals
readonly user = signal<User | null>(null);
readonly loading = signal(false);
async loadUser(id: string) {
this.loading.set(true);
this.user.set(await firstValueFrom(this.http.get<User>(`/api/users/${id}`)));
this.loading.set(false);
}
// ✅ Complex stream - use RxJS
readonly searchResults$ = this.searchTerm$.pipe(
debounceTime(300),
distinctUntilChanged(),
switchMap(term => this.http.get<Results>(`/api/search?q=${term}`))
);
// Convert to signal when needed in template
readonly searchResults = toSignal(this.searchResults$, { initialValue: [] });Signals是默认方案,仅在处理复杂异步操作时使用RxJS。
| 使用Signals的场景 | 使用RxJS的场景 |
|---|---|
| 组件状态管理 | 多流合并 |
| 派生值计算 | 防抖/节流 |
| 简单异步操作(单次API调用) | 竞态条件处理 |
| 输入/输出处理 | WebSocket、实时数据 |
| 复杂错误重试逻辑 |
typescript
// ✅ 简单API调用:使用Signals
readonly user = signal<User | null>(null);
readonly loading = signal(false);
async loadUser(id: string) {
this.loading.set(true);
this.user.set(await firstValueFrom(this.http.get<User>(`/api/users/${id}`)));
this.loading.set(false);
}
// ✅ 复杂流处理:使用RxJS
readonly searchResults$ = this.searchTerm$.pipe(
debounceTime(300),
distinctUntilChanged(),
switchMap(term => this.http.get<Results>(`/api/search?q=${term}`))
);
// 模板中需要时转换为signal
readonly searchResults = toSignal(this.searchResults$, { initialValue: [] });Zoneless Angular (REQUIRED)
无Zone Angular(强制要求)
Angular is zoneless. Use .
provideZonelessChangeDetection()typescript
bootstrapApplication(AppComponent, {
providers: [provideZonelessChangeDetection()]
});Remove ZoneJS:
bash
npm uninstall zone.jsRemove from polyfills: and .
angular.jsonzone.jszone.js/testing采用无Zone的Angular,使用。
provideZonelessChangeDetection()typescript
bootstrapApplication(AppComponent, {
providers: [provideZonelessChangeDetection()]
});移除ZoneJS:
bash
npm uninstall zone.js从的polyfills中移除:和。
angular.jsonzone.jszone.js/testingZoneless Requirements
无Zone环境要求
- Use change detection
OnPush - Use signals for state (auto-notifies Angular)
- Use for observables
AsyncPipe - Use when needed
markForCheck()
- 使用变更检测策略
OnPush - 使用Signals管理状态(自动通知Angular)
- 对可观察对象使用
AsyncPipe - 必要时使用
markForCheck()