首页
/ Angular 仓库 Public API 契约全解析:受支持 npm 包、兼容边界与 Golden 文件守护机制

Angular 仓库 Public API 契约全解析:受支持 npm 包、兼容边界与 Golden 文件守护机制

2026-09-07 12:51:00作者:申梦珏Efrain

本指南以 angular/angular 仓库的公共 API 面(Public API Surface)约定为主体,系统梳理哪些 npm 包与导出符号被纳入 SemVer 版本语义与弃用(deprecation)策略、哪些实现细节被明确排除在兼容承诺之外,并深入讲解仓库如何通过 Golden 文件与 Public API Guard 在 CI 中强制约束任何破坏性变更。阅读本文后,你将能准确判断「什么可以安全 import、什么绝不能依赖」,并掌握向该仓库贡献 API 变更时正确接受 Golden 文件的标准流程。

为什么需要一份「公共 API 面」清单

angular/angular 是一个长期演进、拥有数亿应用依赖的框架仓库。在持续的 minor / patch 发布中,如果某个看似无害的内部符号被应用直接 import,一旦重构就可能造成无法预料的破坏。因此,该仓库通过一份明确清单回答「哪些内容受到 SemVer 承诺保护」,凡是未列入承诺范围的实现细节,都允许在任何时刻被调整或删除,而无需等待 major 版本。

这份清单即本指南所依据的仓库文档 contributing-docs/public-api-surface.md,它与仓库实际构建配置、Golden 测试目标一一对应,可以直接在源码中验证。

受 SemVer 承诺保护的 npm 包

Angular 的语义化版本(SemVer)、发布节奏与弃用策略,仅适用于下列 npm 包:

包名 仓库目录 用途
@angular/animations packages/animations 动画 DSL 与浏览器实现
@angular/common packages/common 通用指令、管道与 HTTP 客户端
@angular/core packages/core 框架核心:DI、变更检测、信号、渲染
@angular/elements packages/elements 将 Angular 组件封装为 Web Components
@angular/forms packages/forms 模板驱动与响应式表单
@angular/platform-browser-dynamic packages/platform-browser-dynamic 基于 JIT 编译器的浏览器引导
@angular/platform-browser packages/platform-browser 浏览器平台实现
@angular/platform-server packages/platform-server 服务端渲染(SSR)
@angular/router packages/router 路由与导航
@angular/service-worker packages/service-worker PWA 与离线缓存
@angular/upgrade packages/upgrade AngularJS 迁移桥接

以上每个包在仓库内都对应一个独立的 Bazel 包与 ng_package 产物,其中多数同时部署了对应的 API Golden 测试,这一点在下文会结合具体 BUILD 文件印证。

被明确排除的 @angular/compiler 与受限的 @angular/compiler-cli

  • @angular/compiler显式排除在承诺列表之外,整体视为私有 / 内部 API,可能随时变化。仅极少数特殊场景(如 linter 或 IDE 集成)需要直接访问编译器 API;若确实在做此类集成,官方要求先联系 Angular 团队再使用,避免把你的工具建立在随时可能变更的实现上。
  • @angular/compiler-cli 本身虽不在表格中,但它的命令行用法(command line usage)受到覆盖,而其 API 的直接调用方式不在覆盖范围内。也就是说,通过 Angular CLI / ngc 命令行执行编译是被支持的,绕过命令行直接编程调用其内部接口则不受兼容性保护。

从源码结构看,packages/compiler-cli 同时存在 index.ts 与庞大的 src/test/ 目录,属于典型的工具链内部实现;goldens/public-api/compiler-cli 目录中确实保留有 golden 产物,用于守护 CLI 层对外暴露的类型。

稳定 API 的判定边界:入口点与全局命名空间

在上述受支持包内部,Angular 保持稳定的内容分为两类:

  1. 主入口点导出的符号,例如 @angular/core;以及 testing 入口点导出的符号,例如 @angular/core/testing。该承诺同时适用于运行时 / JavaScript 值与 TypeScript 类型。
  2. 通过全局命名空间 ng 导出的符号(如 ng.core),这类全局符号主要服务于非打包的脚本加载场景。

值得注意的是,goldens/public-api/core/index.api.md 的 Golden 文件头部明确标注「Do not edit this file. It is a report generated by API Extractor」,而 goldens/public-api/core/global_utils.api.md 则守护全局工具函数,可见这两类稳定面在 CI 中都有独立的持久化产物与之对应。

边界规则背后的可执行载体

在仓库中,「入口点」概念并不是抽象口号,而是直接体现在 packages/core/BUILD.bazel 等文件的 api_golden_test_npm_package 目标上。例如 core 包的目标定义中声明了 golden_dir = "goldens/public-api/core"npm_package = "packages/core/npm_package",表示该测试直接以打包后的 npm 产物.d.ts 声明文件为输入来生成 API 报告并与 Golden 比对。

明确排除在 Public API 之外的内容

仓库文档逐条列举了「不要当作稳定 API 使用」的清单,任何依赖下列内容的代码都可能在 minor 版本中无声破裂:

  • //testing/bundles/* 及其他有文档记录的入口点之外的任何文件 / import 路径。换言之,直接 import@angular/core/src/... 这类内部深路径不受任何保护。
  • 可注入类(服务与指令)的构造函数。获取这类实例必须通过依赖注入(Dependency Injection),不要自行 new
  • 标记为 private、或以下划线(_)、单重拉丁 o(ɵ)、双重拉丁 o(ɵɵ)前缀的类成员与符号。前缀 ɵɵɵ 正是 Angular 压缩产物中用于标记私有内部符号的专用字符。Golden 生成过程中亦有对应体现,例如 packages/core/BUILD.bazel 中的 strip_export_pattern = "^ɵ(?!.*ApiGuard)",表示导出名以 ɵ 开头的符号通常会被从 API 报告中剥离(仅对 ApiGuard 相关私有导出做白名单豁免)。
  • 继承(extend)任何 Angular 类,除非 API 参考文档明确声明支持该继承——详见下文「Final 类」约定。
  • Angular 编译器生成代码的内容与 API 面(框架输出到应用产物中的那部分代码不属于公共契约)。
  • @angular/core/primitives 包及其全部后代入口点。这对应仓库内 packages/core/primitives 目录下按域拆分的多组实现(如 signals、di、dom-navigation 等),它们是内部原语,仅通过 core 聚合后再对外暴露稳定接口。

同行依赖:不在 API 面内,但在 SemVer 策略内

TypeScript、Zone.js、RxJS 等 peer dependencies(同行依赖) 不被视为 Angular API 面的一部分,但它们被纳入 SemVer 版本策略进行管理:

  • 如果升级某个 peer dependency 的必需版本不会对 Angular 应用造成破坏性变更,则允许放在 minor 发布中一并推进;
  • 如果升级会引入非平凡的破坏性变更,则必须推迟到 major 版本再执行。

这一条对框架使用者的实际意义在于:你应该把 Angular 版本与关键同行依赖的版本约束看作一套需要联动评估的组合,而不是各自独立的版本号。

Final 类约定:Angular 类的可扩展性边界

文档中单列一节强调:Angular 公共 API 中的所有类都被视为 final,除非 API 文档中显式声明,否则不应被继承。

原因很直接:这些类的 protected 成员与内部实现可能在任何非 major 版本中调整。一旦你的业务类继承了某个 Angular 类,而 Angular 在下个 minor 版本中重排了内部字段布局,你的派生类就会在不被通知的情况下编译失败或运行时行为异常。

判断某个类是否允许继承的唯一依据是 API 参考文档中的显式说明,而不是「看起来可以 extends」。这也是为什么 API Extractor 生成的 Golden 报告(如 goldens/public-api/core/index.api.md 中每个 @public 声明)会成为此类约定的仲裁证据。

Golden 文件:用 diff 锁定每一个公开变更

Golden 文件的含义

Angular 使用 Golden 文件 追踪受支持包的公共 API 现状,并由一个名为 Public API Guard 的工具来维护。每份 Golden 都是对包内 .d.ts 声明文件的文本化快照(典型如 core.d.ts),经工具规范化后落盘到 goldens/public-api 目录。

仓库的 Golden 目录结构与受支持包一一对应,读者可自行核对:

  • core、common、router、forms、animations、elements、platform-browser、platform-browser-dynamic、platform-server、service-worker、upgrade、localize 各自拥有目录;
  • 顶层 goldens/public-api/manage.js 提供跨目录管理的辅助逻辑;
  • 部分包目录内含 errors.api.md(错误类型面)与 testing/http/upgrade/ 等子入口点的独立报告。

CI 中的拦截机制

一旦你在某个受支持公共包中改动任何一部分公共 API,Pull Request 就会在 CI 中失败一项测试,失败信息会提示你接受(accept)新的 Golden 文件。从 packages/core/BUILD.bazel 可以看到,每个核心包都声明了多组守护目标:

  • core_api:使用 api_golden_test_npm_package 守护主 npm 包的完整导出面;
  • ng_global_utils_api:守护全局 ng 工具函数的单独报告;
  • core_errors:守护错误类型的公共面。

这些目标的公共宏定义源自 tools/defaults.bzl,其中 api_golden_testapi_golden_test_npm_package 均从 @devinfra//bazel/api-golden 规则集再导出,是仓库统一接入 Golden 测试的入口。

接受新 Golden 的命令

Public API Guard 为每个包提供了 Bazel 目标用于更新该包当前 API 状态。修改或新增任何公共 API 之后,必须在终端(建议使用较新版本的 bash)中通过 pnpm 执行相应的 Bazel 目标:

pnpm bazel run //packages/<modified_package>:<modified_package>_api.accept

例如修改的是 @angular/core 的公共导出,则应执行:

pnpm bazel run //packages/core:core_api.accept

该命令会把当前生成的 API 报告覆盖到对应的 Golden 文件,使后续 CI 中的 Golden 对比测试转绿。

一次真实的 CI 失败剖析

文档中给出了一次由「向 core.d.ts 中某个公共属性的合法类型集合新增类型」引发的 CI 失败样例。API Guard 的错误信息使用 git-diff 的组合 diff 格式输出,关键段落解读如下:

--- goldens/public-api/core/core.d.ts	Golden file
+++ goldens/public-api/core/core.d.ts	Generated API
@@ -563,9 +563,9 @@
     ngModule: Type<T>;
     providers?: Provider[];
 }
 
-export declare type NgIterable<T> = Array<T> | Iterable<T>;
+export declare type NgIterable<T> = Iterable<T>;
 
 export declare interface NgModule {
  • - 行表示 Golden 中记录的既有状态
  • + 行表示 本次改动后实际生成的 API 状态
  • 中间的 @@ 头部指明变更发生的声明位置。

这类输出把「契约与现状的偏差」精确到符号级,既可用于 code review,也可作为回归测试的机器可读输入。修复方式即执行上文给出的 accept 目标,让 Golden 反映有意为之的新 API,随后将更新后的 Golden 文件随 PR 一并提交。

对框架使用者的实践建议

基于以上契约,给直接使用 Angular 的开发者几条可操作的原则:

  1. 只从受文档保护的入口点导入@angular/* 根入口、/testing 后缀入口以及有文档记录的公共入口之外的深路径都视为私有;@angular/core/primitives 及其子路径尤其不要直接引用。
  2. 绝不手写 new 服务或指令,一律通过构造器参数注入或 inject() 获取实例;不要在类外部调用被注入类的构造器。
  3. 远离 ɵɵɵ 前缀符号。它们在产物中出现时是框架内部的正常压缩产物,应用代码不应感知或依赖它们。
  4. 除非 API 参考文档白纸黑字允许,否则不要 extends 任何 Angular 类;尽量采用组合、接口与 DI 机制实现扩展需求。
  5. 把版本升级当作组合评估:Angular minor 版本可能顺带提升 TypeScript / Zone.js / RxJS 的下限版本,升级 Angular 时应同步阅读其 peer dependency 约束。
  6. 若你是库作者或贡献者:修改公共 API 后必须运行 pnpm bazel run //packages/<modified_package>:<modified_package>_api.accept 接受新 Golden,并让更新后的 goldens/public-api 文件随 PR 提交,CI 才会放行。

结语

「Supported public API surface」本质上是 Angular 对自己社区的一份兼容性宪法:它在 contributing-docs/public-api-surface.md 中写明承诺范围,又用 goldens/public-api 目录下的持久化快照与散落在各包 BUILD.bazel 中的 Guard 目标把它变成每次 CI 都会执行的硬约束。理解这份契约,既能帮助普通开发者写出经得起版本升级的应用代码,也能帮助贡献者以正确姿势向框架提交 API 演进。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
898
5.82 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
921
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.8 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
596
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
519
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
391