You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无需FCM与Android Service实现推送通知的可行方案咨询

无需后台服务的推送实现方案(基于你的Express Node.js服务器)

这正是Messenger、Discord、X这类应用在用的无后台推送逻辑,核心是Web Push API——完全不需要你的App/网页保持后台或轮询,靠系统/浏览器的原生推送服务和你的Express服务器配合就能实现低功耗、低流量的推送。

具体实现步骤

1. 生成VAPID密钥对

VAPID是让推送服务识别你服务器身份的凭证,用web-push包直接生成:

npx web-push generate-vapid-keys

保存好输出的公钥和私钥,后面会用到。

2. 改造你的Express服务器

先装依赖:

npm install web-push body-parser

然后加推送相关的代码(直接整合到你现有的Express服务里就行):

const webpush = require('web-push');
const bodyParser = require('body-parser');

// 假设你已经有了express实例app
app.use(bodyParser.json());

// 配置VAPID
const vapidKeys = {
  publicKey: '你刚生成的VAPID公钥',
  privateKey: '你刚生成的VAPID私钥'
};

webpush.setVapidDetails(
  'mailto:your-email@example.com', // 推送服务用来联系你的邮箱,随便填一个就行
  vapidKeys.publicKey,
  vapidKeys.privateKey
);

// 生产环境别用内存存,换成MongoDB/PostgreSQL这类数据库
let userSubscriptions = [];

// 接收前端的订阅请求
app.post('/push/subscribe', (req, res) => {
  const subscription = req.body;
  userSubscriptions.push(subscription);
  res.status(201).send('订阅成功');
});

// 触发推送的接口(你可以在业务逻辑里调用这个,比如有新消息时)
app.post('/push/send', (req, res) => {
  const { title, content } = req.body;
  const payload = JSON.stringify({ title, body: content });

  // 给所有订阅用户发推送
  userSubscriptions.forEach(sub => {
    webpush.sendNotification(sub, payload)
      .catch(err => console.error('推送失败:', err));
  });

  res.status(200).send('通知已触发');
});

3. 前端(网页/移动端WebView)做订阅

不管是网页还是移动端用WebView套的应用,都要做这两步:

第一步:请求用户授权并订阅

if ('serviceWorker' in navigator && 'PushManager' in window) {
  window.addEventListener('load', async () => {
    // 注册Service Worker
    const swReg = await navigator.serviceWorker.register('/sw.js');
    
    // 弹授权框
    const perm = await Notification.requestPermission();
    if (perm !== 'granted') return;

    // 生成订阅对象,发给你的服务器
    const subscription = await swReg.pushManager.subscribe({
      userVisibleOnly: true, // 必须设为true,所有推送都要让用户可见
      applicationServerKey: urlBase64ToUint8Array('你的VAPID公钥')
    });

    await fetch('/push/subscribe', {
      method: 'POST',
      body: JSON.stringify(subscription),
      headers: { 'Content-Type': 'application/json' }
    });
  });
}

// 工具函数:把base64公钥转成Uint8Array
function urlBase64ToUint8Array(base64Str) {
  const padding = '='.repeat((4 - base64Str.length %4) %4);
  const base64 = (base64Str + padding).replace(/-/g, '+').replace(/_/g, '/');
  const raw = window.atob(base64);
  return Uint8Array.from([...raw].map(c => c.charCodeAt(0)));
}

第二步:写Service Worker文件(sw.js)

这个文件是用来接收推送并弹通知的,必须放在网站根目录:

// 收到推送时显示通知
self.addEventListener('push', (e) => {
  const data = e.data?.json() || { title: '新消息', body: '你有一条未读通知' };
  const options = {
    body: data.body,
    icon: '/your-icon.png', // 可选:自定义通知图标
    badge: '/your-badge.png' // 可选:手机状态栏的小图标
  };

  e.waitUntil(self.registration.showNotification(data.title, options));
});

// 用户点击通知时打开应用/页面
self.addEventListener('notificationclick', (e) => {
  e.notification.close();
  e.waitUntil(clients.openWindow('/')); // 换成你要打开的页面
});

为什么这个方案比轮询好?

  • 完全不用后台:推送逻辑由浏览器/系统的原生服务处理,你的App不用保持后台进程
  • 极低功耗流量:只有有消息时才会触发推送,没有轮询的空请求浪费资源
  • 跨平台兼容:支持所有主流浏览器,移动端用WebView也能适配

生产环境注意点

  • 别用内存存订阅信息,换成数据库(MongoDB、Redis都行)
  • 必须用HTTPS:除了localhost开发,正式环境必须部署在HTTPS域名下,否则Web Push API用不了
  • 推送内容要合规:不能发用户不可见的推送,浏览器会直接拦截

内容的提问来源于stack exchange,提问作者Khỉ Em

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 23:41:18