ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Electron Notification API 完全指南:主进程桌面通知从创建、交互事件到跨平台分组管理

Electron Notification API 完全指南:主进程桌面通知从创建、交互事件到跨平台分组管理 Electron Notification API 完全指南主进程桌面通知从创建、交互事件到跨平台分组管理【免费下载链接】electron:electron: Build cross-platform desktop apps with JavaScript, HTML, and CSS项目地址: https://gitcode.com/GitHub_Trending/el/electron本篇指南围绕 Electron 的Notification类展开系统讲解主进程桌面通知的创建选项、实例事件show、click、reply、action、close、failed、macOS 通知中心历史管理与 Windows 激活回调等全部静态/实例 API并结合 Electron 仓库中shell/browser/下的 C 实现与spec/api-notification-spec.ts测试用例剖析参数解析、UUID 默认值、Windows 64 字符限制等平台差异的底层来源。读完本文你将能够编写在各操作系统上表现一致、可分组、可交互、可在应用重启后恢复监听的通知功能。一、Notification 类概览只在主进程使用Notification是一个 EventEmitter用于在操作系统层面创建桌面通知。它定义在 docs/api/notification.md 中属于Main主进程API如果希望从渲染进程显示通知官方建议改用 Web 标准的 Notifications API两者不可混用。跨进程需求可通过 IPC 桥接参见 IPC 教程。与 Web Notification API 不同new Notification()构造对象不会立即显示必须显式调用show()才会出现在操作系统上。Notification是 Electron 内置类不能在用户代码中被子类化相关背景见 FAQ。注意原文档说明在 macOS 上通知底层基于 UNNotification API。该 API 要求应用经过代码签名后通知才能出现未签名的二进制在调用通知 API 时会触发failed事件。从源码结构看JS 层只是一个极薄的绑定层。lib/browser/api/notification.ts 全文仅 14 行它通过process._linkedBinding(electron_browser_notification)拿到 C 侧的Notification类并把isSupported、getHistory、remove、removeAll、removeGroup静态方法逐一挂到 JS 类上其中handleActivation仅在process.platform win32且 C 绑定暴露该方法时才挂载。真正的实现位于 shell/browser/api/electron_api_notification.cc。二、静态方法Notification.isSupported()返回boolean表示当前系统是否支持桌面通知。实现上它检查浏览器客户端能否提供通知 presenter// shell/browser/api/electron_api_notification.cc节选 bool Notification::IsSupported() { return !!static_castElectronBrowserClient*(ElectronBrowserClient::Get()) -GetNotificationPresenter(); }即isSupported()的结果取决于平台 presenter 是否成功创建例如 Linux 下依赖 libnotify 环境。Notification.handleActivation(callback)WindowscallbackFunctiondetailsActivationArguments - 通知激活详情类型、原始参数字符串、actionIndex、reply、userInputs等。注册一个集中式回调处理所有通知激活点击、回复、动作按钮无论触发来源对应的Notification对象是否还在内存中。该方法自动处理时序问题若调用前激活已经发生例如应用因点击通知冷启动回调会立即带着当时的详情被调用一次之后的每次激活发生时回调照常触发回调持续注册直到再次调用handleActivation被替换。它覆盖的典型场景包括冷启动从通知点击拉起应用、Action Center 中残留通知在应用重启后没有内存对象、Notification对象被 GC、以及对象仍存活此时回调与实例事件同时触发。const { Notification, app } require(electron) app.whenReady().then(() { // Register handler for all notification activations Notification.handleActivation((details) { console.log(Notification activated:, details.type) if (details.type reply) { console.log(User reply:, details.reply) } else if (details.type action) { console.log(Action index:, details.actionIndex) } }) })源码佐证在 electron_api_notification.cc#L451-L463 中HandleActivation用v8::Globalv8::Function配合base::NoDestructor持久持有 JS 回调以避免被 GC再通过electron::SetActivationHandler把 C 激活事件桥接到 JSActivationArguments到 JS 对象的转换逻辑位于 electron_api_notification.cc#L396-L420只有type action时写入actionIndextype reply时写入replyuserInputs非空时才附加。Notification.getHistory()macOS返回PromiseNotification[]解析出当前仍存在于通知中心的全部已投递通知。每个返回的Notification都是与对应已投递通知相连的活对象用户在通知中心与之交互时click、reply、action、close事件会正常触发——这使应用重启后可以重新挂接事件处理器。返回对象仅填充通知中心可得的信息id、groupId、title、subtitle、bodyactions、silent、icon等其他属性为默认值。与new Notification()创建的普通通知不同getHistory()返回的通知不会因为对象被 GC 而从通知中心消失。对其调用show()会把通知中心中的原通知移除并以相同属性重新发布一条新通知。同受代码签名约束未签名的开发构建中通知不会投递到通知中心该方法将解析为空数组。const { Notification, app } require(electron) app.whenReady().then(async () { // Restore notifications from a previous session const notifications await Notification.getHistory() for (const n of notifications) { console.log(Found delivered notification: ${n.id} - ${n.title}) n.on(click, () { console.log(User clicked: ${n.id}) }) n.on(reply, (event) { console.log(User replied to ${n.id}: ${event.reply}) }) } // Keep references so events continue to fire })实现上electron_api_notification.cc#L466-L529 中的GetHistory调用 presenter 的GetDeliveredNotifications异步回调把每条NotificationInfoid/title/subtitle/body/group_id见 shell/browser/notifications/notification.h#L63-L76包装成一个以NotificationInfo专用构造器创建的对象再调用平台通知的Restore()把交互事件路由过来。Notification.remove(id)macOSid(string | string[]) - 要移除的通知标识符对应构造函数中的id值。按标识符从通知中心移除一条或多条已投递通知const { Notification } require(electron) // Remove a single notification Notification.remove(my-notification-id) // Remove multiple notifications Notification.remove([msg-1, msg-2, msg-3])从源码看electron_api_notification.cc#L531-L561参数既可以是字符串也可以是字符串数组传参缺失或类型不符会抛出Expected a string or array of strings错误——这一点在 spec/api-notification-spec.ts#L423-L445 中有逐项验证空字符串与空数组不抛错数字参数抛错。Notification.removeAll()macOS移除该应用在通知中心中的所有已投递通知const { Notification } require(electron) Notification.removeAll()Notification.removeGroup(groupId)macOSgroupIdstring - 通知组标识符对应构造函数中的groupId值。移除通知中心中所有具有给定groupId的已投递通知const { Notification } require(electron) // Remove all notifications in the chat-thread-1 group Notification.removeGroup(chat-thread-1)三、构造函数new Notification([options])optionsObject (optional)选项类型平台说明idstringmacOS, Windows通知唯一标识。macOS 映射到UNNotificationRequest的identifierWindows 映射到 toast 的Tag。不提供或传空字符串时默认随机 UUID。配合Notification.remove()/Notification.getHistory()使用。groupIdstringmacOS, Windows组标识符用于在通知中心 / Action Center 中视觉分组。macOS 映射UNNotificationContent.threadIdentifierWindows 映射 toast 的Group。配合Notification.removeGroup()使用。groupTitlestringWindows组标题。与groupId同时提供时Windows 会在分组通知上方显示一个标题头对应 toast 的header元素。titlestring全平台通知标题显示在通知窗口顶部。subtitlestringmacOS副标题显示在标题下方。bodystring全平台正文显示在标题或副标题下方。silentboolean全平台是否抑制通知声音。iconstring | NativeImage全平台通知图标。传字符串时必须是本地图标文件的有效路径。hasReplybooleanmacOS, Windows是否添加内联回复输入框。timeoutTypestringLinux, Windows超时时长default或never。replyPlaceholderstringmacOS, Windows内联回复输入框的占位文本。soundstringmacOS通知显示时播放的声音文件名。urgencystringLinux, Windows紧急级别normal、critical或low。actionsNotificationAction[]macOS, Windows附加动作。类型支持矩阵与限制见NotificationAction文档。closeButtonTextstringmacOS自定义关闭按钮文案空字符串使用系统本地化默认文本。toastXmlstringWindows自定义 Toast XML覆盖以上所有属性提供对设计行为的完全控制。注意原文档说明在 Windows 上urgency为critical只会把通知排到 Action Center 更高位置高于默认优先级通知但不会阻止自动消失要阻止自动消失还需把timeoutType设为never。构造参数的解析与校验源码级electron_api_notification.cc#L140-L168 展示了构造器如何用gin::Dictionary逐项取出id、groupId、groupTitle、title、subtitle、body、icon、silent、replyPlaceholder、urgency、hasReply、timeoutType、actions、sound、closeButtonText、toastXml并且if (id_.empty()) id_ base::Uuid::GenerateRandomV4().AsLowercaseString();即「未提供或为空字符串时默认随机 UUID」的行为正是此处实现spec/api-notification-spec.ts#L40-L61 用正则^[0-9a-f]{8}-...验证了这一点。Windows 平台的额外约束在Notification::Newelectron_api_notification.cc#L193-L225中强制app未就绪时抛错Cannot create Notification before app is readyid/groupId超过64 个 UTF-16 字符对应 Windows toastTag/Group上限时抛错设置了groupTitle却未设置groupId时抛错。这些校验与 spec/api-notification-spec.ts#L130-L173 的测试一一对应65 字符抛错、64 字符接受、groupTitle requires groupId to be set。最小可用示例const { Notification, app } require(electron) app.whenReady().then(() { const n new Notification({ title: Title!, subtitle: Subtitle!, body: Body! }) n.show() })一个更完整的窗口场景可参考官方 Fiddle 示例 docs/fiddles/features/notifications/其讲解见 通知教程。四、实例事件由new Notification创建的对象会发出以下事件部分事件仅限特定操作系统标注于名称后Event:showeventEvent通知向用户显示时发出。由于show()可重复调用每次会销毁旧通知并创建属性相同的新通知该事件可能触发多次。const { Notification, app } require(electron) app.whenReady().then(() { const n new Notification({ title: Title!, subtitle: Subtitle!, body: Body! }) n.on(show, () console.log(Notification shown!)) n.show() })Event:clickeventEvent用户点击通知时发出。const { Notification, app } require(electron) app.whenReady().then(() { const n new Notification({ title: Title!, subtitle: Subtitle!, body: Body! }) n.on(click, () console.log(Notification clicked!)) n.show() })Event:closedetailsEventreasonWindowsstring (optional) - 关闭原因userCanceled、applicationHidden或timedOut。通知被用户手动干预关闭时发出。该事件不保证在所有关闭场景下都触发。在 Windows 上close事件有三种触发途径程序调用notification.close()、用户关闭通知、系统超时。若通知在首次close事件发出后仍存在于 Action Center再次调用notification.close()会将其从 Action Center 移除但不会再次发出close事件。const { Notification, app } require(electron) app.whenReady().then(() { const n new Notification({ title: Title!, subtitle: Subtitle!, body: Body! }) n.on(close, () console.log(Notification closed!)) n.show() })源码上NotificationClosedelectron_api_notification.cc#L320-L337区分了有无reason为空时直接Emit(close)否则构造带reason字段的事件对象——这正是「reason仅在 Windows 出现」的底层原因Windows presenter 传入了具体原因macOS/Linux 不传。Event:replymacOSWindowsdetailsEventreplystring - 用户在回复输入框中键入的文本。replystringDeprecated当用户在带hasReply: true的通知上点击 “Reply” 按钮时发出。const { Notification, app } require(electron) app.whenReady().then(() { const n new Notification({ title: Send a Message, body: Body Text, hasReply: true, replyPlaceholder: Message text... }) n.on(reply, (e, reply) console.log(User replied: ${reply})) n.on(click, () console.log(Notification clicked)) n.show() })Event:actionmacOSWindowsdetailsEventactionIndexnumber - 被触发的动作索引。selectionIndexnumberWindows- 用户选中项的索引未选择时为 -1。actionIndexnumberDeprecatedselectionIndexnumberWindowsDeprecatedconst { Notification, app } require(electron) app.whenReady().then(() { const items [One, Two, Three] const n new Notification({ title: Choose an Action!, actions: [ { type: button, text: Action 1 }, { type: button, text: Action 2 }, { type: selection, text: Apply, items } ] }) n.on(click, () console.log(Notification clicked)) n.on(action, (e) { console.log(User triggered action at index: ${e.actionIndex}) if (e.selectionIndex -1) { console.log(User chose selection item ${items[e.selectionIndex]}) } }) n.show() })关于actions的平台支持矩阵、macOS 上额外按钮需满足「应用已签名 Info.plist中NSUserNotificationAlertStyle为alert」等限制以及 Windowsselection下拉动作的完整用法详见 NotificationAction 结构文档。Event:failedmacOSWindowseventEventerrorstring - 执行show()过程中遇到的错误。创建/显示原生通知发生错误时发出。macOS 上未签名应用调用通知 API 即属于此类见开头提示。const { Notification, app } require(electron) app.whenReady().then(() { const n new Notification({ title: Bad Action }) n.on(failed, (e, err) { console.log(Notification failed: , err) }) n.show() })五、实例方法与实例属性notification.show()立即向用户显示通知。与 Web Notification API 不同new Notification()本身不会显示通知必须调用本方法。若通知此前已显示过该方法会先销毁已显示的通知再创建一条属性完全相同的新通知。在 macOS 上对Notification.getHistory()返回的通知调用show()会把通知中心中的原通知移除并以相同属性重新发布一条。const { Notification, app } require(electron) app.whenReady().then(() { const n new Notification({ title: Title!, subtitle: Subtitle!, body: Body! }) n.show() })源码中Show()的完整流程electron_api_notification.cc#L356-L386值得注意若对象是getHistory()恢复的通知is_restored_为 true则直接返回——避免重复投递先调用Close()清理上一次显示通过 presenter 的CreateNotification(delegate_, id_)创建平台通知把 JS 属性拷贝到 C 的NotificationOptions见 shell/browser/notifications/notification.h#L36-L61最后调用平台通知的Show(options)。notification.close()移除通知。在 Windows 上通知仍在屏幕上时调用会使其消失并移出 Action Center通知已不在屏幕上时调用则尝试将其从 Action Center 移除。const { Notification, app } require(electron) app.whenReady().then(() { const n new Notification({ title: Title!, subtitle: Subtitle!, body: Body! }) n.show() setTimeout(() n.close(), 5000) })Close()的实现electron_api_notification.cc#L339-L353区分了「已被平台 dismiss」调用Remove()与「尚未 dismiss」调用Dismiss()两种路径对应 C 基类 notification.h#L83-L92 中Dismiss/Remove的注释部分平台包括 Windows初始移除并不会彻底销毁通知需要Remove兜底。实例属性一览属性类型平台说明notification.idstringmacOS, Windows只读。通知唯一标识构造时确定来自id选项未提供则生成 UUID。notification.groupIdstringmacOS, Windows只读。组标识符相同groupId的通知在通知中心/Action Center 中视觉分组。notification.groupTitlestringWindows只读。分组标题头文本。notification.titlestring全平台标题。notification.subtitlestring全平台副标题。notification.bodystring全平台正文。notification.replyPlaceholderstring全平台回复输入框占位文本。notification.soundstring全平台声音。notification.closeButtonTextstring全平台关闭按钮文本。notification.silentboolean全平台是否静默。notification.hasReplyboolean全平台是否有回复动作。notification.urgencystringLinuxnormal、critical或low默认low参见 Notify 规范 urgency 级别定义。notification.timeoutTypestringLinux, Windowsdefault或never设为never时通知永不过期直到调用 API 关闭或用户关闭。notification.actionsNotificationAction[]全平台通知动作数组。notification.toastXmlstringWindows自定义 Toast XML。其中id、groupIdWindows 下groupTitle为只读测试 spec/api-notification-spec.ts#L28-L38 验证了赋值n.id new-id会抛错groupId未提供时默认为空字符串spec/api-notification-spec.ts#L85-L95。JS 侧属性的 getter/setter 注册集中在 electron_api_notification.cc#L585-L612 的FillObjectTemplate中与上表一一对应。六、在 macOS 上播放声音macOS 上可以指定通知显示时播放的声音系统「系统偏好设置 声音」中的任何默认声音均可使用也支持自定义声音文件。自定义文件需拷贝到以下位置之一应用包内例如YourApp.app/Contents/Resources~/Library/Sounds/Library/Sounds/Network/Library/Sounds/System/Library/Sounds更多细节参考 Apple 的NSSound文档。七、平台注意事项Windows应用需要带AppUserModelID的开始菜单快捷方式及对应的ToastActivatorCLSID。生产环境中使用 Squirrel.Windows 时快捷键会自动配置开发阶段可能需要手动调用app.setAppUserModelId()详见 通知教程的 Windows 小节。通知点击/回复/动作的集中处理使用第二节的Notification.handleActivation()。macOS应用必须代码签名通知事件才能正确发出未签名二进制会触发failed事件。另外通知内容超过 256 字节会被截断。Linux通知通过libnotify发送兼容遵循 Desktop Notifications Specification 的桌面环境Cinnamon、Enlightenment、Unity、GNOME、KDE。八、架构小结与延伸阅读从源码结构看Electron 通知模块采用「JS API 绑定层 平台 Presenter」的分层设计JS 层 lib/browser/api/notification.ts 仅做绑定挂载API 层 shell/browser/api/electron_api_notification.h 定义Notificationcppgc 垃圾回收管理通过NotificationDelegateProxy持有对 C 平台通知的WeakPtr把平台回调转成 JS 事件平台层由NotificationPresentershell/browser/notifications/notification_presenter.h按平台实现macOSnotification_presenter_mac.mm对接通知中心、Windowsnotification_presenter_win.cc对接 toast/Action Center、Linuxnotification_presenter_linux.cc对接 libnotify。isSupported()、getHistory()、remove*等静态方法全部经由当前 presenter 分发。相关文档与测试Notification API 原文NotificationAction 结构 / ActivationArguments 结构渲染进程通知教程 与 Fiddle 示例 docs/fiddles/features/notifications/完整行为测试 spec/api-notification-spec.ts【免费下载链接】electron:electron: Build cross-platform desktop apps with JavaScript, HTML, and CSS项目地址: https://gitcode.com/GitHub_Trending/el/electron创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进