首页
/ 深入解读 @angular/service-worker 公开 API:注册、版本更新与推送通知的完整接口面

深入解读 @angular/service-worker 公开 API:注册、版本更新与推送通知的完整接口面

2026-09-07 16:31:24作者:魏侃纯Zoe

@angular/service-worker 是 Angular 官方的 Service Worker 工具包,负责 Service Worker 的注册、版本更新管理和 Web Push 通知。本文以仓库中由 API Extractor 生成的公开 API 报告 index.api.md 为核心,逐一解读 provideServiceWorkerSwUpdateSwPush 等全部 @public 导出的签名与语义,并结合 packages/service-worker/src 目录下的源码实现,说明每个接口的默认值、运行时机与底层通信机制,帮助你在项目中正确使用这套 API 并通过 golden 文件约束其接口稳定性。

一、API 报告文件是什么

index.api.md 顶部明确声明:

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

该文件是 Angular 仓库 golden(黄金文件)机制的一部分,通过 tools/symbol-extractor 工具链对 @angular/service-worker 包做 API 表面提取后自动生成。文件中每个 // @public 注释标记了对应的导出符号属于对外承诺的稳定 API。仓库通过 goldens/public-api/manage.js 管理这批报告,任何对包公共接口的增删改都会体现在该文件中并在 CI 中被校验——这决定了本文讨论的所有符号都是经过审查、可被下游项目依赖的正式接口,而非内部实现细节。

从报告内容看,该包的公开 API 表面由以下符号构成:

导出符号 类型 职责
provideServiceWorker 函数 功能式 API(standalone/bootstrap)入口,注册 Service Worker 脚本
ServiceWorkerModule NgModule 入口,提供静态 register 方法
SwRegistrationOptions 抽象类(作为配置对象类型) 注册选项:开关、策略、作用域、Worker 类型、缓存更新策略
SwUpdate 可注入类 订阅版本更新事件、触发更新检查、激活新版本
SwPush 可注入类 管理 Web Push 订阅与通知点击/关闭事件
VersionEvent 及相关接口 类型 versionUpdates 流中事件载荷的类型定义
UnrecoverableStateEvent 接口 不可恢复状态事件载荷

以下逐节展开,每个接口的行为都以 packages/service-worker/src 中对应源文件为准。

二、注册入口:provideServiceWorkerServiceWorkerModule.register

报告中声明了两个注册入口:

// @public
export function provideServiceWorker(script: string, options?: SwRegistrationOptions): EnvironmentProviders;

// @public (undocumented)
export class ServiceWorkerModule {
    static register(script: string, options?: SwRegistrationOptions): ModuleWithProviders<ServiceWorkerModule>;
}

2.1 功能式 API:provideServiceWorker

源码位于 provider.ts。它在 bootstrapApplication 场景下返回一组 EnvironmentProviders,实际注册的 provider 集合为:

return makeEnvironmentProviders([
  SwPush,
  SwUpdate,
  {provide: SCRIPT, useValue: script},
  {provide: SwRegistrationOptions, useValue: options},
  {provide: NgswCommChannel, useFactory: ngswCommChannelFactory},
  provideAppInitializer(ngswAppInitializer),
]);

由此可以推断其完整职责链:

  1. 直接提供 SwPushSwUpdate 两个可注入服务;
  2. 用私有 SCRIPT token 保存脚本 URL(如 'ngsw-worker.js'),用 SwRegistrationOptions 抽象类 token 保存配置对象;
  3. 通过工厂 ngswCommChannelFactory 构造底层通信信道 NgswCommChannel(详见第六节);
  4. 借助 provideAppInitializer 挂入应用初始化函数 ngswAppInitializer,注册动作实际发生在应用初始化阶段。

provideServiceWorker 的 JSDoc 中给出了标准用法:

bootstrapApplication(AppComponent, {
  providers: [
    provideServiceWorker('ngsw-worker.js')
  ],
});

其测试用例见 provider_spec.ts,验证了该功能式入口与 NgModule 入口行为一致。

2.2 NgModule 入口:ServiceWorkerModule.register

module.ts 中的实现非常薄:

static register(
  script: string,
  options: SwRegistrationOptions = {},
): ModuleWithProviders<ServiceWorkerModule> {
  return {
    ngModule: ServiceWorkerModule,
    providers: [provideServiceWorker(script, options)],
  };
}

register 只是把 provideServiceWorker 包进 ModuleWithProviders 返回,两条入口最终走同一套 provider 与初始化逻辑。若 options.enabledfalse,两个入口都会“表现得像浏览器不支持 Service Worker 一样,直接不注册”。

2.3 初始化函数 ngswAppInitializer 的注册时机

真正的浏览器注册逻辑在 provider.tsngswAppInitializer 中,关键行为如下:

  • 服务端跳过ngServerMode 为真时直接返回,SSR 环境下不注册;
  • 开关判断!('serviceWorker' in navigator && options.enabled !== false) 时直接返回;
  • controllerchange 监听:在 Zone 外监听 controllerchange,每当新 Worker 成为 active 就向其 postMessage({action: 'INITIALIZE'}),使 Worker 即使没有应用流量也能完成自初始化;并在 ApplicationRef.onDestroy 时移除监听;
  • 按策略注册:解析 options.registrationStrategy 得到 readyToRegister Promise,Promise 完成后若应用未被销毁,则调用 navigator.serviceWorker.register(script, {scope, updateViaCache, type})
  • 失败不阻塞:注册失败仅打印格式化运行时错误(错误码 SERVICE_WORKER_REGISTRATION_FAILED),不会让应用卡住。

scopeupdateViaCachetype 三个字段会原样透传给浏览器原生 ServiceWorkerContainer#register 的 options 参数,因此其行为与 MDN 定义的语义一致。

三、SwRegistrationOptions:注册选项逐项说明

报告中的定义为:

// @public (undocumented)
export abstract class SwRegistrationOptions {
    enabled?: boolean;
    registrationStrategy?: string | (() => Observable<unknown>);
    scope?: string;
    type?: WorkerType;
    updateViaCache?: ServiceWorkerUpdateViaCache;
}

它是一个抽象类,但实际用法是配置对象形状(TypeScript 允许以对象字面量实现抽象类结构),并可通过同名 DI token 在 register() 之外单独提供。各字段语义(结合 provider.ts 的 JSDoc 与实现):

字段 类型 默认值 说明
enabled boolean true false 时不注册 Worker,SwPush/SwUpdate 退化为不可用状态
registrationStrategy string | (() => Observable<unknown>) 'registerWhenStable:30000' 决定注册时机,见下方四种策略
scope string 浏览器默认(脚本所在目录) Worker 可控制的 URL 范围
type WorkerType 'classic' 'classic' 不允许 import/export'module' 允许 ES Module 语法
updateViaCache ServiceWorkerUpdateViaCache 浏览器默认 浏览器更新 Worker 脚本时是否查阅 HTTP 缓存

registrationStrategy 是实践中最重要的选项,源码中的分支逻辑(provider.ts)支持:

  • 'registerWhenStable:<timeout>'(默认 'registerWhenStable:30000'):Promise.race([appRef.whenStable(), delayWithTimeout(timeout)])——应用一旦稳定(无待处理微/宏任务)立即注册,但最迟不超过 <timeout> 毫秒;<timeout> 省略则只等稳定。默认值的设计意图是“尽快注册,但不影响首次加载”。
  • 'registerImmediately'Promise.resolve(),同步路径上立即注册。
  • 'registerWithDelay:<timeout>':固定延时 <timeout> 毫秒后注册;省略时默认 0,即“尽快但仍是异步(等待所有微任务完成后)”。
  • Observable 工厂函数() => Observable,运行期订阅该 Observable,首个值发出即注册——适合“用户登录后才注册”这类自定义条件。

字符串形式按 strategy:args: 分割解析;遇到未知策略会抛出运行时错误 UNKNOWN_REGISTRATION_STRATEGY(错误码 5600)。

四、SwUpdate:版本更新事件的消费端

报告签名:

// @public
export class SwUpdate {
    constructor(sw: NgswCommChannel);
    activateUpdate(): Promise<boolean>;
    checkForUpdate(): Promise<boolean>;
    get isEnabled(): boolean;
    readonly unrecoverable: Observable<UnrecoverableStateEvent>;
    readonly versionUpdates: Observable<VersionEvent>;
}

实现见 update.ts

4.1 versionUpdates 事件流

构造器中,若 NgswCommChannel.isEnabled 为假,两个流都会退化为 NEVER(永不发射、永不结束),从而在 Worker 不可用的浏览器里订阅方不会收到任何事件;否则:

this.versionUpdates = this.sw.eventsOfType<VersionEvent>([
  'VERSION_DETECTED',
  'VERSION_INSTALLATION_FAILED',
  'VERSION_READY',
  'NO_NEW_VERSION_DETECTED',
]);
this.unrecoverable = this.sw.eventsOfType<UnrecoverableStateEvent>('UNRECOVERABLE_STATE');

即应用侧看到的 VersionEvent 联合类型(报告中的定义):

export type VersionEvent = VersionDetectedEvent | VersionInstallationFailedEvent
  | VersionReadyEvent | NoNewVersionDetectedEvent;

四个事件接口的 type 字段是字面量判别式,便于 switch 分发:

事件接口 type 载荷字段 语义
VersionDetectedEvent 'VERSION_DETECTED' version: {hash, appData?} 检测到服务器上存在新版本,即将开始下载
VersionInstallationFailedEvent 'VERSION_INSTALLATION_FAILED' versionerror 检查或下载新版本失败,适合接入日志/监控
VersionReadyEvent 'VERSION_READY' currentVersionlatestVersion 新版本已下载完毕、可激活
NoNewVersionDetectedEvent 'NO_NEW_VERSION_DETECTED' version 检查完毕,未发现新版本

每个 version 对象都携带 hash(版本哈希,即 ngsw.json 的 hash)与可选的 appData

4.2 checkForUpdateactivateUpdate

  • checkForUpdate():向 Worker 发送 CHECK_FOR_UPDATES 动作并携带一次性 nonce,Promise 在新版本“已下载且可激活”时 resolve 为 true,无新版本时为 false,出错则 reject。源码里用 ongoingCheckForUpdate 做了并发去重——重复调用会返回同一个 Promise。
  • activateUpdate():将当前客户端(标签页)切换到已就绪的最新版本。官方注释明确警告:大多数场景应通过整页刷新完成更新,因为不刷新就激活新 Worker 可能造成应用 shell 与懒加载 chunk 文件名不一致的版本错配;activateUpdate 仅在确定安全时使用。

两个方法都通过 sw.postMessageWithOperation 实现“请求—nonce—OPERATION_COMPLETED 回执”的 RPC 语义(见第六节)。isEnabled getter 则透传 NgswCommChannel.isEnabled,用于 UI 上判断推送/更新功能是否可用。

4.3 unrecoverable:不可恢复状态

UnrecoverableStateEvent 定义:

export interface UnrecoverableStateEvent {
    reason: string;
    type: 'UNRECOVERABLE_STATE';
}

触发场景(见 low_level.ts 的注释):Worker 用于向本客户端提供服务的某个应用版本进入了“不整页刷新就无法恢复”的损坏状态,例如缓存被浏览器部分清除后,某资源既取不到缓存也取不到服务器。监听该流并在收到事件后执行 location.reload() 是推荐的处理方式。

五、SwPush:Web Push 订阅与通知事件

报告签名(对应 push.ts):

export class SwPush {
    constructor(sw: NgswCommChannel);
    get isEnabled(): boolean;
    readonly messages: Observable<object>;
    readonly notificationClicks: Observable<{action: string; notification: NotificationOptions & {title: string}}>;
    readonly notificationCloses: Observable<{action: string; notification: NotificationOptions & {title: string}}>;
    readonly pushSubscriptionChanges: Observable<{oldSubscription: PushSubscription | null; newSubscription: PushSubscription | null}>;
    requestSubscription(options: {serverPublicKey: string}): Promise<PushSubscription>;
    readonly subscription: Observable<PushSubscription | null>;
    unsubscribe(): Promise<void>;
}

5.1 五个只读事件流

构造器把 NgswCommChannel 上的原始 message 事件按 type 过滤(eventsOfType)映射成五个语义化流;Worker 不可用时全部退化为 NEVER

  • messages:来自 Worker 的 PUSH 事件载荷(message.data),即收到推送消息的原始数据;
  • notificationClicksNOTIFICATION_CLICK 事件,载荷为 {action, notification}action 是用户点击的动作 id(无动作时为空字符串 '');notification 是含 titleNotificationOptions 对象,而非 Notification 实例;
  • notificationClosesNOTIFICATION_CLOSE 事件,结构同上,携带关闭时的动作;
  • pushSubscriptionChangesPUSH_SUBSCRIPTION_CHANGE 事件,载荷 {oldSubscription, newSubscription}。任一可为 null:无旧订阅时 oldSubscriptionnull,订阅失效且未被替换时 newSubscriptionnull。该流用于响应浏览器自动触发的订阅变化(如浏览器过期、密钥轮换);
  • subscription:合成流——把 registration 的 pushManager.getSubscription() 与本地 subscriptionChanges Subject 合并,始终反映当前生效的 PushSubscription | null

5.2 requestSubscriptionunsubscribe

requestSubscription(options: {serverPublicKey: string}) 的实现细节(push.ts):

  1. Worker 不可用(sw.isEnabled 为假)时直接 reject ERR_SW_NOT_SUPPORTED
  2. serverPublicKey 从 URL-safe Base64(_/-)还原为标准 Base64(//+)后 atob 解码为字节,构造 applicationServerKey
  3. userVisibleOnly: true 调用 pushManager.subscribe,成功则发出 subscriptionChanges 并 resolve 新的 PushSubscription

用户拒绝授权、浏览器不支持 Push API 或 Service Worker 都会导致 Promise reject,调用前可先检查 isEnabled

unsubscribe() 先取 subscription 流的首个值,若为 null 则抛出 NOT_SUBSCRIBED_TO_PUSH_NOTIFICATIONS(错误码 5602);否则调用 sub.unsubscribe(),返回 false 时抛 PUSH_SUBSCRIPTION_UNSUBSCRIBE_FAILED(5603),成功后向 subscriptionChanges 发出 null

5.3 服务端推送的消息格式

源码注释约定了推送消息载荷结构,服务端应推送形如:

{
  "notification": {
    "actions": "NotificationAction[]",
    "badge": "USVString",
    "body": "DOMString",
    "data": "any",
    "dir": "auto|ltr|rtl",
    "icon": "USVString",
    "image": "USVString",
    "lang": "DOMString",
    "renotify": "boolean",
    "requireInteraction": "boolean",
    "silent": "boolean",
    "tag": "DOMString",
    "timestamp": "DOMTimeStamp",
    "title": "DOMString",
    "vibrate": "number[]"
  }
}

其中仅 title 必填。Worker 监听 PushEvent 并据此创建 Notification 实例,应用侧再通过 messages / notificationClicks / notificationCloses 三个流消费后续交互。

六、底层通信信道:NgswCommChannel

报告中 SwUpdate/SwPush 的构造函数参数 sw: NgswCommChannel 揭示了两者的共同底座:low_level.ts 中的 NgswCommChannel(该类型本身是 @publicApi 导出,但属于内部基础设施,日常应用开发通常无需直接使用)。它封装了浏览器与 Worker 之间的全部消息通道:

  • Worker 追踪:监听 controllerchange,用 Subject 把“当前控制该页的 ServiceWorker”暴露为 worker 流;registration 流则由 worker 派生,调用 getRegistration()——在非安全上下文或隐私模式下可能取不到 registration,此时以 SERVICE_WORKER_DISABLED_OR_NOT_SUPPORTED_BY_THIS_BROWSER(错误码 5601)报错;
  • 事件分发message 事件中带 type 字段的对象进入统一事件流,eventsOfTypetype 过滤——这正是第四节、第五节所有语义化流的来源;
  • RPC 语义postMessageWithOperation(type, payload, nonce) 发送动作后等待带同一 nonceOPERATION_COMPLETED 回执,result 为布尔则 resolve,携带 error 则 reject。SwUpdate.checkForUpdate/activateUpdate 与 Worker 侧 msg.ts 的动作处理(worker/src/msg.ts)由此配对。

应用销毁时(ApplicationRef.onDestroy)信道会移除 controllerchangemessage 监听,避免内存泄漏。

七、错误码速查

该包的运行时错误集中在 errors.ts,保留错误码区间为 5600–5699:

错误码 枚举名 触发场景
5600 UNKNOWN_REGISTRATION_STRATEGY registrationStrategy 是无法识别的字符串
5601 SERVICE_WORKER_DISABLED_OR_NOT_SUPPORTED_BY_THIS_BROWSER 浏览器不支持 SW、enabled: false、或取不到 registration
5602 NOT_SUBSCRIBED_TO_PUSH_NOTIFICATIONS 未订阅时调用 SwPush.unsubscribe()
5603 PUSH_SUBSCRIPTION_UNSUBSCRIBE_FAILED sub.unsubscribe() 返回 false
5604 SERVICE_WORKER_REGISTRATION_FAILED navigator.serviceWorker.register 失败(仅打印,不抛出)

这些编号与 Angular 运行时错误码规范一致,生产环境日志中可通过编号直接定位问题。

八、包的出口与配套工件

package.json 定义了除 TS 入口外的额外产物出口:

  • @angular/service-worker/ngsw-worker.js:编译后的主 Worker 脚本(源码在 worker/src,由 worker/main.ts 汇聚 adapter、driver、data、manifest 等模块);
  • @angular/service-worker/safety-worker.js:兜底 Worker(safety-worker.js),在主 Worker 脚本加载失败时接管并尝试恢复;
  • @angular/service-worker/config/schema.json:构建期 ngsw-config 工具的 ngsw-config.json 配置 schema(config/schema.json,bin 入口为 ngsw-config);
  • 依赖约束:peerDependencies 要求 @angular/core 同版本与 rxjs ^6.5.3 || ^7.4.0,这也解释了报告头部 import { Observable } from 'rxjs' 的来源。

九、总结:如何正确消费这套 API

  1. 注册:standalone/bootstrap 应用用 provideServiceWorker('ngsw-worker.js', options),NgModule 应用用 ServiceWorkerModule.register(...),两者等价;默认 registerWhenStable:30000 策略通常无需修改,长轮询/持续定时器场景可显式指定 registerImmediately 或自定义 Observable 策略;
  2. 更新:订阅 SwUpdate.versionUpdatestype 分发四类 VersionEvent,在 VERSION_READY 时提示用户刷新;订阅 unrecoverable 并在事件到达时整页重载;确有需要才调用 activateUpdate
  3. 推送:注入 SwPush 后以 serverPublicKeyrequestSubscription 获取订阅,服务端按 notification 载荷约定推送,应用侧用 notificationClicks/notificationCloses 处理交互,用 pushSubscriptionChanges 同步服务端订阅状态;
  4. 稳定性:以上符号的签名受 goldens/public-api/service-worker/index.api.md golden 文件约束,依赖这些 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