Electron Notification API 完全指南:主进程桌面通知从创建、交互事件到跨平台分组管理
2026/9/7 18:35:40 网站建设 项目流程

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

【免费下载链接】electron:electron: Build cross-platform desktop apps with JavaScript, HTML, and CSS项目地址: https://gitcode.com/GitHub_Trending/el/electron

本篇指南围绕 Electron 的Notification类展开,系统讲解主进程桌面通知的创建选项、实例事件(showclickreplyactionclosefailed)、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类,并把isSupportedgetHistoryremoveremoveAllremoveGroup静态方法逐一挂到 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_cast<ElectronBrowserClient*>(ElectronBrowserClient::Get()) ->GetNotificationPresenter(); }

isSupported()的结果取决于平台 presenter 是否成功创建(例如 Linux 下依赖 libnotify 环境)。

Notification.handleActivation(callback)Windows

  • callbackFunction
    • detailsActivationArguments - 通知激活详情(类型、原始参数字符串、actionIndexreplyuserInputs等)。

注册一个集中式回调,处理所有通知激活:点击、回复、动作按钮,无论触发来源对应的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 中,HandleActivationv8::Global<v8::Function>(配合base::NoDestructor)持久持有 JS 回调以避免被 GC,再通过electron::SetActivationHandler把 C++ 激活事件桥接到 JS;ActivationArguments到 JS 对象的转换逻辑位于 electron_api_notification.cc#L396-L420,只有type === 'action'时写入actionIndextype === 'reply'时写入replyuserInputs非空时才附加。

Notification.getHistory()macOS

返回Promise<Notification[]>,解析出当前仍存在于通知中心的全部已投递通知。每个返回的Notification都是与对应已投递通知相连的活对象:用户在通知中心与之交互时,clickreplyactionclose事件会正常触发——这使应用重启后可以重新挂接事件处理器。

  • 返回对象仅填充通知中心可得的信息:idgroupIdtitlesubtitlebodyactionssilenticon等其他属性为默认值。
  • 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)macOS

  • id(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)macOS

  • groupIdstring - 通知组标识符,对应构造函数中的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 映射到UNNotificationRequestidentifier,Windows 映射到 toast 的Tag。不提供或传空字符串时默认随机 UUID。配合Notification.remove()/Notification.getHistory()使用。
groupIdstringmacOS, Windows组标识符,用于在通知中心 / Action Center 中视觉分组。macOS 映射UNNotificationContent.threadIdentifier,Windows 映射 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逐项取出idgroupIdgroupTitletitlesubtitlebodyiconsilentreplyPlaceholderurgencyhasReplytimeoutTypeactionssoundcloseButtonTexttoastXml,并且:

if (id_.empty()) id_ = base::Uuid::GenerateRandomV4().AsLowercaseString();

即「未提供或为空字符串时默认随机 UUID」的行为正是此处实现,spec/api-notification-spec.ts#L40-L61 用正则^[0-9a-f]{8}-...验证了这一点。

Windows 平台的额外约束在Notification::New(electron_api_notification.cc#L193-L225)中强制:

  • app未就绪时抛错Cannot create Notification before app is ready
  • id/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:'show'

  • eventEvent

通知向用户显示时发出。由于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:'click'

  • eventEvent

用户点击通知时发出。

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:'close'

  • detailsEvent<>
    • reasonWindowsstring (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() })

源码上,NotificationClosed(electron_api_notification.cc#L320-L337)区分了有无reason:为空时直接Emit("close"),否则构造带reason字段的事件对象——这正是「reason仅在 Windows 出现」的底层原因(Windows presenter 传入了具体原因,macOS/Linux 不传)。

Event:'reply'macOSWindows

  • detailsEvent<>
    • replystring - 用户在回复输入框中键入的文本。
  • 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:'action'macOSWindows

  • detailsEvent<>
    • actionIndexnumber - 被触发的动作索引。
    • selectionIndexnumberWindows- 用户选中项的索引;未选择时为 -1。
  • actionIndexnumberDeprecated
  • selectionIndexnumberWindowsDeprecated
const { 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.plistNSUserNotificationAlertStylealert」等限制,以及 Windowsselection下拉动作的完整用法,详见 NotificationAction 结构文档。

Event:'failed'macOSWindows

  • eventEvent
  • errorstring - 执行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)值得注意:

  1. 若对象是getHistory()恢复的通知(is_restored_为 true)则直接返回——避免重复投递;
  2. 先调用Close()清理上一次显示;
  3. 通过 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.urgencystringLinux'normal''critical''low';默认'low'(参见 Notify 规范 urgency 级别定义)。
notification.timeoutTypestringLinux, Windows'default''never';设为'never'时通知永不过期,直到调用 API 关闭或用户关闭。
notification.actionsNotificationAction[]全平台通知动作数组。
notification.toastXmlstringWindows自定义 Toast XML。

其中idgroupId(Windows 下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」的分层设计:

  1. JS 层 lib/browser/api/notification.ts 仅做绑定挂载;
  2. API 层 shell/browser/api/electron_api_notification.h 定义Notification(cppgc 垃圾回收管理),通过NotificationDelegateProxy持有对 C++ 平台通知的WeakPtr,把平台回调转成 JS 事件;
  3. 平台层由NotificationPresenter(shell/browser/notifications/notification_presenter.h)按平台实现:macOS(notification_presenter_mac.mm,对接通知中心)、Windows(notification_presenter_win.cc,对接 toast/Action Center)、Linux(notification_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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询