首页
/ Angular upgrade/static 公共 API 全解:@angular/upgrade_static 的 API 报告与混合升级应用实战

Angular upgrade/static 公共 API 全解:@angular/upgrade_static 的 API 报告与混合升级应用实战

2026-09-07 16:35:29作者:霍妲思

本文以 Angular 仓库中的公共 API 报告文件 index.api.md 为核心,逐条解读 @angular/upgrade_static 包的完整导出清单,并结合 upgrade/static 源码upgrade 公共实现 深入剖析 UpgradeModuleUpgradeComponentdowngradeModuledowngradeComponentdowngradeInjectable 等 API 的真实行为与底层机制。读完后,你将能够独立搭建一个 AngularJS 与 Angular 共存的混合应用,理解两套框架之间变更检测的双向同步原理,并知道如何利用 API 报告文件校验包的公共 API 表面是否发生破坏性变更。

API 报告文件是什么:@angular/upgrade_static 的黄金标准

index.api.md 位于仓库的 goldens/public-api/upgrade/static/ 目录下,文件开头声明:

Do not edit this file. It is a report generated by API Extractor.

这是一份由 API Extractor 工具自动生成的 公共 API 报告(API Report)黄金文件。仓库在 CI 中会重新生成报告并与该文件比对,任何未被显式批准的公共 API 增删改(新增导出、参数变化、废弃标记等)都会导致比对失败,从而保证 @angular/upgrade_static 包的对外 API 表面(public API surface)稳定可控。这也是 goldens/README.md 所描述的 goldens 目录的职责:为发布产物提供不可手改的基线。

该报告完整列出了 @angular/upgrade_static 的全部 @public 导出:

导出项 类型 作用
downgradeComponent(info) 函数 将 Angular 组件降级为 AngularJS 指令,供 AngularJS 模板使用
downgradeInjectable(token, downgradedModule?) 函数 将 Angular 服务(可注入对象)降级为 AngularJS 服务
downgradeModule(moduleOrBootstrapFn) 函数(两个重载,其中一个已废弃) 创建一个 AngularJS 模块,可"按需/惰性"引导一个 Angular 模块(Lite 模式)
getAngularJSGlobal() / setAngularJSGlobal(ng) 函数 获取/设置全局 window.angular 对象引用
UpgradeComponent 类(可继承) 用作 Angular 指令基类,将 AngularJS 组件"升级"为可在 Angular 模板中使用的组件
UpgradeModule 类(NgModule) 提供 AngularJS 核心服务的 Angular 模块,并持有 bootstrap() 引导混合应用
VERSION 常量 包的 Version 元数据

报告中的每一条签名(例如 downgradeModuleType<T> | ((extraProviders: StaticProvider[]) => Promise<NgModuleRef<T>>) 重载、UpgradeComponent implements OnInit, OnChanges, DoCheck, OnDestroy 的生命周期接口)都可以直接对照仓库源码验证:

  • 公共导出入口:public_api.ts,其中 downgradeComponentdowngradeInjectableVERSIONgetAngularJSGlobal/setAngularJSGlobal 转发自 upgrade 包的 common 层,而 downgradeModuleUpgradeComponentUpgradeModule 则实现于 static/src/
  • 入口文件 index.ts 仅 re-export public_api,注释明确说明该文件在 AOT 构建时会被 ngc 用生产版 index.ts 替换以重写私有符号名。

心智模型:两个框架如何共存于一个页面

UpgradeModule 的 JSDoc 给出了理解混合应用最重要的"心智模型"(见 upgrade_module.ts):

  1. 应用内运行着两个相互独立的框架,每个框架把对方当作黑盒;
  2. 页面上的每个 DOM 元素恰好由一个框架"拥有"——谁实例化了它,谁就负责更新它,另一个框架对其视而不见;
  3. AngularJS 指令永远在 AngularJS 框架代码中执行,Angular 组件永远在 Angular 框架代码中执行,与实例化位置无关;
  4. "升级"(upgrade)指用 Angular 指令包裹 AngularJS 组件,即 UpgradeComponent;"降级"(downgrade)指用 AngularJS 指令包裹 Angular 组件,即 downgradeComponent
  5. 升级/降级组件实例化时,宿主元素归"做实例化的框架"所有,而组件视图归另一个框架所有。因此绑定语义遵循实例化框架的规则,但模板绑定语法始终使用 Angular 风格(如方括号属性绑定);
  6. Angular 先引导,AngularJS 后引导,且 AngularJS 始终拥有应用根组件;
  7. 应用运行在 Angular 的 zone 中,因此不再需要手动调用 $apply()

upgrade/staticstatic 一词来源于 AOT(Ahead-of-Time)支持:所有辅助 API 都是设计为可以在编译期静态分析的,这正是它与旧版运行时 @angular/upgrade 的本质区别。

UpgradeModule:引导混合应用的核心

结构与提供的服务

UpgradeModule 本身是一个 NgModule,装饰器为(见 upgrade_module.ts):

@NgModule({providers: [angular1Providers, ɵinternalProvideZoneChangeDetection({})]})
export class UpgradeModule {
  public $injector: any;        // 升级应用的 AngularJS $injector
  public injector: Injector;    // Angular 注入器(NgAdapterInjector 包装)
  public ngZone: NgZone;        // 引导区
  constructor(
    injector: Injector,        // 升级应用的根 Injector
    public ngZone: NgZone,
    private platformRef: PlatformRef,  // 用于把 AngularJS 应用生命周期绑到 PlatformRef
  ) { ... }
}

导入该模块即向根注入器注入四个 AngularJS 核心服务(实现见 angular1_providers.ts):

export const angular1Providers = [
  {provide: '$injector',  useFactory: injectorFactory,  deps: []},
  {provide: '$rootScope', useFactory: rootScopeFactory, deps: ['$injector']},
  {provide: '$compile',   useFactory: compileFactory,   deps: ['$injector']},
  {provide: '$parse',     useFactory: parseFactory,     deps: ['$injector']},
];

这里有一处精巧实现:AngularJS 的 $injector 在引导之前还不存在,因此源码用模块级变量 tempInjectorRef 暂存,引导完成后由 setTempInjectorRef() 注入,再经由 provider 的一次 get 触发读取并清空引用以防内存泄漏(angular1_providers.ts#L11-L27)。

bootstrap() 的完整流程

API 报告中 UpgradeModule.bootstrap(element, modules?, config?) 对应源码中的同名方法(upgrade_module.ts#L183-L364):

  • 参数element 为引导目标元素;modules(默认 [])是要引导的 AngularJS 模块名数组;config 是可选的 AngularJS bootstrap 配置,透传给 angular.bootstrap()
  • 返回值angular.bootstrap() 的返回值(一个带 $destroy 等方法的 scope)。

其内部步骤值得逐一看:

  1. 创建名为 ngUpgrade.init 的内部 AngularJS 模块,注册 UPGRADE_APP_TYPE_KEY 常量为 Static(这是后续区分 UpgradeModule 模式与 downgradeModule Lite 模式的依据),并把 Angular 的 Injector 作为 INJECTOR_KEY 值供 AngularJS 侧读取;
  2. .config 中装饰 $testability:将 Angular 侧 Testability.whenStable 与 AngularJS 侧串联,只有两边都稳定时才回调——这是混合应用测试可靠等待稳定状态的保障(相关测试见 testability_spec.ts);
  3. 装饰 $interval:让 setInterval 在 NgZone 之外调用、而回调在 zone 内执行,这样 $interval 不会阻塞 zone 稳定状态,保持 AngularJS 的原始行为;
  4. .run 钩子中:保存 $injector、把 Angular 注入器挂到 DOM 元素上(以便 AngularJS 侧 require)、注册 platformRef.onDestroy(() => destroyApp($injector))——即 Angular PlatformRef 销毁时一并销毁 AngularJS 应用(源码注释指明这主要服务于 HMR 等需要销毁应用的场景,对应上游 issue #39935);
  5. 双向变更检测桥接:在下一个宏任务中订阅 zone 的微任务空闲事件(ZoneJS 场景为 ngZone.onMicrotaskEmpty;从源码结构看,若检测到 NoopNgZone——即 zoneless——则退化为订阅 ApplicationRef.afterTick,源码注释坦诚这是因混合应用缺乏 zoneless 覆盖而做的防御性兼容),每次 zone 稳定后触发 $rootScope.$digest()(若已有 digest 进行中则改为 $evalAsync() 并发出警告);$rootScope.$destroy 时取消订阅。
const subscription =
  this.ngZone instanceof ɵNoopNgZone
    ? (this.applicationRef as any).afterTick.subscribe(() => synchronize())
    : this.ngZone.onMicrotaskEmpty.subscribe(() => synchronize());
$rootScope.$on('$destroy', () => {
  subscription.unsubscribe();
});
  1. 组装最终的 ngUpgrade 模块(依赖 ngUpgrade.init 与用户传入的 modules),在 ngZone.run() 内调用 angular.bootstrap(),并打补丁让 angular.resumeBootstrap 也运行在 zone 内,最终返回 angular.bootstrap 的返回值。

UpgradeComponent:把 AngularJS 组件带入 Angular 模板

API 报告显示 UpgradeComponent 实现了 OnInit, OnChanges, DoCheck, OnDestroy 四个生命周期接口,构造签名为 constructor(name: string, elementRef: ElementRef, injector: Injector)upgrade_component.ts)。

使用方式

假设有一个 AngularJS 组件 ng1Hero(带 @=& 绑定的 scope),要让它出现在 Angular 模板中,需要派生一个 Angular 指令:

@Directive({selector: 'ng1-hero'})
export class Ng1HeroCmp extends UpgradeComponent {
  hero: string;                       // 对应 '@'/'<' 绑定(input)
  @Output() nameChange = new EventEmitter(); // 对应 '=' 绑定
  save: () => void;                   // 对应 '&' 绑定(output)

  constructor(elementRef: ElementRef, injector: Injector) {
    super('ng1Hero', elementRef, injector); // 第一个参数是 AngularJS 侧的指令名
  }
}

AOT 编译器要求选择器、inputs、outputs 在编译期静态可见,因此必须写 @Directive() 装饰器并显式声明绑定,而不能在运行时动态生成。

生命周期如何映射到 AngularJS 钩子

upgrade_component.ts#L127-L234 的实现看,四个 Angular 钩子分别驱动了 AngularJS 侧的对应行为:

  • 构造时:通过内部 UpgradeHelper 获取 AngularJS 侧的 IDirective 定义、把宿主要素包成 $element,依据 directive.scope(或 bindToController 对象)调用 $parentScope.$new(isolate) 创建组件 scope,并对 =& 绑定预创建 EventEmitter 输出(= 绑定映射为 propChange 输出名,& 绑定映射为同名输出);
  • ngOnInit:准备模板与投影(prepareTransclusion/compileTemplate)、实例化 controller、处理 bindToController、回放暂存的 pendingChanges(首次 ngOnChanges 早于 controller 构建时会被缓存),依次调用 AngularJS 的 $onChanges$onInit$doCheck(通过父 scope $watch 持续触发)→ pre/post link → $postLink
  • ngOnChanges:把 SimpleChanges 中的当前值转发到绑定目标(scope 或 controller 实例),并调用 AngularJS 侧的 $onChanges
  • ngDoCheck:轮询所有 = 双向绑定属性,用 Object.is 比对新旧值,有变化则通过对应 EventEmitter 向外发值(这就是 = 绑定在 Angular 侧表现为输出事件的机制);
  • ngOnDestroy:注销 $doCheck watcher,销毁 scope 与 controller 实例。

一个重要的限制值得注意:scopebindToController 不能同时定义绑定,否则抛出 Binding definitions on scope and controller at the same time is not supported.upgrade_component.ts#L236-L242)。

downgradeComponent:把 Angular 组件带入 AngularJS 模板

downgradeComponent(info) 在 API 报告中的签名为:

export function downgradeComponent(info: {
  component: Type<any>;
  downgradedModule?: string;
  propagateDigest?: boolean;
  inputs?: string[];    // v4 起废弃,不再使用
  outputs?: string[];   // v4 起废弃,不再使用
  selectors?: string[]; // v4 起废弃,不再使用
}): any;

实现位于 downgrade_component.ts。它返回一个 AngularJS 指令工厂函数(带 $inject = ['$compile', '$injector', '$parse'] 标注),你把它注册到 AngularJS 模块即可:

angular.module('ng1App', [ng2Module])
  .directive('ng2Heroes', downgradeComponent({component: Ng2Heroes}));

参数含义(结合源码与 JSDoc):

  • component(必填):要降级的 Angular 组件 Type
  • downgradedModule(可选):组件"所属"的 downgradeModule() 返回的模块名。仅在降级多个 Angular 模块时需要,用于决定实例化组件时引导哪个 Angular 模块、从哪个 injector 取服务;
  • propagateDigest(可选,默认 true):是否每次 $digest 都在组件上执行 detectChanges。设为 false 时,仅当组件 inputs 变化才触发变更检测;
  • inputs / outputs / selectors:自 v4 起废弃且源码中已不使用(绑定信息现在直接从组件元数据静态获取),报告文件仍保留这些可选字段以维持签名兼容。

生成的指令具有 restrict: 'E'terminal: truerequire: [REQUIRE_INJECTOR, REQUIRE_NG_MODEL](即宿主元素上同时支持 AngularJS 侧 require 注入器与 ng-model 双向绑定桥接)。其 link 函数内部处理了三种注入器拓扑(顶层组件、同模块父组件、跨模块父组件),通过 DowngradeComponentAdapter 完成组件创建与 ng-model/输入输出的双向同步;Lite 模式下还会额外确保所有回调用 NgZone.run 包回 Angular zone(downgrade_component.ts#L109-L124)。

downgradeInjectable:把 Angular 服务带入 AngularJS

downgradeInjectable(token, downgradedModule?) 实现见 downgrade_injectable.ts。它返回一个只依赖 $injector 的工厂函数,典型用法:

angular.module('ng1App', [ng2Module])
  .factory('ng2HeroesService', downgradeInjectable(Ng2HeroesService));

随后在 AngularJS controller 中即可通过注入 'ng2HeroesService' 拿到 Angular 侧的服务实例。两个使用要点(来自源码 JSDoc 的重要告警):

  1. 该服务必须先被提供在升级应用的某个 NgModule 中;
  2. 使用 downgradeModule(),降级注入对象在其 Angular 模块被实例化之前不可用——只能在"保证模块已实例化"的范围内使用,例如只能用于同一模块内的已降级组件,不能用于可能独立于 Angular 使用的 AngularJS 组件。多模块场景下必须通过 downgradedModule 参数指定归属模块,源码会用 validateInjectionKey() 校验 key 存在并抛出清晰的错误(Trying to get the Angular injector before bootstrapping the corresponding Angular module. 等)。

downgradeModule:Lite 模式的惰性引导

API 报告中 downgradeModule 有两个重载:

export function downgradeModule<T>(moduleOrBootstrapFn: Type<T> | ((extraProviders: StaticProvider[]) => Promise<NgModuleRef<T>>)): string;
// @public @deprecated
export function downgradeModule<T>(moduleOrBootstrapFn: NgModuleFactory<T>): string;

第二个 NgModuleFactory 重载已被标记 @deprecated(报告文件中以 // @public @deprecated 注释标注),应传 NgModule 类或引导函数。实现见 downgrade_module.ts#L373-L452

  • 传入 NgModule:包装为 platformBrowser(extraProviders).bootstrapModule(...)
  • 传入 函数:函数接收一组额外 StaticProvider,返回解析为 NgModuleRef 的 Promise——这允许完全自定义惰性引导逻辑(例如先 import()bootstrapModule);
  • 返回值是生成的 AngularJS 包装模块名(形如 ngUpgrade.lazy1,内部自增 moduleUid),把它声明进主 AngularJS 模块即可。

内部机制上,它注册了一个名为 ngUpgrade.init 的兄弟模块:标记 UPGRADE_APP_TYPE_KEYLite、惰性工厂 LAZY_MODULE_REF 在首次被取用(即某个已降级组件/注入对象被实例化时)才执行 bootstrapFn,把结果包装为 NgAdapterInjector 并同样注册 PlatformRef.onDestroy(() => destroyApp($injector));还在 .config 中通过 DOWNGRADED_MODULE_COUNT_KEY 记录已降级模块数量,供 downgradeComponent 判断是否处于"多降级模块"模式。

与 UpgradeModule 的关键差异

源码文档明确警告:同一个混合应用不能同时使用 downgradeModule()UpgradeModule,只能二选一。两者的行为差异有两点(downgrade_module.ts#L93-L111):

  1. downgradeModule() 不在 Angular zone 内引导主 AngularJS 模块(UpgradeModule.bootstrap() 则在 zone 内调用 angular.bootstrap);
  2. downgradeModule() 不会自动把 Angular 侧的变更同步为 $digest()——它只在确定必要时(如已降级组件的输入变化)触发对方框架的变更检测。

这意味着 UpgradeModule 以更多变更检测运行换取"两边总被正确通知"的省心;downgradeModule 模式则把性能换给开发者,需要手动用 scope.$apply() / $rootScope.$digest() 触发 AngularJS 侧变更检测,或用 ngZone.run(...) 触发 Angular 侧。对于变更检测密集的应用,Lite 模式通常更省,代价是心智负担。

降级多个模块时的注意事项:每个降级组件/注入对象必须显式通过 downgradedModule 关联到某个模块;bootstrapModule()/bootstrapModuleFactory() 引导的每个模块都被视为"根"模块,providedIn: 'root' 的服务在每个模块中都会新建实例——如需共享,可建一个共享根模块并用它的 injector 引导其余模块(相关测试见 downgrade_module_spec.ts)。

getAngularJSGlobal / setAngularJSGlobal 与 VERSION

报告中的 getAngularJSGlobal(): anysetAngularJSGlobal(ng: any): void 实现在 upgrade 包 common 层的 angular1.ts:它们负责读取/替换全局 window.angular 对象引用。混合应用升级期间常需在多个 AngularJS 版本或 mock 之间切换全局对象,这两个函数提供了可控的访问入口;UpgradeModule.bootstrap() 中直接使用 (window as any)['angular'] 打补丁 resumeBootstrapupgrade_module.ts#L344-L361)也依赖同一全局对象的稳定性。

VERSION: Version 则是标准的包版本常量(转发自 common/src/version.ts),供运行时诊断与调试使用。

测试与验证:从仓库中继续深入

本 API 报告所描述的每个行为都有对应集成测试佐证,可作为深入阅读的入口:

小结

index.api.md 以 API Extractor 黄金报告的形式固化了 @angular/upgrade_static 的全部公共契约:UpgradeModule + UpgradeComponent 构成"Angular 引导、自动双向同步变更检测"的完整升级路径;downgradeModule + downgradeComponent + downgradeInjectable 构成"AngularJS 引导、按需惰性加载 Angular 模块"的 Lite 性能路径;两组模式互斥,需按应用的性能画像选择。对照 packages/upgrade/static/packages/upgrade/src/common/ 的源码,可以逐行验证报告中的每一条签名——这正是黄金 API 报告文件"报告即契约、契约随源码"的价值所在。

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

项目优选

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