如何为应用添加通知:应用内、邮件和推送
要为 Web 应用添加通知,可以先从应用内通知开始:在数据库中建一张通知表,再用一个铃铛图标显示未读项目。对于用户离开时需要知道的事情,可以使用邮件服务提供商添加邮件通知;只有在及时提醒确实重要时,才添加 Web 推送,因为它需要 Service Worker、用户授权,并且在 iPhone 上还需要把应用保存到主屏幕。无论使用哪些渠道,都应在服务端的一个地方创建所有通知,并让用户自己选择接收哪些通知。
本指南对比了各种渠道,展示了一个简单的架构,并提供了可在 AI 应用构建工具中使用的提示词。
主要渠道对比
| 渠道 | 何时触达用户 | 设置难度 | 运行成本 | 最适合 |
|---|---|---|---|---|
| 应用内(铃铛、弹出提示) | 用户正在使用你的应用时 | 低 | 除数据库外没有其他成本 | 活动动态、提及、状态变化 |
| 邮件 | 用户查看邮箱的任何时候 | 中等:需要服务商和域名设置 | 按邮件数量由服务商收费 | 收据、摘要,以及任何重要内容 |
| Web 推送 | 浏览器或已安装的 Web 应用,即使关闭时也可以 | 中等到高 | 通常免费发送 | 时间敏感的提醒 |
| 短信 | 几乎总能送达 | 中等;需遵守同意规则 | 按条收费 | 预约提醒、紧急提醒 |
| 聊天工具(Slack、Teams) | 用户在工作时 | 低到中等 | 通常免费 | 内部工具和团队提醒 |
大多数应用需要“应用内 + 邮件”的组合。只有当时机真的重要时,例如预约提醒或订单已准备好,才值得添加推送和短信。
通知系统如何组合在一起
保持简单,并且集中管理:
- **事件发生。**有人发表了评论、支付失败、预约在明天。
- 服务端调用一个函数,例如
notify(user, type, data)。 - 该函数检查用户的偏好设置,并在
notifications表中写入一行记录(用于铃铛)。 - 然后发送到用户为该类型开启的其他渠道:通过邮件服务商发送邮件,通过 Push API 发送推送。
- 前端显示未读通知,并在用户打开时标记为已读。
统一的入口意味着你可以添加渠道、修改文案或遵守偏好设置,而无需在代码库中到处查找。
需要存储什么
通知表通常需要:用户 ID、类型(例如 comment_reply)、简短的标题和正文、指向相关页面的链接、创建时间和已读时间。另外还有一张偏好设置表,用于记录每位用户针对每种类型希望使用哪些渠道。
显示新通知
- **在页面加载时获取,并每 30–60 秒轮询一次。**简单且可靠。
- **使用 WebSockets 或服务器发送事件实现实时更新。**体验更即时,但会增加更多组成部分。除非即时更新确实重要,否则先从轮询开始。
邮件通知
邮件需要发送服务商和已验证的域名;具体细节见如何从你的应用发送邮件。针对通知本身:
- 从服务端发送,绝不要在浏览器中使用服务商密钥。
- **合并频繁发生的事件。**一封邮件写“3 条新评论”,胜过发送三封邮件。
- 为低优先级的动态提供每日或每周摘要。
- **在非必要邮件中加入退订或偏好设置链接。**营销邮件的规则因国家而异,请确认适用于你的规则。
Web 推送通知
Web 推送让你的网站能够发送类似应用通知的提醒。根据 MDN 的 Push API 文档,其工作方式如下:
- 你的网站注册一个 Service Worker,这是浏览器可以在后台运行的脚本。
- 用户同意后,Service Worker 通过
PushManager.subscribe()进行订阅。浏览器会返回一个订阅信息,其中包含端点和加密密钥。 - 你的服务器保存该订阅信息;当事件发生时,向该端点发送消息,通常使用 VAPID 密钥进行签名(VAPID 是用于标识你服务器的密钥对)。
- 即使你的页面没有打开,浏览器也会启动你的 Service Worker,由它显示通知。
MDN 还指出,端点应当保密,因为知道该端点就足以向对应用户发送消息。
iPhone 和 iPad
WebKit 在 iOS 和 iPadOS 16.4 中为 Web 应用加入了 Web Push,其公告中提出两个条件:网站必须添加到主屏幕,并且权限必须响应用户操作来请求,例如点击“订阅”按钮。因此在 iPhone 上,推送只能触达已安装你 Web 应用的用户。关于安装 Web 应用意味着什么,请参见原生应用与渐进式 Web 应用。
恰当地请求权限
不要在首次访问时就请求。先说明好处(“在课程开始前一小时获得提醒”),然后在用户点击按钮时再显示浏览器的权限提示。如果有人拒绝了,浏览器可能不再允许你再次请求,因此过早弹出提示可能会永久失去这位用户。
定时通知需要触发器
“提前 24 小时提醒”或“每周一发送周报”需要有东西按计划运行。这属于后台任务问题,参见如何运行后台任务。一种常见做法:外部调度器每隔几分钟调用一个受保护的端点,由该端点发送所有到期的通知。
给 AI 构建工具的提示词
先从应用内通知开始:
添加应用内通知。创建一张 notifications 表(user、type、title、body、link、created_at、read_at)。添加一个服务端函数 notify(userId, type, data),所有功能都调用它。在页头显示一个带未读数量的铃铛图标,下拉列表显示最新的 20 条,并提供“全部标为已读”按钮。每 60 秒轮询一次新通知。
然后是偏好设置和邮件:
添加通知设置:针对每种类型(评论回复、提及、账单),让用户选择应用内、邮件、两者都要或都不要。notify() 必须遵守这些设置。使用 [PROVIDER] 发送邮件,并且只在服务端使用密钥 [ENV_NAME]。将评论邮件分组,确保同一用户每 15 分钟最多收到一封邮件。
如果需要推送,再添加:
添加可选的 Web 推送。注册一个 Service Worker,生成 VAPID 密钥,并将私钥保存为机密。在设置中添加一个“启用通知”按钮,仅在点击时才请求权限。为每位用户、每台设备保存一条订阅信息,当用户已选择接收时,由 notify() 发送推送,并删除推送服务报告为已过期的订阅。
常见错误
- **通知散落在代码各处。**每个功能都以不同方式单独发送邮件,偏好设置也被忽略。应只使用一个
notify()函数。 - **在页面加载时请求推送权限。**大多数人会拒绝,而你可能再也没有第二次机会。
- **没有偏好设置。**无法关闭嘈杂提醒的用户,要么把所有通知都关掉,要么直接离开。
- **在浏览器中发送。**邮件和推送的密钥应只保存在服务端。
- **不清理失效的推送订阅。**过期的订阅会不断堆积,浪费发送次数。
- **通知文本中包含敏感信息。**锁屏会显示这些内容。“你有一条新消息”比消息本身更安全。
- **没有发送记录。**当用户说“我没收到邮件”时,带有服务商消息 ID 的日志能帮你查明原因。
检查清单
- 所有地方都使用同一个服务端
notify()函数 - 通知表包含已读状态,并有带未读数量的铃铛
- 按类型设置渠道偏好,并由
notify()遵守 - 邮件从服务端发送,使用已验证的域名;对频繁事件进行摘要或合并
- 只在用户操作之后请求推送权限,并附上说明
- 已在桌面端和已添加到主屏幕的 iPhone 上测试推送
- 已移除过期的推送订阅
- 推送或邮件预览中不包含敏感内容
- 定时提醒有可靠的触发器
Mythex 中的通知
Mythex 不为你构建的应用提供内置的邮件或推送服务,因此你需要使用自己的邮件服务商,并生成自己的推送密钥,将其保存为项目机密。应用内部分只需要项目的数据库,你在需要时请求即可添加。使用上面类似的提示词进行构建,在预览中测试铃铛和邮件,并在已发布的 URL 上测试推送,因为 Service Worker 和推送需要真实的 HTTPS 地址。文档中的从你的应用发送邮件配方提供了入门提示词。Mythex 没有记录定时任务,因此请按照如何运行后台任务中的说明,使用外部触发器来规划定时提醒。
常见问题
为 Web 应用添加哪种通知最简单?
应用内通知:在数据库中建一张通知表,再用一个铃铛图标显示未读通知。它不需要任何第三方服务。下一步是邮件,需要使用邮件服务提供商。
Web 应用能向 iPhone 发送推送通知吗?
可以,但有限制。WebKit 在 iOS 和 iPadOS 16.4 中为主屏幕上的 Web 应用加入了 Web Push。因此用户必须先把你的 Web 应用添加到主屏幕,并且权限请求必须在用户点击之后发起,例如点击“订阅”按钮时。
网站关闭时推送通知还能工作吗?
可以。Push API 会把消息投递给网站的 Service Worker,消息到达时浏览器会启动它,因此页面不必保持打开。但用户必须先授予权限。
如何避免通知打扰用户?
只通知用户真正关心的事情,把密集的通知合并为一条消息,让用户在设置中按类型选择接收渠道,并且只有在用户看到推送的价值之后,才请求推送权限。