鸿蒙PC 使用 Electron 实现通知功能详解
双重保障机制:优先使用 HTML5 API,降级到 Electron API跨平台支持:支持 Windows、macOS、Linux 和鸿蒙PC平台平台优化:针对不同平台使用最佳实践事件监听:完善的错误处理和事件监听。
鸿蒙PC 使用 Electron 实现通知功能详解
问题背景
在桌面应用开发中,通知功能是一个重要的用户体验特性。无论是消息提醒、任务完成通知,还是系统状态变化提示,通知都能帮助用户及时了解应用的状态变化。
需求分析
-
显示系统通知:在系统通知中心显示应用通知
-
跨平台兼容:在 Windows、macOS、Linux 以及鸿蒙PC平台上都能正常工作
-
通知交互:支持通知点击、关闭等事件处理
-
权限管理:正确处理不同平台的通知权限
-
用户体验优化:提供友好的通知内容和样式
技术挑战
-
平台差异:不同操作系统的通知 API 和行为不同
-
权限处理:macOS、Windows、Linux 的通知权限机制不同
-
API 兼容性:HTML5 Notification API 和 Electron Notification API 的使用差异
-
鸿蒙平台适配:需要确保在鸿蒙PC平台上也能正常工作
效果预览

实现方案
方案对比
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| HTML5 Notification API | 简单直接,浏览器原生支持,跨平台兼容性好 | 需要用户授权,某些浏览器可能不支持 | 优先使用,最可靠 |
| Electron Notification API | 功能强大,可配置选项多,在主进程中运行 | 需要 IPC 通信,平台差异较大 | 降级方案,功能更丰富 |
| 第三方通知库 | 功能完整,统一接口 | 增加依赖,可能过度设计 | 复杂场景 |
最终方案
采用双重保障方案,确保在各种环境下都能正常工作:
-
优先使用 HTML5 Notification API:
new Notification()- 浏览器原生 API,在鸿蒙PC平台上完全支持 -
降级到 Electron IPC + Notification API:通过主进程使用 Electron 的
Notification模块 -
平台特定优化:针对不同平台使用不同的通知选项和配置
-
事件监听机制:监听通知点击、关闭、显示等事件
方案优势
-
✅ 跨平台兼容:支持 Windows、macOS、Linux 和鸿蒙PC平台
-
✅ 双重保障:HTML5 API 优先,Electron API 降级
-
✅ 平台优化:针对不同平台使用最佳实践
-
✅ 用户体验好:提供丰富的通知选项和交互功能
代码实现
1. 主进程实现(main.js)
在主进程中实现 Electron 通知功能:
const { app, BrowserWindow, ipcMain, Notification } = require('electron');
const path = require('path');
// 存储通知实例,用于后续关闭和管理
const notifications = new Map();
// 显示通知
ipcMain.handle('show-notification', async (event, options) => {
console.log('收到显示通知请求:', options);
// 检查通知权限
if (!Notification.isSupported()) {
console.warn('当前系统不支持通知功能');
return { success: false, error: 'Notifications not supported' };
}
try {
// macOS 特殊处理:检查系统通知权限
if (process.platform === 'darwin') {
// macOS 上 Electron 的 Notification 不需要额外权限请求
// 但需要确保应用在 Dock 中(通常 Electron 应用会自动处理)
console.log('macOS 平台,准备显示通知');
}
// 创建通知选项(根据平台调整)
const notificationOptions = {
title: options.title || '通知',
body: options.body || options.message || '',
silent: options.silent || false
};
// macOS 特殊选项
if (process.platform === 'darwin') {
// macOS 支持 subtitle
if (options.subtitle) {
notificationOptions.subtitle = options.subtitle;
}
// macOS 支持 sound(使用系统默认声音)
if (options.sound !== false) {
notificationOptions.sound = options.sound || 'default';
}
// macOS 支持 urgency(但可能被系统忽略)
if (options.urgency) {
notificationOptions.urgency = options.urgency;
}
} else {
// Linux/Windows/鸿蒙PC 选项
notificationOptions.icon = options.icon || undefined;
notificationOptions.urgency = options.urgency || 'normal';
notificationOptions.timeoutType = options.timeoutType || 'default';
if (options.actions && options.actions.length > 0) {
notificationOptions.actions = options.actions;
}
if (options.closeButtonText) {
notificationOptions.closeButtonText = options.closeButtonText;
}
if (options.hasReply) {
notificationOptions.hasReply = options.hasReply;
notificationOptions.replyPlaceholder = options.replyPlaceholder;
}
}
console.log('创建通知,选项:', JSON.stringify(notificationOptions, null, 2));
// 创建通知
let notification;
try {
notification = new Notification(notificationOptions);
console.log('通知对象创建成功');
} catch (error) {
console.error('创建通知对象失败:', error);
throw error;
}
// 生成通知ID
const notificationId = options.notificationId || Date.now();
// 监听通知点击事件
notification.on('click', () => {
console.log('通知被点击:', notificationId);
// 通知所有窗口
BrowserWindow.getAllWindows().forEach(win => {
if (!win.isDestroyed()) {
win.webContents.send('notification-clicked', notificationId);
}
});
// 如果指定了点击回调,激活窗口
if (options.onClick !== false && mainWindow && !mainWindow.isDestroyed()) {
mainWindow.show();
mainWindow.focus();
}
});
// 监听通知关闭事件
notification.on('close', () => {
console.log('通知已关闭:', notificationId);
notifications.delete(notificationId);
});
// 监听通知显示事件
notification.on('show', () => {
console.log('✅ 通知已显示:', notificationId);
console.log('通知标题:', notificationOptions.title);
console.log('通知内容:', notificationOptions.body);
});
// 监听通知错误事件
notification.on('error', (error) => {
console.error('❌ 通知错误:', notificationId, error);
console.error('错误详情:', error.message);
});
// 监听操作按钮点击事件(如果支持,非 macOS)
if (process.platform !== 'darwin' && notificationOptions.actions && notificationOptions.actions.length > 0) {
notification.on('action', (event, index) => {
console.log('通知操作按钮被点击:', notificationId, index);
BrowserWindow.getAllWindows().forEach(win => {
if (!win.isDestroyed()) {
win.webContents.send('notification-action-clicked', {
notificationId: notificationId,
actionIndex: index
});
}
});
});
}
// 所有平台都需要调用 show() 才能显示通知
try {
notification.show();
console.log('通知 show() 调用成功');
if (process.platform === 'darwin') {
console.log('macOS 通知已调用 show(),应该会显示在通知中心');
}
} catch (error) {
console.error('通知 show() 调用失败:', error);
console.error('错误详情:', error.stack);
if (process.platform !== 'darwin') {
// 非 macOS 平台,show() 失败是严重错误
throw error;
}
}
// 存储通知实例(防止被垃圾回收)
notifications.set(notificationId, notification);
// 添加超时清理(5分钟后自动清理)
setTimeout(() => {
if (notifications.has(notificationId)) {
notifications.delete(notificationId);
console.log('通知实例已清理:', notificationId);
}
}, 5 * 60 * 1000);
console.log('通知处理完成,ID:', notificationId);
return { success: true, notificationId: notificationId };
} catch (error) {
console.error('显示通知失败:', error);
return { success: false, error: error.message };
}
});
// 关闭通知
ipcMain.on('close-notification', (event, notificationId) => {
console.log('收到关闭通知请求:', notificationId);
const notification = notifications.get(notificationId);
if (notification) {
notification.close();
notifications.delete(notificationId);
} else {
console.warn('通知不存在:', notificationId);
}
});
// 应用准备就绪时检查通知支持
app.whenReady().then(() => {
// 检查通知是否支持
if (Notification.isSupported()) {
console.log('通知功能已支持');
console.log('平台:', process.platform);
// macOS 特殊处理:确保应用在 Dock 中
if (process.platform === 'darwin') {
if (app.dock) {
app.dock.show().catch(err => {
console.log('Dock 显示(可选):', err.message);
});
} else {
console.warn('app.dock 不可用,可能影响 macOS 通知显示');
}
}
} else {
console.warn('通知功能不支持');
console.warn('请检查系统通知设置');
}
createWindow();
});
2. 预加载脚本(preload.js)
在预加载脚本中暴露安全的通知 API:
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', {
versions: {
chrome: process.versions.chrome,
node: process.versions.node,
electron: process.versions.electron
},
// 显示通知
showNotification: (options) => {
return ipcRenderer.invoke('show-notification', options);
},
// 关闭通知
closeNotification: (notificationId) => {
ipcRenderer.send('close-notification', notificationId);
},
// 监听通知点击事件
onNotificationClick: (callback) => {
ipcRenderer.on('notification-clicked', (event, notificationId) => {
callback(notificationId);
});
},
// 移除通知点击监听
removeNotificationClickListener: () => {
ipcRenderer.removeAllListeners('notification-clicked');
}
});
3. 渲染进程实现(index.html)
在页面中实现显示通知的功能:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>通知示例</title>
</head>
<body>
<button οnclick="showNotification()">🔔 显示通知</button>
<script>
// 显示通知
async function showNotification() {
console.log('点击显示通知按钮');
try {
// 方法1: 优先使用浏览器原生 Notification API
if ('Notification' in window) {
// 请求权限
if (Notification.permission === 'default') {
const permission = await Notification.requestPermission();
if (permission !== 'granted') {
alert('通知权限被拒绝');
return;
}
}
if (Notification.permission === 'granted') {
// 使用浏览器原生通知
const notification = new Notification('坚果通知', {
body: '这是来自坚果的通知',
icon: undefined,
badge: undefined,
tag: 'electron-notification',
requireInteraction: false,
silent: false
});
notification.onclick = () => {
console.log('通知被点击');
window.focus();
};
notification.onshow = () => {
console.log('通知已显示');
};
notification.onclose = () => {
console.log('通知已关闭');
};
notification.onerror = (error) => {
console.error('通知错误:', error);
};
return;
}
}
// 方法2: 使用 Electron IPC 显示通知
if (window.electronAPI && window.electronAPI.showNotification) {
console.log('使用 Electron IPC 显示通知');
// 检测平台,使用不同的选项
const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
const notificationOptions = {
title: '坚果通知',
body: '这是来自坚果的通知',
message: '这是来自坚果的通知',
notificationId: Date.now(),
silent: false,
onClick: true
};
// macOS 特殊选项
if (isMac) {
notificationOptions.subtitle = '坚果派';
notificationOptions.sound = 'default';
} else {
notificationOptions.urgency = 'normal';
}
console.log('通知选项:', notificationOptions);
const result = await window.electronAPI.showNotification(notificationOptions);
if (result.success) {
console.log('通知显示成功,ID:', result.notificationId);
// 监听通知点击事件
if (window.electronAPI.onNotificationClick) {
window.electronAPI.onNotificationClick((notificationId) => {
console.log('通知被点击:', notificationId);
alert('通知 ID ' + notificationId + ' 被点击了!');
});
}
} else {
console.error('显示通知失败:', result.error);
alert('显示通知失败: ' + result.error);
}
} else {
alert('通知功能不可用');
}
} catch (error) {
console.error('显示通知失败:', error);
alert('显示通知出错: ' + error.message);
}
}
// 页面加载时检查通知权限
window.addEventListener('DOMContentLoaded', () => {
if ('Notification' in window) {
console.log('浏览器支持通知功能');
console.log('当前通知权限:', Notification.permission);
} else {
console.log('浏览器不支持通知功能');
}
});
</script>
</body>
</html>
鸿蒙PC平台兼容性
兼容性说明
重要提示:本文档中实现的所有代码在鸿蒙PC平台上都可以正常使用!
1. HTML5 Notification API 支持
鸿蒙PC平台基于 Chromium 内核,完全支持 HTML5 Notification API:
// 在鸿蒙PC平台上,这段代码可以正常工作
if ('Notification' in window) {
const permission = await Notification.requestPermission();
if (permission === 'granted') {
const notification = new Notification('标题', {
body: '内容'
});
}
}
2. Electron Notification API 支持
Electron 的 Notification API 在鸿蒙PC平台上完全支持:
// 在鸿蒙PC平台上,Electron Notification API 可以正常工作
const { Notification } = require('electron');
const notification = new Notification({
title: '标题',
body: '内容'
});
notification.show();
3. IPC 通信支持
Electron 的 IPC 通信机制在鸿蒙PC平台上完全支持:
// 主进程和渲染进程之间的通信在鸿蒙PC上正常工作
ipcMain.handle('show-notification', async (event, options) => {
// 处理逻辑
});
// 渲染进程调用
const result = await window.electronAPI.showNotification(options);
4. 平台特性支持
鸿蒙PC平台支持 Linux 风格的通知选项:
-
✅
icon- 通知图标 -
✅
urgency- 通知紧急程度 -
✅
timeoutType- 超时类型 -
✅
actions- 操作按钮(如果系统支持) -
✅
closeButtonText- 关闭按钮文本
测试验证
在鸿蒙PC平台上测试结果:
-
✅ HTML5 Notification API 正常工作
-
✅ Electron Notification API 正常工作
-
✅ IPC 通信正常
-
✅ 通知点击事件正常
-
✅ 通知关闭事件正常
-
✅ 通知显示事件正常
遇到的问题与解决方案
问题一:macOS 上通知不显示
现象:在 macOS 上点击通知按钮后,通知没有显示。
原因分析:
-
macOS 需要应用在 Dock 中才能显示通知
-
需要调用
notification.show()方法 -
系统通知权限可能被禁用
解决方案:
-
确保应用在 Dock 中:
if (process.platform === 'darwin' && app.dock) {
app.dock.show();
}
-
调用 show() 方法:
// macOS 上也需要调用 show() notification.show();
-
检查系统通知设置:
-
打开"系统设置" > "通知与专注模式"
-
找到应用并确保通知权限已启用
-
问题二:通知权限被拒绝
现象:浏览器原生通知 API 返回权限被拒绝。
解决方案:
-
请求权限:
if (Notification.permission === 'default') {
const permission = await Notification.requestPermission();
if (permission !== 'granted') {
// 处理权限被拒绝的情况
alert('通知权限被拒绝,请在浏览器设置中启用');
return;
}
}
-
降级到 Electron API:
// 如果浏览器 API 不可用,使用 Electron API
if (window.electronAPI && window.electronAPI.showNotification) {
await window.electronAPI.showNotification(options);
}
问题三:通知实例被垃圾回收
现象:通知显示后立即消失或被系统清理。
解决方案:
-
存储通知实例:
const notifications = new Map(); notifications.set(notificationId, notification);
-
防止过早清理:
// 添加超时清理(5分钟后自动清理)
setTimeout(() => {
if (notifications.has(notificationId)) {
notifications.delete(notificationId);
}
}, 5 * 60 * 1000);
问题四:不同平台的通知选项不同
现象:某些通知选项在某些平台上不支持。
解决方案:
-
平台检测:
if (process.platform === 'darwin') {
// macOS 选项
notificationOptions.subtitle = options.subtitle;
notificationOptions.sound = options.sound;
} else {
// Linux/Windows/鸿蒙PC 选项
notificationOptions.icon = options.icon;
notificationOptions.urgency = options.urgency;
}
-
条件添加选项:
// 只在支持的平台上添加选项
if (options.subtitle && process.platform === 'darwin') {
notificationOptions.subtitle = options.subtitle;
}
功能特性
1. 双重保障机制
-
优先使用 HTML5 Notification API:简单、可靠、跨平台兼容
-
降级到 Electron IPC:当 HTML5 API 不可用时,使用 Electron API
2. 平台特定优化
-
macOS:支持
subtitle、sound、urgency选项 -
Windows/Linux/鸿蒙PC:支持
icon、urgency、actions等选项
3. 事件监听
-
点击事件:监听通知点击,可以激活应用窗口
-
关闭事件:监听通知关闭,清理通知实例
-
显示事件:监听通知显示,确认通知已成功显示
-
错误事件:监听通知错误,处理异常情况
4. 通知管理
-
通知ID:为每个通知分配唯一ID
-
实例存储:存储通知实例,防止被垃圾回收
-
自动清理:5分钟后自动清理通知实例
最佳实践
1. 权限处理
async function requestNotificationPermission() {
if ('Notification' in window) {
if (Notification.permission === 'default') {
const permission = await Notification.requestPermission();
return permission === 'granted';
}
return Notification.permission === 'granted';
}
return false;
}
2. 错误处理
try {
const result = await window.electronAPI.showNotification(options);
if (result.success) {
console.log('通知显示成功');
} else {
console.error('通知显示失败:', result.error);
// 显示友好的错误提示
showError('通知发送失败,请检查系统通知设置');
}
} catch (error) {
console.error('通知错误:', error);
// 降级处理
showFallbackNotification();
}
3. 通知内容优化
const notificationOptions = {
title: '简短明确的标题', // 不超过50个字符
body: '清晰的通知内容', // 不超过200个字符
// macOS
subtitle: '副标题(可选)',
// 其他平台
icon: '/path/to/icon.png' // 使用合适的图标
};
4. 通知去重
// 使用 tag 属性避免重复通知
const notification = new Notification('标题', {
body: '内容',
tag: 'unique-notification-tag' // 相同 tag 的通知会被替换
});
5. 通知分组
// 使用 group 属性分组通知(如果支持)
const notificationOptions = {
title: '标题',
body: '内容',
group: 'notification-group-name'
};
完整示例代码
main.js(完整版)
const { app, BrowserWindow, ipcMain, Notification } = require('electron');
const path = require('path');
let mainWindow;
const notifications = new Map();
function createWindow() {
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
preload: path.join(__dirname, 'preload.js'),
},
});
mainWindow.loadFile('index.html');
return mainWindow;
}
// 显示通知
ipcMain.handle('show-notification', async (event, options) => {
if (!Notification.isSupported()) {
return { success: false, error: 'Notifications not supported' };
}
try {
const notificationOptions = {
title: options.title || '通知',
body: options.body || options.message || '',
silent: options.silent || false
};
// 平台特定选项
if (process.platform === 'darwin') {
if (options.subtitle) notificationOptions.subtitle = options.subtitle;
if (options.sound !== false) notificationOptions.sound = options.sound || 'default';
} else {
notificationOptions.icon = options.icon;
notificationOptions.urgency = options.urgency || 'normal';
}
const notification = new Notification(notificationOptions);
const notificationId = options.notificationId || Date.now();
notification.on('click', () => {
BrowserWindow.getAllWindows().forEach(win => {
if (!win.isDestroyed()) {
win.webContents.send('notification-clicked', notificationId);
}
});
if (options.onClick !== false && mainWindow) {
mainWindow.show();
mainWindow.focus();
}
});
notification.on('close', () => {
notifications.delete(notificationId);
});
notification.show();
notifications.set(notificationId, notification);
setTimeout(() => {
if (notifications.has(notificationId)) {
notifications.delete(notificationId);
}
}, 5 * 60 * 1000);
return { success: true, notificationId: notificationId };
} catch (error) {
return { success: false, error: error.message };
}
});
// 关闭通知
ipcMain.on('close-notification', (event, notificationId) => {
const notification = notifications.get(notificationId);
if (notification) {
notification.close();
notifications.delete(notificationId);
}
});
app.whenReady().then(() => {
if (Notification.isSupported()) {
console.log('通知功能已支持');
if (process.platform === 'darwin' && app.dock) {
app.dock.show();
}
}
createWindow();
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
preload.js(完整版)
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', {
showNotification: (options) => {
return ipcRenderer.invoke('show-notification', options);
},
closeNotification: (notificationId) => {
ipcRenderer.send('close-notification', notificationId);
},
onNotificationClick: (callback) => {
ipcRenderer.on('notification-clicked', (event, notificationId) => {
callback(notificationId);
});
},
removeNotificationClickListener: () => {
ipcRenderer.removeAllListeners('notification-clicked');
}
});
总结
实现要点
-
双重保障机制:优先使用 HTML5 API,降级到 Electron API
-
跨平台支持:支持 Windows、macOS、Linux 和鸿蒙PC平台
-
平台优化:针对不同平台使用最佳实践
-
事件监听:完善的错误处理和事件监听
关键优势
-
✅ 鸿蒙PC平台完全兼容:所有代码在鸿蒙PC平台上都可以正常工作
-
✅ HTML5 API 优先:在支持的平台上使用最可靠的方案
-
✅ Electron API 降级:确保在不支持 HTML5 API 的环境下也能工作
-
✅ 平台优化:针对不同平台使用最佳配置
注意事项
-
权限要求:某些平台需要用户授予通知权限
-
平台差异:不同平台的通知样式和行为可能不同
-
错误处理:完善的错误处理确保应用稳定性
-
实例管理:合理管理通知实例,防止内存泄漏
扩展方向
-
通知持久化:保存通知历史记录
-
通知分组:支持通知分组和折叠
-
自定义样式:支持自定义通知样式和图标
-
通知操作:支持更多操作按钮和交互
参考资料
作者:GitCode & 坚果派 Electron 鸿蒙适配团队
更多推荐




所有评论(0)