
ECC Angular 编码规范实战指南基于 Signals、OnPush 与 standalone 的现代开发标准【免费下载链接】ECCThe agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond.项目地址: https://gitcode.com/GitHub_Trending/ev/ECC本指南以 ECCAgent Harness Performance Optimization System规则体系中的 Angular 编码规范英文原版见 rules/angular/coding-style.md为核心骨架并结合仓库内配套的 Angular Patterns、Angular Testing、Angular Security、Angular Hooks 以及 公共编码规范 展开纵深讲解。读完本文你将掌握一套可直接落地的 Angular 编码标准版本感知与 CLI 校验流程、standalone OnPush 组件范式、inject()依赖注入、完整的 Signals 状态管理signal/computed/linkedSignal/resource/effect、v17 块语法模板、三种表单策略的选型以及变更检测的正确用法并了解如何在 Claude Code / Codex 等 Agent 环境中通过规则文件与 Hook 自动化执行这些规范。一、规则体系背景common 层与语言专属层的分层设计在深入编码规范本身之前有必要先理解这套规则在 ECC 仓库中的组织方式。规则被组织为common通用层与语言专属层两层Angular 专属规则目录结构如下rules/ ├── common/ # 语言无关的通用原则始终安装 │ ├── coding-style.md │ ├── git-workflow.md │ ├── testing.md │ ├── performance.md │ ├── patterns.md │ ├── hooks.md │ ├── agents.md │ └── security.md └── angular/ # Angular 专属规则 ├── coding-style.md ├── patterns.md ├── testing.md ├── security.md └── hooks.md按 rules/README.md 的说明Angular 专属文件通过文件头部的一行引用声明与公共规则的关系This file extends common/coding-style.md with Angular specific content.即“本文件以 Angular 专属内容扩展 common/coding-style.md”。这形成了一种类似 CSS 特异性或.gitignore优先级的分层配置模式当语言专属规则与 common 规则冲突时语言专属规则优先。例如common/coding-style.md默认推崇“不可变性”Angular 专属规则则在此基础上补充了信号、OnPush 等框架特有实践。安装方式推荐通过仓库根目录的安装脚本一键安装 common 语言专属规则集./install.sh angular # 安装 common Angular 规则 ./install.sh typescript python # 可同时安装多个语言规则集手动安装时务必整目录复制不要用/*拍平因为 common 与语言专属目录存在同名文件如coding-style.md拍平会导致语言专属文件覆盖公共规则并破坏语言专属文件中对../common/的相对引用。用户级安装建议放入 ECC 命名空间mkdir -p ~/.claude/rules/ecc cp -r rules/common ~/.claude/rules/ecc/ cp -r rules/angular ~/.claude/rules/ecc/二、版本感知写代码前的第一道工序编码规范开篇强调写任何代码之前必须先确认项目的 Angular 版本——不同版本之间的功能差异巨大。这一条在 Signal 与块语法时代尤为关键standalone组件是 v17 的默认行为块模板语法if、for等需要 v17linkedSignal、resource()属于较新的版本特性Signal Forms则是 v21 新项目的推荐方案。确认版本的方式有两种ng version # 查看当前项目 Angular CLI / 框架版本或者直接查看package.json中的angular/core依赖版本。创建新项目时除非用户明确指定否则不要固定版本——让脚手架使用当前最新稳定版。版本确认之外还有一条硬性流程要求生成或修改 Angular 代码后完成之前必须运行ng build检测错误。模板错误与类型错误往往要等编译期才能暴露把ng build作为收尾动作是防止带错提交的最低成本手段。对应地rules/angular/hooks.md 建议在 Agent 的~/.claude/settings.json中配置 PostToolUse Hook让这一校验自动化Prettier编辑后自动格式化.ts与.html文件ESLint / ng lint编辑 Angular 源文件后运行ng lint捕获装饰器误用、模板错误与风格违规TypeScript 检查编辑.ts文件后运行tsc --noEmit构建检查生成或显著修改 Angular 代码后运行ng build尽早发现模板与类型错误。三、文件命名一个文件一个成果物遵循 Angular CLI 约定每个文件只放一个成果物artifact并采用类型后缀命名user-profile.component.tsuser-profile.component.htmluser-profile.component.spec.ts组件三件套user.service.ts服务、auth.guard.ts守卫、date-format.pipe.ts管道按功能组织目录features/users/、features/auth/用 CLI 生成ng generate component features/users/user-card这与 rules/common/coding-style.md 的“多小文件优于少大文件”原则一脉相承高内聚、低耦合源文件通常 200–400 行800 行是软性维护上限测试、生成、vendored 文件在角色合理时可突破按功能/领域而非类型组织代码。四、组件规范standalone OnPush 双默认所有新组件优先使用 standalone 组件v17 默认且一律采用OnPush变更检测。规范给出的标准写法Component({ selector: app-user-card, standalone: true, imports: [RouterModule], templateUrl: ./user-card.component.html, changeDetection: ChangeDetectionStrategy.OnPush, }) export class UserCardComponent { user input.requiredUser(); select outputstring(); }注意其中两个信号化 APIinput.requiredUser()声明必选输入outputstring()声明输出事件——它们取代了传统Input()/Output()装饰器是组件通信的信号化新范式。结合 rules/angular/patterns.md 的Smart / Dumb 组件拆分实践组件职责被进一步明确Smart容器组件负责数据获取与状态管理可注入服务Dumb展示组件只接收输入、发射输出不注入任何服务。// Smart — 拥有数据 Component({ standalone: true, changeDetection: ChangeDetectionStrategy.OnPush }) export class UserPageComponent { private userService inject(UserService); user toSignal(this.userService.getUser(this.userId)); }!-- Dumb — 纯展示 -- app-user-card [user]user() (select)onSelect($event) /五、依赖注入inject()优先于构造函数注入在构造函数注入与inject()函数之间一律使用inject()。构造函数保持为空或彻底删除。// 正确 Injectable({ providedIn: root }) export class UserService { private http inject(HttpClient); private router inject(Router); } // 错误构造函数注入冗长且不利于 tree-shaking constructor(private http: HttpClient, private router: Router) {}规范的论点很明确构造函数注入会迫使private参数属性产生冗余样板代码且依赖被绑定在构造函数签名上不利于摇树优化tree-shaking。inject()函数式写法让依赖声明成为字段初始化的一部分配合providedIn: root让服务默认单例且按需加载。对于非类依赖配置值、字符串、接口对象等使用InjectionTokenconst API_URL new InjectionTokenstring(API_URL); // 提供 { provide: API_URL, useValue: https://api.example.com } // 使用 private apiUrl inject(API_URL);patterns.md 还补充了InjectionToken的工厂提供方式{ provide: CONFIG, useFactory: () loadConfig(), deps: [] }以及providers与viewProviders的区别——providers对组件及其所有内容子节点可见viewProviders仅对组件自身视图可见投影内容不可见。六、Signals现代 Angular 状态管理的核心6.1 基础原语signal与computedcount signal(0); doubled computed(() this.count() * 2); increment() { this.count.update(n n 1); }要点computed是派生状态的唯一正确来源。规范明确禁止把派生值存入独立 signal——那会造成状态重复与同步维护负担详见 patterns.md 的 Signal State Patterns。6.2linkedSignal可写的派生状态当某个 signal 需要在源变化时重置或自适应、但同时又允许独立写入时使用linkedSignalselectedOption linkedSignal(() this.options()[0]); // options 变化时重置为第一项但用户仍然可以手动覆盖选择这是computed只读派生与普通signal完全独立可写之间的中间形态非常适合“跟随数据源、但保留用户覆盖”的场景例如选中项跟随下拉选项列表变化。6.3resource异步数据进入信号世界resource()用于在无需手动订阅的情况下响应式地获取异步数据userResource resource({ request: () ({ id: this.userId() }), loader: ({ request }) fetch(/api/users/${request.id}).then(r r.json()), }); // 访问userResource.value()、userResource.isLoading()、userResource.error()request函数返回的请求参数是响应式的——当this.userId()变化时loader会自动重新执行。patterns.md 给出了与 RxJS 桥接的实战写法在loader中用firstValueFrom(inject(UserService).getUser(request.id))把Observable转为 Promise同时可访问的完整状态还包括userResource.reload()手动刷新。对于简单数据加载resource()优先于手工 RxJS 管道。6.4effect只做副作用绝不用于同步信号effect()仅用于必须响应信号变化的副作用日志记录、第三方 DOM 操作。绝不能用 effect 来同步信号——那正是computed或linkedSignal的职责// 正确副作用 effect(() console.log(User changed:, this.user())); // 错误应该用 computed effect(() { this.fullName.set(${this.first()} ${this.last()}); });渲染完成后的 DOM 操作则应使用afterRenderEffect取代旧式的afterNextRender/afterRender组合场景。6.5 订阅清理takeUntilDestroyed对于仍在使用 RxJS 手动订阅的遗留场景patterns.md 规定新代码一律使用takeUntilDestroyed()禁止再手写ngOnDestroySubjecttakeUntilexport class UserComponent { private destroyRef inject(DestroyRef); ngOnInit() { this.userService.updates$ .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe(update this.handleUpdate(update)); } }七、模板规范v17 块语法与 track 强制模板一律使用 v17 的块语法。for中必须指定track以帮助 Angular 高效复用 DOM 节点for (item of items(); track item.id) { app-item [item]item / } if (isLoading()) { app-spinner / } else if (error()) { app-error [message]error() / } else { app-content [data]data() / }同时模板中的逻辑只允许简单条件表达式复杂逻辑一律下沉到组件方法或管道pipe中——这既保证模板可读性也让逻辑可以被单元测试覆盖。八、表单策略与项目既有方案对齐表单没有“唯一正确答案”规范要求选择与项目既有方案匹配的策略策略适用版本/场景说明Signal Formsv21 新项目推荐基于信号的表单状态Reactive Forms大多数应用的标准方案FormBuilderFormGroupFormControl适合带动态校验的复杂表单Template-Driven Forms仅适合简单表单ngModel标准 Reactive Forms 写法export class LoginComponent { private fb inject(FormBuilder); form this.fb.group({ email: [, [Validators.required, Validators.email]], password: [, [Validators.required, Validators.minLength(8)]], }); submit() { if (this.form.valid) { // 使用 this.form.value } } }注意这里同样贯彻了inject()风格FormBuilder通过inject()获取而非构造函数注入。九、组件样式Emulated 封装与主题化默认使用ViewEncapsulation.Emulated组件级样式除非在刻意构建“样式有意泄漏”的设计系统否则避免ViewEncapsulation.None。三条具体准则样式限定在组件作用域内组件样式表中不要使用全局类名宿主元素样式使用:host选择器可主题化值优先使用CSS 自定义属性var(--xxx)。这与 patterns.md 的可访问性实践互相呼应用 CDK 构建无头可访问组件时通过样式化 ARIA 属性而非手工管理属性状态例如[aria-selectedtrue] { background: var(--color-selected); }十、变更检测OnPush 默认与“不要手动干预”所有新组件默认ChangeDetectionStrategy.OnPush信号与async管道会自动触发检测——因此应避免手动调用markForCheck()与detectChanges()使用 OnPush 时绝不要原地修改Input()对象原地修改后 Angular 无法感知引用未变化检测会被跳过。这最后一条与 common 层“不可变性”原则完全同构rules/common/coding-style.md将不可变性列为CRITICAL级原则——永远创建新对象、绝不原地修改// 伪代码 错误: modify(original, field, value) → 原地修改 original 正确: update(original, field, value) → 返回带修改的新副本理由不可变数据可防止隐藏副作用、简化调试、并支持安全并发。在 OnPush 场景下不可变性不只是代码质量偏好更是保证变更检测正确触发的必要条件。十一、从风格到工程闭环配套规则与质量清单coding-style.md只是 Angular 规则集五件套之一完整的工程质量闭环还需要与同目录下其他规则配合它们都以**/*.component.ts、**/*.service.ts、**/*.guard.ts等 Angular 源文件为适用路径patterns.md除上文已涉及的 Smart/Dumb 拆分、resource异步数据、Signal 状态模式、订阅清理外还覆盖路由canMatch优于canActivate防止未授权加载 chunk、loadChildren懒加载、resolve预取数据、withViewTransitions()视图过渡、函数式守卫与解析器、HTTP 函数式拦截器provideHttpClient(withInterceptors([authInterceptor, errorInterceptor]))、RxJS 操作符选型switchMap搜索导航、mergeMap独立并行、exhaustMap表单提交以及渲染策略CSR/SSRHydration/SSG。testing.mdTestBed 配置、fixture.componentRef.setInput()设置信号输入、CDK Component Harnesses、RouterTestingHarness、fakeAsynctick、provideHttpClientTesting服务与管道覆盖率目标 ≥80%。security.md禁止绕过消毒器bypassSecurityTrust*需文档化理由、统一走HttpClient拦截器、密钥经environment注入、敏感路由必须canMatch守卫。hooks.mdPostToolUse/Stop Hook 自动化ng lint、tsc --noEmit、ng build见第二节。最后无论语言如何完成工作前都应过一遍 common/coding-style.md 的质量清单代码可读且命名良好函数足够小50 行文件聚焦800 行无深层嵌套≤4 层恰当的异常处理无硬编码值使用常量或配置无原地修改使用不可变模式结语ECC 的 Angular 编码规范是一份“版本敏感、信号优先、OnPush 默认、inject 贯通”的现代实践集合版本感知与ng build收尾保证代码跑得起来standalone OnPush Signals 保证架构跟得上 Angular v17–v21 的演进inject()与 InjectionToken 统一了依赖获取方式而 common 层的不可变性与质量清单则为所有语言划定了底线。配合./install.sh angular将规则集装入 Agent 环境、再用 hooks.md 的 Hook 配置把ng lint、tsc --noEmit、ng build自动化这套规范就不仅是文档而是一套可执行的工程化约束。深度实践细节可进一步查阅 angular-developer 技能目录获取信号、表单、路由、SSR 与可访问性的完整指南。【免费下载链接】ECCThe agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond.项目地址: https://gitcode.com/GitHub_Trending/ev/ECC创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考