Electron ServiceWorkers API:在主进程掌握 Service Worker 的运行状态、日志与通信
【免费下载链接】electron:electron: Build cross-platform desktop apps with JavaScript, HTML, and CSS项目地址: https://gitcode.com/GitHub_Trending/el/electron
session.serviceWorkers是 Electron 提供给主进程(Main Process)的 API,用于查询某个会话中所有正在运行的 Service Worker、监听其控制台日志与注册/生命周期事件,并通过ServiceWorkerMain实例与 Worker 进程直接进行 IPC 通信。读完本文,你将能够完整掌握ServiceWorkers类的全部事件与方法、理解其在 源码 中如何挂接 Chromium 的content::ServiceWorkerContext观察者,并学会在应用启动时为已注册的 Worker 主动启动并下发消息。
一、定位与获取方式:Session的serviceWorkers属性
ServiceWorkers类不直接从'electron'模块导出,它只能作为其他 API 方法的返回值获得。获取它的唯一入口是Session实例的serviceWorkers属性:
const { session } = require('electron') // 获取所有正在运行的 service worker console.log(session.defaultSession.serviceWorkers.getAllRunning()) // 监听控制台日志,并获取发送日志的 worker 信息 session.defaultSession.serviceWorkers.on('console-message', (event, messageDetails) => { console.log( 'Got service worker message', messageDetails, 'from', session.defaultSession.serviceWorkers.getFromVersionID(messageDetails.versionId) ) })从源码可以确认这一绑定关系:Session的 JS 绑定在 electron_api_session.cc 中通过.SetProperty("serviceWorkers", &Session::ServiceWorkerContext)注册,其实现会懒创建并缓存一个ServiceWorkerContext对象。值得注意的是,ServiceWorkerContext构造时还会把会话对应的存储分区配置登记进BrowsingDataRemover的数据类型列表(DATA_TYPE_SERVICE_WORKERS),因此session.clearStorageData()同样能清理已注册的 Service Worker——这一点在 测试用例 中被明确使用:每次用例前后都调用ses.clearStorageData()以保证隔离。
ServiceWorkerContext与底层的关系也很直接:其构造函数从ElectronBrowserContext的默认存储分区取出content::ServiceWorkerContext,并调用AddObserver(this)注册自己,析构时移除观察者。这意味着ServiceWorkers的全部事件与方法,本质都是对 Chromium 内容层 Service Worker 基础设施的封装。
二、ServiceWorkerInfo:每个 Worker 版本的描述对象
ServiceWorkers的多个方法都会返回ServiceWorkerInfo对象(结构定义见 service-worker-info.md):
| 字段 | 类型 | 说明 |
|---|---|---|
scriptUrl | string | 该 Service Worker 运行的脚本的完整 URL |
scope | string | 该 Service Worker 生效的基准 URL |
renderProcessId | number | 该 Worker 所在进程的虚拟 ID(非操作系统 PID),与webContents.getProcessId()使用同一套 ID 体系 |
versionId | number | 该 Service Worker 版本的 ID |
在实现层,scriptUrl、scope、renderProcessId由ServiceWorkerRunningInfoToDict从content::ServiceWorkerRunningInfo直接组装(见 electron_api_service_worker_context.cc#L68-L76)。
三、实例事件
Event:console-message
当某个 Service Worker 向控制台输出内容时发出,回调参数为:
eventEventmessageDetailsObjectmessagestring - 实际的控制台消息文本versionIdnumber - 发送日志的 Service Worker 的版本 IDsourcestring - 消息来源类型,可能为javascript、xml、network、console-api、storage、rendering、security、deprecation、worker、violation、intervention、recommendation或otherlevelnumber - 日志级别,0~3,依次对应verbose、info、warning、errorsourceUrlstring - 消息来源的 URLlineNumbernumber - 触发该消息的源码行号
source字段的字符串映射在 MessageSourceToString 中逐一对应 Blink 的ConsoleMessageSource枚举;level则是直接把message.message_level转成int32_t后透出。spec/api-service-workers-spec.ts#L77-L96 验证了级别映射:console.log/info为 1,warn为 2,error为 3,且来源均为console-api。
Event:registration-completed
当 Service Worker 注册成功时发出,可能发生在navigator.serviceWorker.register('/sw.js')成功 resolve 之后,也可能发生在 Chrome 扩展加载时。回调参数:
eventEventdetailsObjectscopestring - 该 Service Worker 注册的基准 URL
Event:running-status-changedExperimental
当某个 Service Worker 的运行状态变化时发出。回调参数:
detailsversionIdnumber - 状态发生变化的 Service Worker 版本 IDrunningStatusstring - 运行状态,可能为starting、running、stopping、stopped
从源码结构看,这个事件是四个 Chromium 生命周期回调的统一出口:OnVersionStartingRunning/OnVersionStartedRunning/OnVersionStoppingRunning/OnVersionStoppedRunning分别转调OnRunningStatusChanged,后者先通知对应ServiceWorkerMain实例,再向 JS 侧发射事件(见 electron_api_service_worker_context.cc#L99-L162)。
四、实例方法
serviceWorkers.getAllRunning()
返回Record<number, ServiceWorkerInfo>—— 键为 Service Worker 版本 ID、值为对应信息的对象。底层直接遍历content::ServiceWorkerContext::GetRunningServiceWorkerInfos()返回的flat_map,逐个版本组装字典(GetAllRunningWorkerInfo)。没有任何 Worker 运行时返回空对象{},这一点在 spec/api-service-workers-spec.ts#L52-L54 中得到验证。
serviceWorkers.getInfoFromVersionID(versionId)
versionIdnumber - Service Worker 版本 ID
返回ServiceWorkerInfo。若该 Worker 不存在或未运行,方法会抛出异常(源码中抛出"Could not find service worker with that version_id")。
serviceWorkers.getFromVersionID(versionId)Deprecated
versionIdnumber - Service Worker 版本 ID
返回ServiceWorkerInfo。若该 Worker 不存在或未运行则抛出异常。
已弃用:请改用getInfoFromVersionID。源码中GetFromVersionID会先发出弃用警告(ServiceWorkersDeprecateGetFromVersionID),然后委托给GetInfoFromVersionID(见 electron_api_service_worker_context.cc#L198-L208)。
serviceWorkers.getWorkerFromVersionID(versionId)Experimental
versionIdnumber - Service Worker 版本 ID
返回ServiceWorkerMain | undefined—— 与该版本 ID 关联的 Service Worker 实例;若没有对应版本,或其运行状态已变为stopped,则返回undefined。
serviceWorkers.startWorkerForScope(scope)Experimental
scopestring - 要启动的 Service Worker 的 scope
返回Promise<ServiceWorkerMain>—— Worker 启动后 resolve。若该 scope 的 Worker 已在运行,则不做任何额外操作。底层调用content::ServiceWorkerContext::StartWorkerForScope,以 scope 的一方 origin 构造StorageKey并异步等待结果(StartWorkerForScope)。
典型实战场景:应用启动时为所有已注册的 Worker 兜底启动,并通过 IPC 通知其"有窗口已创建":
const { app, session } = require('electron') const { serviceWorkers } = session.defaultSession // 收集所有 service worker 的 scope const workerScopes = Object.values(serviceWorkers.getAllRunning()).map((info) => info.scope) app.on('browser-window-created', async (event, window) => { for (const scope of workerScopes) { try { // 确保 worker 已启动 const serviceWorker = await serviceWorkers.startWorkerForScope(scope) serviceWorker.send('window-created', { windowId: window.id }) } catch (error) { console.error(`Failed to start service worker for ${scope}`) console.error(error) } } })五、ServiceWorkerMain:与 Worker 进程通信的句柄
startWorkerForScope与getWorkerFromVersionID返回的ServiceWorkerMain实例代表某个 scope 下脚本的某个具体版本(详见 service-worker-main.md),其成员同样为Experimental:
- 只读属性
scope:Worker 的 scope URLscriptURL:Worker 脚本 URLversionId:该 scope 下脚本版本的 IDipc:一个作用域限定到该 Worker 的IpcMainServiceWorker实例
- 方法
isDestroyed():Worker 版本是否已被销毁send(channel, ...args):通过channel向 Worker 进程发送异步消息,参数按结构化克隆算法序列化(与postMessage相同),发送 Function、Promise、Symbol、WeakMap 或 WeakSet 会抛异常;Worker 侧可用ipcRenderer监听同一channel接收startTask():发起一个任务以在end()被调用前保持 Worker 存活,返回对象仅含end方法;若从不调用end,Worker 在空闲时也不会终止
ServiceWorkerMain的 C++ 实现(electron_api_service_worker_main.h)透露了几个关键设计:
- 实例由
browser_context_id + storage_partition_config + version_id三元组(ServiceWorkerKey)唯一标识,保证不同会话/分区中相同 versionId 的 Worker 不会混淆; - 对象在 cppgc 堆上通过
SelfKeepAlive自持有,生命周期与底层 Service Worker 版本对齐,使得已注册的 IPC 处理器在整个存活期内都能正常分发; - 通过 Mojo 关联远程对象(
mojom::ElectronRenderer)与渲染进程建立连接来完成send通信,并在状态变化时可能断开该连接。
Worker 侧的回复通道由IpcMainServiceWorker提供(详见 ipc-main-service-worker.md):它是IpcMain的变体,提供on、once、removeListener、removeAllListeners、handle、handleOnce、removeHandler等方法,专用于主进程与 Service Worker 之间的send/invoke双向通信。相关端到端行为在 spec/api-service-worker-main-spec.ts 中有独立测试覆盖。
六、内部实现与内部方法一览
ServiceWorkerContext在 GetObjectTemplateBuilder 中除了注册文档列出的公开方法外,还暴露了两个以下划线开头的内部方法(未在公开文档中列出,属于实现细节):
_getWorkerFromVersionIDIfExists(versionId):按ServiceWorkerKey查找已存在的ServiceWorkerMain,找不到时不抛异常;_stopAllWorkers():调用底层StopAllServiceWorkers停止该会话的所有 Worker,返回 Promise。
事件与状态同步的完整链路可以概括为:
content::ServiceWorkerContext观察者回调(注册完成 / 控制台消息 / 状态变化 / 版本冗余)触发ServiceWorkerContext对应方法;- 控制台消息经
OnReportConsoleMessage组装为versionId、source、level、message、lineNumber、sourceUrl后发出console-message事件; - 状态变化经
OnRunningStatusChanged更新ServiceWorkerMain后发出running-status-changed事件; - 方法调用(
getAllRunning、getInfoFromVersionID等)直接读取GetRunningServiceWorkerInfos()的内存快照。
七、测试验证与使用前提
- spec/api-service-workers-spec.ts:覆盖
getAllRunning()(初始为空、加载注册页后恰好一个)、getFromVersionID()的scriptUrl/scope正确性、console-message的 source 与 level 映射; - spec/api-service-worker-main-spec.ts:覆盖
ServiceWorkerMain的实验性能力; - 事件触发的前提是页面真实注册了 Service Worker(如
navigator.serviceWorker.register('/sw.js')),且监听方需持有正确的Session实例——不同 partition 的会话各自独立。
适用前提小结:
running-status-changed、getWorkerFromVersionID、startWorkerForScope及全部ServiceWorkerMain能力均为Experimental,接口可能随版本演进调整;getFromVersionID已弃用,新代码应使用getInfoFromVersionID;getInfoFromVersionID与getFromVersionID在目标 Worker 不存在或未运行时会抛异常,生产代码中建议配合getAllRunning()的结果或try/catch做防御;- 与 Worker 通信走结构化克隆,函数等不可克隆值不能通过
send传递。
【免费下载链接】electron:electron: Build cross-platform desktop apps with JavaScript, HTML, and CSS项目地址: https://gitcode.com/GitHub_Trending/el/electron
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考