mcpField
A
angular-best-practices

angular-best-practices

github.com/boise-state-development开发22758 次安装1 天前更新
代码仓库

关于 angular-best-practices

angular-best-practices 是面向 Angular 21 开发者的技能,提供 TypeScript、组件、Signals、模板、路由、样式与无障碍方面的实践指导。

主要功能

  1. 01严格 TypeScript 与组件规范
  2. 02基于 Signals 的状态和异步资源
  3. 03原生模板控制流
  4. 04功能路由延迟加载
  5. 05图像、样式与无障碍指导

适用场景

  1. 01为新 Angular 项目制定规范
  2. 02检查组件与状态模式
  3. 03实现异步数据和延迟路由

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/boise-state-development/agentcore-public-stack --skill angular-best-practices

安装后在 Claude Code 中输入/angular-best-practices确认已加载。

附带文件angular-best-practices.skillsignal-patterns.md

详细介绍

Angular 21 最佳实践

TypeScript

  • 使用严格类型检查
  • 类型显而易见时,优先使用类型推断
  • 避免使用 any;类型不确定时使用 unknown

组件

  • 始终使用独立组件(不要设置 standalone: true ——它在 v20+ 中是默认值)
  • 设置 changeDetection: ChangeDetectionStrategy.OnPush
  • 使用 input() 和 output() 函数,而不是装饰器
  • 使用 computed() 表示派生状态
  • 保持组件小巧且职责单一
  • 小型组件优先使用内联模板
  • 优先使用响应式表单,而不是模板驱动表单
  • 使用 class 绑定,而不是 ngClass
  • 使用 style 绑定,而不是 ngStyle
  • 对于外部模板和样式,使用相对于组件 TS 文件的路径
  • 不要使用 @HostBinding/@HostListener ——改用装饰器中的 host 对象

使用 Signals 管理状态

  • 使用 signals 管理组件本地状态
  • 使用 computed() 表示派生状态
  • 让状态转换保持纯粹且可预测
  • 不要对 signals 使用 mutate ——改用 update 或 set

复杂的派生状态模式请参阅 references/signal-patterns.md。

资源(异步数据)

使用 resource() 配合 signals 获取异步数据:

const userResource = resource({
  params: () => ({ id: userId() }),
  loader: ({ params, abortSignal }) => fetch(`/api/users/${params.id}`, { signal: abortSignal }),
});

const userName = computed(() => userResource.hasValue() ? userResource.value().name : undefined);

resource 的关键模式:

  • params 返回 undefined → loader 不会运行,状态变为 'idle'
  • 使用 abortSignal 取消正在进行的请求
  • 访问 value() 前检查 hasValue(),以处理加载或错误状态
  • 状态值:'idle'、'loading'、'reloading'、'resolved'、'error'、'local'

模板

  • 使用原生控制流:@if、@for、@switch(不要使用 *ngIf、*ngFor、*ngSwitch)
  • 对 observables 使用 async pipe
  • 保持模板简单 ——不要包含复杂逻辑
  • 不要在模板中使用箭头函数(不受支持)
  • 不要假定 new Date() 等全局对象可用

服务

  • 每个服务只承担一项职责
  • 单例使用 providedIn: 'root'
  • 使用 inject() 函数,而不是构造函数注入
@Injectable({ providedIn: 'root' })
export class UserService {
  private readonly http = inject(HttpClient);
}

路由

  • 为功能路由实现惰性加载:
export const routes: Routes = [
  {
    path: 'admin',
    loadComponent: () => import('./admin/admin.page').then(m => m.AdminPage),
  },
];

文件命名

  • 可路由视图组件:file-name.page.ts、file-name.page.html、file-name.page.css
  • 普通组件:file-name.component.ts
  • 服务:file-name.service.ts

图标(ng-icon)

import { NgIcon, provideIcons } from '@ng-icons/core';
import { heroSparkles, heroTrash } from '@ng-icons/heroicons/outline';

@Component({
  selector: 'app-example',
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [NgIcon],
  providers: [provideIcons({ heroSparkles, heroTrash })],
  template: `<ng-icon name="heroSparkles" />`,
})
export class ExampleComponent {}

图片

  • 所有静态图片都使用 NgOptimizedImage
  • NgOptimizedImage 不适用于内联 base64 图片

样式

  • 使用 Tailwind 4.1 编写 CSS(如有 tailwind skill,请参阅)
  • 如有需要,可使用 Angular CDK

无障碍访问

  • 必须通过所有 AXE 检查
  • 必须至少满足 WCAG AA 标准:焦点管理、颜色对比度、ARIA 属性

评论

支持 Markdown