关于 angular-core
提供 Angular 开发规范的 Skill,涵盖独立组件、Signals、函数式输入输出和无 Zone 变更检测,并说明何时使用 RxJS。
主要功能
- 01独立组件规范
- 02函数式输入与输出
- 03使用 Signals 管理状态和副作用
- 04避免使用生命周期钩子
- 05使用 inject() 注入依赖
- 06无 Zone 变更检测
适用场景
- 01构建独立 Angular 组件
- 02使用 Signals 管理组件状态
- 03用 effect 替代生命周期钩子
- 04配置无 Zone 的 Angular 应用
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ gentleman-programming/ gentleman-skills --skill angular-core安装后在 Claude Code 中输入/angular-core确认已加载。
详细介绍
独立组件(必需)
组件默认是独立的。不要设置 standalone: true。
@Component({
selector: 'app-user',
imports: [CommonModule],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `...`
})
export class UserComponent {}
输入/输出函数(必需)
// ✅ 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>();
使用信号管理状态(必需)
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()));
不使用生命周期钩子(必需)
信号可替代生命周期钩子。不要使用 ngOnInit、ngOnChanges、ngOnDestroy。
// ❌ 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');
何时使用什么
| 需求 | 使用 |
|---|---|
| 响应输入变化 | 使用 effect() 监听输入信号 |
| 派生/计算状态 | computed() |
| 副作用(API 调用、localStorage) | effect() |
| 销毁时清理 | DestroyRef + inject() |
// Cleanup example
private readonly destroyRef = inject(DestroyRef);
constructor() {
const subscription = someObservable$.subscribe();
this.destroyRef.onDestroy(() => subscription.unsubscribe());
}
优先使用 inject(),而非构造函数(必需)
// ✅ ALWAYS
private readonly http = inject(HttpClient);
// ❌ NEVER
constructor(private http: HttpClient) {}
使用原生控制流(必需)
@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> }
}
RxJS——仅在需要时使用
默认使用信号。仅对复杂异步操作使用 RxJS。
| 使用信号 | 使用 RxJS |
|---|---|
| 组件状态 | 合并多个流 |
| 派生值 | 防抖/节流 |
| 简单异步操作(单次 API 调用) | 竞态条件 |
| 输入/输出 | WebSocket、实时功能 |
| 复杂的错误重试逻辑 |
// ✅ 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: [] });
无 Zone Angular(必需)
Angular 不使用 Zone。使用 provideZonelessChangeDetection()。
bootstrapApplication(AppComponent, {
providers: [provideZonelessChangeDetection()]
});
移除 ZoneJS:
npm uninstall zone.js
从 angular.json 的 polyfills 中移除:zone.js 和 zone.js/testing。
无 Zone 的要求
- 使用
OnPush变更检测 - 使用信号管理状态(自动通知 Angular)
- 对可观察对象使用
AsyncPipe - 需要时使用
markForCheck()
资源
评论
支持 Markdown