mcpField
A
angular-core

angular-core

github.com/gentleman-programming开发661227 次安装31 周前更新
代码仓库

关于 angular-core

提供 Angular 开发规范的 Skill,涵盖独立组件、Signals、函数式输入输出和无 Zone 变更检测,并说明何时使用 RxJS。

主要功能

  1. 01独立组件规范
  2. 02函数式输入与输出
  3. 03使用 Signals 管理状态和副作用
  4. 04避免使用生命周期钩子
  5. 05使用 inject() 注入依赖
  6. 06无 Zone 变更检测

适用场景

  1. 01构建独立 Angular 组件
  2. 02使用 Signals 管理组件状态
  3. 03用 effect 替代生命周期钩子
  4. 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