news 2026/8/30 20:56:55

Expo 推送通知实战指南:expo-notifications 全解析与常见坑

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Expo 推送通知实战指南:expo-notifications 全解析与常见坑

Expo 推送通知实战指南:expo-notifications 全解析与常见坑

【免费下载链接】expoAn open-source framework for making universal native apps with React. Expo runs on Android, iOS, and the web.项目地址: https://gitcode.com/GitHub_Trending/ex/expo

在 React Native 里做推送,几乎每个团队都绕不开这几件事:权限弹窗被用户随手点了拒绝、拿到的令牌不知道往哪儿存、Android 和 iOS 行为对不上、应用在前台时通知表现又不一样。这篇文章不追求"从零到精通"的宏大叙事,只解决一个具体问题——如何把一条消息可靠地推到用户设备上,并在用户点击后跳到对应页面。

推送通知的工作链路:令牌、EAS 与前台后台

动手写代码前,先把整条链路看清楚,后面写代码心里才有数。

推送的核心载体是设备令牌(ExpoPushToken)。每台安装了 App 的设备都会向系统申请一个推送令牌:iOS 找 APNs,Android 找 FCM。Expo 的做法是用expo-notifications把这两套体系封装成同一个接口,你拿到的是一串ExponentPushToken[...]格式的统一令牌,格式里还带了平台前缀(如device1://device2://),服务端不需要关心底下走的是哪条通道。

消息的送达由EAS(Expo Application Services)推送服务负责。你的后端只需把令牌和消息内容 POST 给 Expo 推送服务(https://exp.host/--/api/v2/push/send),EAS 会解析令牌前缀,路由到 FCM 或 APNs,最终把通知交给系统展示。也就是说你不需要自建推送服务器,也不用自己管理 Firebase 和 Apple 证书的细节。

至于前后台表现不同,原因在系统层:应用处于后台或关闭状态时,通知直接交给系统渲染,展示什么、响不响铃由系统按令牌绑定的凭据决定;应用在前台时,系统把事件"递"给你的 JS 代码,由setNotificationHandler里的策略决定要不要弹横幅、响铃、改角标。所以推送代码天然是两条路径,这也是后面要注册两种监听器的原因。

把一条用户消息推送到 App 并支持点击跳转

下面用一个具体场景串起完整链路:服务器推送一条新消息,用户点击后进入消息详情。

先装依赖并注册插件

用官方安装命令引入expo-notificationsexpo-constants(后者用来读projectId):

npx expo install expo-notifications expo-constants

然后在app.jsonexpo.plugins数组里加上"expo-notifications",配置插件会替你处理两端原生工程里的通知相关设置,不用手改 AndroidManifest 或 Info.plist。

如何申请权限并拿到推送令牌

权限和令牌是同一件事的两半:没有权限就拿不到令牌。Android 上先建好通知渠道,再检查/请求权限,最后生成令牌:

async function registerForPushNotificationsAsync() { // Android 8.0+ 必须有渠道,否则通知会被静默归到默认渠道 if (Platform.OS === 'android') { await Notifications.setNotificationChannelAsync('default', { name: 'default', importance: Notifications.AndroidImportance.MAX, }); } // 先查状态再弹窗,避免重复打扰用户 const { status } = await Notifications.getPermissionsAsync(); if (status !== 'granted') { await Notifications.requestPermissionsAsync(); } // 令牌绑定 projectId,换账号、改名都不会变 const projectId = Constants.expoConfig?.extra?.eas?.projectId ?? Constants.easConfig?.projectId; return (await Notifications.getExpoPushTokenAsync({ projectId })).data; }

拿到令牌后别只打个日志就丢了——把它随用户账号存到你的后端数据库,这样"给某个用户发消息"才有目标可寻。本地存储只能当缓存,不能作为唯一来源。

发送一条测试通知

开发阶段最简单的验证方式是直接调 Expo 推送接口,把令牌填进to字段:

await fetch('https://exp.host/--/api/v2/push/send', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ to: expoPushToken, sound: 'default', title: '一条新消息', body: '点击查看详情', data: { screen: 'message-detail', id: '42' }, // 点击后要用到的跳转信息 }), });

关键是data字段:它不会展示在通知上,但会原样随通知下发,是你做点击跳转的"暗号"。也可以用 Expo 的推送工具(expo.dev/notifications)网页版发测试,适合没有后端接口的纯调试阶段。

监听前台通知与点击事件

两个监听器各管一段:addNotificationReceivedListener在应用前台收到通知时触发,让你更新 UI 或做静默处理;addNotificationResponseReceivedListener在用户点击通知时触发,是跳转的入口:

useEffect(() => { // 前台收到通知时的策略:弹横幅但不响铃 Notifications.setNotificationHandler({ handleNotification: async () => ({ shouldShowBanner: true, shouldShowList: true, shouldPlaySound: false, shouldSetBadge: true, }), }); const onReceive = Notifications.addNotificationReceivedListener(n => setMessages(prev => [n, ...prev]) ); const onClick = Notifications.addNotificationResponseReceivedListener(r => { const { screen, id } = r.notification.request.content.data; if (screen === 'message-detail') { router.push('/message-detail', { id }); // 用 data 里的信息做深度链接 } }); return () => { onReceive.remove(); onClick.remove(); }; }, []);

点击监听器在应用冷启动时被点开的通知同样会触发,所以在入口组件注册它,跳转链路就完整了。

真实项目里最容易踩的四个坑

权限被拒绝。现象:getPermissionsAsync()返回denied,后续所有令牌逻辑短路。排查:确认自己调的是requestPermissionsAsync()而不是重复弹窗;权限一旦被拒,系统不会再次弹出询问,需要引导用户去系统设置里手动打开(iOS 上还可以用expo-application打开设置页)。

Android 通知渠道差异。现象:通知能收到,但无声无震动,甚至归到"其他"渠道。排查:Android 8.0+ 下通知行为完全由渠道决定,用setNotificationChannelAsync显式建渠道并设置importance;注意渠道属性创建后用户可在系统设置里覆盖,别假设MAX一定响。

模拟器上收不到推送。现象:真机正常,模拟器死活等不到通知。排查:推送需要真实凭据链路,Expo 官方明确不支持在纯 Expo Go 模拟器环境测试——要么用带 Google Play 服务的 Android 模拟器 / Xcode 14+ 的 iOS 模拟器,要么直接用eas build出的开发构建跑真机,这是最省事的路线。

projectId 缺失或错配。现象:调getExpoPushTokenAsync报 "Project ID not found",或令牌发出去石沉大海。排查:projectId是令牌归属项目的 UUID,用expo-constants从配置里读(见上面的注册函数);如果手动配置,确认app.jsonextra.eas.projectId与 EAS 后台一致。

下一步可以走的方向

跑通这条链路后,三个方向值得排进迭代:自定义通知声音(把音频文件放进assets后在 payload 的sound字段引用)、更精细的深度链接(在data里传完整路由参数)、以及 iOS 角标管理(setBadgeCountAsync控制未读数)。

延伸阅读建议直接看仓库里的官方文档:

  • 推送通知总览
  • 推送通知完整配置
  • 接收与处理通知
  • 从服务端发送通知
  • FAQ 与故障排查

仓库里的 apps/notification-tester/ 是一个完整的通知测试工程,从令牌注册、任务注册到通知分类一应俱全,翻它的源码比自己造轮子快得多。

【免费下载链接】expoAn open-source framework for making universal native apps with React. Expo runs on Android, iOS, and the web.项目地址: https://gitcode.com/GitHub_Trending/ex/expo

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/30 20:41:41

78个BT Tracker完整清单:IPv4/IPv6快速提速配置指南

78个BT Tracker完整清单:IPv4/IPv6快速提速配置指南 【免费下载链接】trackerslist Updated list of public BitTorrent trackers 项目地址: https://gitcode.com/GitHub_Trending/tr/trackerslist 当下载速度卡在几十KB/s、又找不到同伴时,问题往…

作者头像 李华
网站建设 2026/8/30 20:38:55

draw.io 桌面版:免费本地绘图与批量导出的桌面工具

draw.io 桌面版:免费本地绘图与批量导出的桌面工具 【免费下载链接】drawio-desktop Official electron build of draw.io 项目地址: https://gitcode.com/GitHub_Trending/dr/drawio-desktop 想把一整文件夹的图批量导出成 PDF,又不想为商业软件…

作者头像 李华
网站建设 2026/8/30 20:37:37

Goose 安装快速上手指南:十分钟搭建本地可用的 AI 开发助手

Goose 安装快速上手指南:十分钟搭建本地可用的 AI 开发助手 【免费下载链接】goose an open source, extensible AI agent that goes beyond code suggestions - install, execute, edit, and test with any LLM 项目地址: https://gitcode.com/GitHub_Trending/g…

作者头像 李华
网站建设 2026/8/30 20:28:50

OpenAI 9个月自研3nm推理芯片Jalapeño,效率与速度双提升

2025 年 AI 芯片领域最值得关注的变量,不是某家芯片巨头又发布了一张新卡,而是 OpenAI 把自己的芯片设计周期压到了 9 个月。这颗代号 “Jalapeo” 的自研 AI 芯片,采用 3nm 工艺,从公开报道看,核心目标是给 GPT 系列模…

作者头像 李华