You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在仅含BrowserWindow的Electron应用中重写通知弹窗?

重写Electron应用中的通知弹窗实现方案

嘿,我看你已经在尝试用IPC拦截Web应用的原生通知了,这个思路完全没问题!不过可以再优化一下拦截逻辑,让它更可靠,同时给你两种不同的自定义通知实现方向,你可以根据需求选:

核心思路

我们要做的是拦截渲染进程里的原生Notification构造函数,把所有通知的内容通过IPC转发到主进程,由主进程来处理显示自定义通知,同时阻止原生通知弹出。这样不管Web应用怎么调用通知,都能被我们接管。


第一步:在Preload脚本中拦截原生通知

把你原来的renderer.js逻辑改成重写Notification,这样能覆盖所有通知创建场景,而不只是监听onshow事件:

// preload.js(通过webPreferences.preload加载)
const { ipcRenderer } = require('electron');

// 先保存原生的Notification,避免完全丢失原有功能
const OriginalNotification = window.Notification;

// 重写Notification构造函数
window.Notification = function(title, options) {
  // 把通知的所有参数发送到主进程
  ipcRenderer.send('custom-notification', {
    title,
    ...options
  });

  // 返回一个模拟的Notification实例,避免Web应用调用show/close时报错
  return {
    show: () => {},
    close: () => {}
  };
};

// 保留权限相关的逻辑,因为有些Web应用会先请求通知权限
Object.defineProperty(window.Notification, 'permission', {
  get: () => OriginalNotification.permission,
  set: (val) => { OriginalNotification.permission = val; }
});

window.Notification.requestPermission = OriginalNotification.requestPermission.bind(OriginalNotification);

第二步:主进程处理通知(两种方案可选)

方案A:用Electron自带的系统级通知

如果只是想替换图标、增加点击交互,用Electron内置的Notification就够了,它会调用系统原生的通知弹窗:

// index.js 主进程代码
const { app, ipcMain, Notification, BrowserWindow } = require('electron');

ipcMain.on('custom-notification', (event, notificationData) => {
  // 构造自定义通知,可以覆盖标题、内容、图标等
  const systemNotification = new Notification({
    title: notificationData.title,
    body: notificationData.body,
    icon: notificationData.icon || `${__dirname}/images/favicon-notification.ico`, // 自定义通知图标
    silent: notificationData.silent || false // 控制是否静音
  });

  // 显示通知
  systemNotification.show();

  // 监听通知点击事件,比如激活主窗口
  systemNotification.on('click', () => {
    const mainWindow = BrowserWindow.getAllWindows()[0];
    if (mainWindow) {
      mainWindow.show();
      mainWindow.focus();
    }
  });
});

方案B:完全自定义样式的弹窗(用BrowserWindow)

如果想要完全控制通知的外观、动画,可以创建一个小型的BrowserWindow作为通知容器,自己写HTML/CSS来定制:

主进程代码

// index.js 主进程代码
const { app, ipcMain, BrowserWindow } = require('electron');
const path = require('path');

ipcMain.on('custom-notification', (event, { title, body }) => {
  // 创建通知窗口,配置成无框、置顶、透明的样式
  const notificationWindow = new BrowserWindow({
    width: 320,
    height: 120,
    frame: false, // 去掉窗口边框
    transparent: true, // 背景透明
    alwaysOnTop: true, // 始终置顶
    skipTaskbar: true, // 不在任务栏显示
    webPreferences: {
      nodeIntegration: false,
      contextIsolation: true,
      preload: path.join(__dirname, 'notification-preload.js') // 用于通知窗口的交互
    }
  });

  // 加载自定义通知的HTML页面
  notificationWindow.loadFile('notification.html');

  // 等页面加载完成后,把通知内容传给它
  notificationWindow.webContents.on('did-finish-load', () => {
    notificationWindow.webContents.send('notification-content', { title, body });
  });

  // 3秒后自动关闭通知窗口,也可以改成点击关闭
  setTimeout(() => {
    if (!notificationWindow.isDestroyed()) {
      notificationWindow.destroy();
    }
  }, 3000);
});

自定义通知的HTML页面(notification.html)

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }
    body {
      background: rgba(30, 30, 30, 0.9);
      color: #fff;
      padding: 15px;
      border-radius: 8px;
      font-family: Arial, sans-serif;
    }
    .notification-title {
      font-size: 16px;
      font-weight: bold;
      margin-bottom: 8px;
    }
    .notification-body {
      font-size: 14px;
      opacity: 0.8;
    }
  </style>
</head>
<body>
  <div class="notification-title" id="title"></div>
  <div class="notification-body" id="body"></div>

  <script>
    const { ipcRenderer } = require('electron');
    // 接收主进程传来的通知内容
    ipcRenderer.on('notification-content', (event, { title, body }) => {
      document.getElementById('title').textContent = title;
      document.getElementById('body').textContent = body;
    });
  </script>
</body>
</html>

一些注意事项

  • 权限兼容:一定要保留原生的requestPermissionpermission属性,不然有些Web应用会因为检测不到通知权限而报错。
  • IPC安全:开启contextIsolation是Electron的最佳实践,preload脚本里的IPC通信是安全的,不要在渲染进程直接暴露ipcRenderer。
  • 内存管理:如果用自定义BrowserWindow做通知,记得及时销毁窗口,避免内存泄漏,比如设置自动关闭或者监听窗口的closed事件。

内容的提问来源于stack exchange,提问作者Dennis Ziolkowski

火山引擎 最新活动