angular-best-practices

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Angular 21 Best Practices

Angular 21 最佳实践

TypeScript

TypeScript

  • Use strict type checking
  • Prefer type inference when type is obvious
  • Avoid
    any
    ; use
    unknown
    when type is uncertain
  • 使用严格类型检查
  • 当类型明确时优先使用类型推断
  • 避免使用
    any
    ;当类型不确定时使用
    unknown

Components

组件

  • Always use standalone components (do NOT set
    standalone: true
    — it's the default in v20+)
  • Set
    changeDetection: ChangeDetectionStrategy.OnPush
  • Use
    input()
    and
    output()
    functions instead of decorators
  • Use
    computed()
    for derived state
  • Keep components small and single-responsibility
  • Prefer inline templates for small components
  • Use Reactive forms over Template-driven
  • Use
    class
    bindings instead of
    ngClass
  • Use
    style
    bindings instead of
    ngStyle
  • For external templates/styles, use paths relative to the component TS file
  • Do NOT use
    @HostBinding
    /
    @HostListener
    — use the
    host
    object in the decorator instead
  • 始终使用独立组件(无需设置
    standalone: true
    ——在v20+版本中这是默认值)
  • 设置
    changeDetection: ChangeDetectionStrategy.OnPush
  • 使用
    input()
    output()
    函数而非装饰器
  • 使用
    computed()
    处理派生状态
  • 保持组件小巧且单一职责
  • 小型组件优先使用内联模板
  • 优先使用响应式表单而非模板驱动表单
  • 使用
    class
    绑定替代
    ngClass
  • 使用
    style
    绑定替代
    ngStyle
  • 对于外部模板/样式,使用相对于组件TS文件的路径
  • 不要使用
    @HostBinding
    /
    @HostListener
    ——改用装饰器中的
    host
    对象

State Management with Signals

基于Signals的状态管理

  • Use signals for local component state
  • Use
    computed()
    for derived state
  • Keep state transformations pure and predictable
  • Do NOT use
    mutate
    on signals — use
    update
    or
    set
    instead
For complex derived state patterns, see references/signal-patterns.md.
  • 使用signals管理组件本地状态
  • 使用
    computed()
    处理派生状态
  • 保持状态转换纯净且可预测
  • 不要对signals使用
    mutate
    ——改用
    update
    set
如需了解复杂的派生状态模式,请查看references/signal-patterns.md

Resources (Async Data)

资源(异步数据)

Use
resource()
for async data fetching with signals:
typescript
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);
Key
resource
patterns:
  • params
    returns
    undefined
    → loader doesn't run, status becomes
    'idle'
  • Use
    abortSignal
    to cancel in-flight requests
  • Check
    hasValue()
    before accessing
    value()
    to handle loading/error states
  • Status values:
    'idle'
    ,
    'loading'
    ,
    'reloading'
    ,
    'resolved'
    ,
    'error'
    ,
    'local'
结合signals使用
resource()
进行异步数据获取:
typescript
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
    → 加载器不会运行,状态变为
    'idle'
  • 使用
    abortSignal
    取消进行中的请求
  • 在访问
    value()
    前检查
    hasValue()
    以处理加载/错误状态
  • 状态值包括:
    'idle'
    ,
    'loading'
    ,
    'reloading'
    ,
    'resolved'
    ,
    'error'
    ,
    'local'

Templates

模板

  • Use native control flow:
    @if
    ,
    @for
    ,
    @switch
    (NOT
    *ngIf
    ,
    *ngFor
    ,
    *ngSwitch
    )
  • Use async pipe for observables
  • Keep templates simple — no complex logic
  • Do NOT use arrow functions in templates (not supported)
  • Do NOT assume globals like
    new Date()
    are available
  • 使用原生控制流:
    @if
    ,
    @for
    ,
    @switch
    (不要使用
    *ngIf
    ,
    *ngFor
    ,
    *ngSwitch
  • 对可观察对象使用async管道
  • 保持模板简洁——避免复杂逻辑
  • 不要在模板中使用箭头函数(不支持)
  • 不要假设
    new Date()
    等全局对象可用

Services

服务

  • Single responsibility per service
  • Use
    providedIn: 'root'
    for singletons
  • Use
    inject()
    function instead of constructor injection
typescript
@Injectable({ providedIn: 'root' })
export class UserService {
  private readonly http = inject(HttpClient);
}
  • 每个服务单一职责
  • 单例服务使用
    providedIn: 'root'
  • 使用
    inject()
    函数而非构造函数注入
typescript
@Injectable({ providedIn: 'root' })
export class UserService {
  private readonly http = inject(HttpClient);
}

Routing

路由

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

File Naming

文件命名

  • Routable view components:
    file-name.page.ts
    ,
    file-name.page.html
    ,
    file-name.page.css
  • Regular components:
    file-name.component.ts
  • Services:
    file-name.service.ts
  • 可路由视图组件:
    file-name.page.ts
    ,
    file-name.page.html
    ,
    file-name.page.css
  • 常规组件:
    file-name.component.ts
  • 服务:
    file-name.service.ts

Icons (ng-icon)

图标(ng-icon)

typescript
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 {}
typescript
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 {}

Images

图片

  • Use
    NgOptimizedImage
    for all static images
  • NgOptimizedImage
    does NOT work for inline base64 images
  • 所有静态图片使用
    NgOptimizedImage
  • NgOptimizedImage
    不适用于内联base64图片

Styling

样式

  • Use Tailwind 4.1 for CSS (see tailwind skill if available)
  • Angular CDK is available when needed
  • 使用Tailwind 4.1编写CSS(若有相关技能可查看Tailwind技能文档)
  • 必要时可使用Angular CDK

Accessibility

可访问性

  • MUST pass all AXE checks
  • MUST meet WCAG AA minimums: focus management, color contrast, ARIA attributes
  • 必须通过所有AXE检查
  • 必须满足WCAG AA最低标准:焦点管理、颜色对比度、ARIA属性