Angular @angular/platform-browser/animations/async:provideAnimationsAsync 异步动画提供器 API 详解
本文以 Angular 仓库中 goldens/public-api/platform-browser/animations/async/index.api.md 这份 API 报告文件为核心,结合 packages/platform-browser/animations/async 下的源码实现与测试用例,系统讲解 @angular/platform-browser/animations/async 包的唯一公开 API provideAnimationsAsync():它的函数签名与参数取值、返回的 EnvironmentProviders 内部结构、动画模块懒加载的底层渲染器切换机制,以及该 API 在 v20.2 中的弃用状态与迁移方向。读完后你可以完整掌握如何在 bootstrapApplication 场景下启用异步动画,并能读懂其源码级工作原理与失败回退策略。
一、API 报告文件:包的完整公开面
goldens/public-api/platform-browser/animations/async/index.api.md 是一份由 API Extractor 自动生成的 API 报告(文件开头明确标注 "Do not edit this file. It is a report generated by API Extractor."),它记录了 @angular/platform-browser_animations_async 包的完整公开 API 面。报告全文如下:
import { EnvironmentProviders } from '@angular/core';
// @public @deprecated
export function provideAnimationsAsync(type?: 'animations' | 'noop'): EnvironmentProviders;
// (No @packageDocumentation comment for this package)
这份报告传达了三个关键事实:
- 该包只有一个公开导出:函数
provideAnimationsAsync,没有类、接口或其他类型导出; - 该 API 已被标记
@deprecated(@public @deprecated双注解),但仍属于@public层级,即当前版本仍可正常导入使用; - 依赖面极小:仅从
@angular/core导入EnvironmentProviders,说明它是一个纯提供者工厂函数,不依赖任何具体渲染模块。
该包的定位由 PACKAGE.md 一句话概括:"Provides a lazy loaded infrastructure for the rendering of animations in supported browsers."(为受支持的浏览器提供动画渲染的懒加载基础设施)。其源码入口链路为:public_api.ts 重新导出 async-animations.ts,后者再导出 provideAnimationsAsync 与私有导出 private_export.ts。
二、函数签名与参数说明
结合 API 报告与 providers.ts 源码,该函数的完整定义为:
export function provideAnimationsAsync(
type: 'animations' | 'noop' = 'animations',
): EnvironmentProviders;
参数与返回值说明如下:
| 成员 | 类型 | 默认值 | 说明 |
|---|---|---|---|
type |
'animations' | 'noop' |
'animations' |
传入 'noop' 时禁用动画(安装无操作动画渲染器);不传或传 'animations' 时启用真实动画 |
| 返回值 | EnvironmentProviders |
— | 环境提供者集合,加入 bootstrapApplication 的 providers 列表即可生效 |
源码中的 JSDoc 给出了标准用法示例(原样继承自 providers.ts 注释):
bootstrapApplication(RootComponent, {
providers: [
provideAnimationsAsync()
]
});
使用场景是:当应用通过 bootstrapApplication 函数启动(而非 NgModule 启动)时,无需再导入 BrowserAnimationsModule,只需将该函数返回的提供者加入 providers 列表。与同步版 provideAnimations() 的核心差异在于:异步版不会立即加载动画渲染器,动画模块会在首次真正需要时才动态导入,从而把 @angular/animations/browser 的体积移出关键渲染路径。
三、返回的提供者内部结构
provideAnimationsAsync 通过 makeEnvironmentProviders 返回两组环境提供者(见 providers.ts):
return makeEnvironmentProviders([
{
provide: RendererFactory2,
useFactory: () => {
return new AsyncAnimationRendererFactory(
inject(DOCUMENT),
inject(DomRendererFactory2),
inject(NgZone),
type,
);
},
},
{
provide: ANIMATION_MODULE_TYPE,
useValue: type === 'noop' ? 'NoopAnimations' : 'BrowserAnimations',
},
]);
RendererFactory2提供者:工厂函数会注入DOCUMENT、DomRendererFactory2(来自 index.ts 的私有符号ɵDomRendererFactory2)、NgZone和type,构造AsyncAnimationRendererFactory实例。这个工厂是懒加载机制的载体;ANIMATION_MODULE_TYPE提供者:写入字符串值'NoopAnimations'或'BrowserAnimations'。@angular/common的NgIf、NgClass、NgStyle等结构性指令会读取该 token 来决定是否安装动画钩子,因此它同时决定了"结构指令是否走动画路径";- 服务端自动降级:源码中存在显式判断——当
ngServerMode为真时,type会被强制改写为'noop'("Animations don't work on the server so we switch them over to no-op automatically."),即在 SSR 环境中该函数自动退化为无动画模式,即使调用方传入了'animations'; - 函数入口还调用了
performanceMarkFeature('NgAsyncAnimations')(即ɵperformanceMarkFeature),用于在性能 trace 中打点,标记应用启用了异步动画特性。
四、懒加载机制源码解析
懒加载的核心实现在 async_animation_renderer.ts 中的 AsyncAnimationRendererFactory 与 DynamicDelegationRenderer 两个类,它们通过私有导出 private_export.ts 以 ɵAsyncAnimationRendererFactory 等符号暴露给内部包。
4.1 模块动态导入与加载调度
loadImpl() 方法负责真正加载动画包:
private loadImpl(): Promise<AnimationRendererFactory> {
const loadFn = () => this.moduleImpl ?? import('@angular/animations/browser').then((m) => m);
let moduleImplPromise: typeof this.moduleImpl;
if (this.loadingSchedulerFn) {
moduleImplPromise = this.loadingSchedulerFn(loadFn);
} else {
moduleImplPromise = loadFn();
}
// ...
}
要点:
- 使用原生
import('@angular/animations/browser')动态导入,动画引擎与渲染器被拆分为独立 chunk; moduleImpl构造参数允许测试或内部场景注入一个 mock 模块(含ɵcreateEngine与ɵAnimationRendererFactory),这就是测试中"提供 mock 实现"的入口;- 私有注入 token
ɵASYNC_ANIMATION_LOADING_SCHEDULER_FN允许替换加载调度函数,对loadFn进行二次封装(源码注释标明其为 "Private token for investigation purposes",即调研/实验用途); - 加载失败时抛出
RuntimeError(错误码ANIMATION_RENDERER_ASYNC_LOADING_FAILURE),开发模式下提示:"Async loading for animations package was enabled, but loading failed. Angular falls back to using regular rendering. No animations will be displayed and their styles won't be applied."——即失败回退到普通 DOM 渲染,动画及其样式不再应用,但应用不会崩溃。
4.2 渲染器动态切换:DynamicDelegationRenderer
createRenderer() 是整个懒加载策略的枢纽(async_animation_renderer.ts):
- 先用当前委托(初始为
DomRendererFactory2)创建普通渲染器; - 若渲染器类型已为
AnimationRendererType.Regular,说明动画工厂已加载,直接返回; - 否则把该渲染器包装进
DynamicDelegationRenderer,并立即返回这个"动态代理渲染器"; - 仅当组件确实用到动画时才触发模块加载:判断条件是
rendererType?.data?.['animation'](即该组件声明了动画触发器),且尚未发起加载; - 模块加载完成后,用新的
AnimationRendererFactory再创建一个真正的动画渲染器,调用dynamicRenderer.use(animationRenderer)完成切换,并通过ChangeDetectionScheduler.notify(NotificationSource.AsyncAnimationsLoaded)通知调度器——这是触发一次额外变更检测的关键,因为渲染器切换后需要重放动画状态; - 若加载失败,则
.catch分支让代理永久使用普通渲染器。
DynamicDelegationRenderer(ɵtype = AnimationRendererType.Delegated)本身实现 Renderer2 接口,把每个调用透传给当前委托,但额外维护一个 replay 回放队列:
private shouldReplay(propOrEventName: string): boolean {
//`null` indicates that we no longer need to collect events and properties
return this.replay !== null && propOrEventName.startsWith(ANIMATION_PREFIX);
}
其中 ANIMATION_PREFIX = '@'。在动画模块加载完成之前,组件上设置的 @trigger 属性、@trigger.start/... 事件都会先透传给普通 DOM 渲染器(源码中还会把 throwOnSyntheticProps 置为 false,防止合成属性报错),同时被记录进 replay 队列;一旦 use() 切换到动画渲染器,队列中的 setProperty / listen 调用会被重放一遍,保证加载期间声明的动画状态在加载完成后依然完整生效。加载完成、队列处理完毕后 replay 被置为 null,停止收集。
另外,工厂实现了 OnDestroy:根视图移除时调用 this._engine?.flush(),源码注释解释了原因——异步动画模式下没有注入式引擎(InjectableAnimationEngine),TransitionAnimationEngine 只会 markElementAsRemoved(),必须 flush 才能真正移除 DOM 节点。
五、测试用例验证
animation_renderer_spec.ts 对上述机制做了直接验证,可作为行为依据:
- 测试通过
provideAnimationsAsync()提供动画能力(第 421、444、474 行等多处),并断言factory.createRenderer(element, type)返回的是DynamicDelegationRenderer实例(第 436 行expect(renderer).toBeInstanceOf(DynamicDelegationRenderer)); - 通过向
AsyncAnimationRendererFactory注入 mockmoduleImpl(含ɵcreateEngine)控制加载时机,验证"加载前委托普通渲染器、加载后切换动画渲染器"的完整生命周期; - 私有导出 private_export.ts 暴露的
AsyncAnimationRendererFactory与DynamicDelegationRenderer正是测试断言所需的符号。
六、弃用状态与迁移方向
API 报告中的 @deprecated 注解在源码中有明确出处(providers.ts 的 JSDoc):
@deprecated 20.2 Use animate.enter or animate.leave instead. Intent to remove in v23
即该函数自 v20.2 起被弃用,官方建议改用信号形式的 animate.enter / animate.leave 等 API,并计划在 v23 中移除。值得对照的是,同目录下的兄弟包 goldens/public-api/platform-browser/animations/index.api.md 中,同步版的 provideAnimations()、provideNoopAnimations() 以及 BrowserAnimationsModule、NoopAnimationsModule 同样全部带有 @deprecated 标记——说明新旧两套动画提供方式正一同向声明式动画 API 迁移。对于新项目,建议直接采用文档推荐的最新动画 API;provideAnimationsAsync() 仍然适用于需要兼容既有启动方式、且要求动画包懒加载的存量应用(受支持浏览器、且非纯 SSR 场景,服务端会自动降级为 noop)。
七、关键文件索引
| 文件 | 作用 |
|---|---|
| goldens/public-api/platform-browser/animations/async/index.api.md | 本包公开 API 面的权威报告(本文核心依据) |
| packages/platform-browser/animations/async/src/providers.ts | provideAnimationsAsync 实现与提供者结构 |
| packages/platform-browser/animations/async/src/async_animation_renderer.ts | 懒加载工厂与动态委托渲染器实现 |
| packages/platform-browser/animations/async/src/async-animations.ts | 公开导出入口 |
| packages/platform-browser/animations/async/src/private_export.ts | ɵ 私有符号导出 |
| packages/platform-browser/animations/async/test/animation_renderer_spec.ts | 渲染器切换行为测试 |
| goldens/public-api/platform-browser/animations/index.api.md | 同步版动画包 API 报告(对照迁移方向) |
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00