微信小程序 wx.navigateToMiniProgram 使用指南:参数、回传和真机失败原因
微信小程序 wx.navigateToMiniProgram 使用指南:参数、回传和真机失败原因
小程序之间要打开另一个小程序,用 wx.navigateToMiniProgram。登录授权、订单支付、会员互通这类流程都靠它:当前小程序把用户送过去,对方处理完再带数据回来。
接口从基础库 1.3.0 开始可用,支持 Promise。微信 Windows、Mac、鸿蒙客户端都可以调用。它只负责「打开」,不负责「等对方做完」。对方返回的数据要在自己的 App.onShow 里收。
先看清三条平台限制
调用前先记住这三件事,后面大部分失败都能对上。
必须由用户点击触发。 基础库 2.3.0 起,用户没有点过当前页面,就不能自动跳转。写在 onLoad、onShow、定时器里,真机经常失败,errMsg 里会带 can only be invoked by user TAP gesture。
跳转前一定有系统确认弹窗。 同样从 2.3.0 开始,微信会先问用户是否打开另一个小程序。用户点取消,回调是 fail,errMsg 为 navigateToMiniProgram:fail cancel。这是用户主动取消,按失败重试去弹 Toast 会打扰用户。
2020 年 4 月 24 日起不再需要配置跳转名单,也不再限制跳转数量。 旧文里的 app.json → navigateToMiniProgramAppIdList(最多 10 个)已经失效。使用时仍要遵守《微信小程序平台运营规范》,互推、小程序盒子一类行为会被下架。
另外,envVersion 只在当前小程序是开发版或体验版时生效。当前包是正式版时,打开的目标小程序一定是正式版。
参数
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
appId | string | - | 目标小程序 AppID。传了 shortLink 时可以不传 |
path | string | 首页 | 打开的页面路径。? 后面是 query |
extraData | object | - | 交给目标小程序的数据,在对方 App.onLaunch / App.onShow 里取 |
envVersion | string | release | develop 开发版、trial 体验版、release 正式版 |
shortLink | string | - | 小程序链接,基础库 2.18.1。传入后可以不传 appId 和 path |
noRelaunchIfPathUnchanged | boolean | false | 基础库 2.24.0。目标小程序还活着,且本次 path、query 与上次相同,就回到它退后台时的页面,不重新启动 |
success / fail / complete | function | - | success 表示跳转已发起,不表示用户已经从目标小程序返回 |
path 不要写成带前导 / 的绝对路径。官方示例是 page/index/index?id=123。query 会出现在目标页 Page.onLoad(query),也会出现在 App.onLaunch、App.onShow 的 options.query。
extraData 走的是另一条通道,只在 App.onLaunch、App.onShow 的 options.referrerInfo.extraData 里。页面 onLoad 拿不到它。
最小可用写法
按钮点击里直接调。success 只说明用户已确认、目标小程序已打开。
Page({
openPartner() {
wx.navigateToMiniProgram({
appId: 'wx1234567890abcdef',
path: 'pages/auth/index?from=order',
extraData: {
biz: 'pay',
orderId: '202610190001',
},
envVersion: 'release',
success() {
// 已打开目标小程序,业务结果还要等 onShow
},
fail(err) {
const message = err.errMsg || ''
if (message.indexOf('cancel') !== -1) {
return
}
wx.showToast({ title: '暂时无法打开', icon: 'none' })
},
})
},
})
基础库支持 Promise 风格,不传回调时可以直接 await:
async openPartner() {
try {
await wx.navigateToMiniProgram({
appId: 'wx1234567890abcdef',
path: 'pages/auth/index',
})
} catch (err) {
const message = (err && err.errMsg) || ''
if (message.indexOf('cancel') !== -1) return
wx.showToast({ title: '暂时无法打开', icon: 'none' })
}
}
Taro 的封装同名,参数一致:
import Taro from '@tarojs/taro'
function openPartner() {
return Taro.navigateToMiniProgram({
appId: 'wx1234567890abcdef',
path: 'pages/auth/index',
extraData: { biz: 'pay' },
envVersion: 'release',
})
}
声明式写法用 navigator,底层同样是这个接口,确认弹窗和点击限制一样存在:
<navigator
target="miniProgram"
open-type="navigate"
app-id="wx1234567890abcdef"
path="pages/auth/index"
version="release"
bindsuccess="onJumpSuccess"
bindfail="onJumpFail"
>
打开对方小程序
</navigator>
extra-data 在组件上是对象。复杂对象用 API 更稳妥,组件属性容易被当成字符串。
数据怎么去、怎么回
一次完整跳转有两个方向。
去程。 当前小程序调用 wx.navigateToMiniProgram。目标小程序冷启动时场景值是 1037(小程序打开小程序)。path 上的 query 进页面参数,extraData 进 referrerInfo。
// 目标小程序 app.js
App({
onShow(options) {
if (options.scene !== 1037) return
const fromAppId = options.referrerInfo && options.referrerInfo.appId
const extraData = (options.referrerInfo && options.referrerInfo.extraData) || {}
// extraData.orderId、extraData.biz
},
})
部分基础库在没有来源时 referrerInfo 是 undefined,读取前先判断。
回程。 目标小程序只能在「自己是被别的小程序打开的」前提下调用 wx.navigateBackMiniProgram。调用方回到前台时场景值是 1038(从另一个小程序返回),数据在同一次 App.onShow 的 referrerInfo.extraData。
// 目标小程序,授权完成后
wx.navigateBackMiniProgram({
extraData: {
code: 'auth-code-from-server',
},
})
// 调用方 app.js
App({
onShow(options) {
if (options.scene !== 1038) return
const extraData = options.referrerInfo && options.referrerInfo.extraData
if (!extraData || !extraData.code) return
// 用 code 向自己的服务端换登录态,不要把 code 当登录态直接存
},
})
extraData 只在场景值 1037 和 1038 下有效。来源小程序的 AppID 在 referrerInfo.appId,收到回包后先核对是不是预期的那个小程序。
两条传参通道的差别:
path query | extraData | |
|---|---|---|
| 适合放 | 页面定位:打开哪一页、简单字符串参数 | 业务上下文:订单号、授权范围、一次性票据 |
| 谁能读到 | Page.onLoad、App.onShow 的 query | 只有 App.onLaunch / App.onShow 的 referrerInfo |
| 形态 | URL 字符串 | 对象 |
| 返回时 | 用不上 | navigateBackMiniProgram 的 extraData 原路带回 |
敏感票据放 extraData,同时在服务端把票据和跳转前生成的 state 绑在一起,并设置很短的有效期。只靠前端回传的 code 不能证明这次回调就是刚才那次点击。
回传会被重复触发
success 不是业务完成。用户从目标小程序回来、切后台再切回、甚至进程被回收后用上次启动参数重新打开,App.onShow 都可能再次带上同一次 scene = 1038。
安卓还有一个平台行为:按 Home 回到桌面再进小程序时,系统拿不到新的场景值,会保留上一次的场景值。于是旧的 1038 和旧的 extraData 会再走一遍。
处理方式:
- 只在
scene === 1038时解析回包。 - 用
referrerInfo.appId校验来源。 - 对
code做一次性消费。内存里记已处理的 code,拦截同一次前台里的并发onShow。 - 内存去重挡不住进程重启。跳转前把
state写入本地存储,回调里读出后立刻删掉。同一个 code 再来时,本地已经没有state,直接忽略。 - 给
state一个短 TTL(例如 5 分钟)。超时的回包丢掉,提示用户重新发起。
const STORAGE_KEY = 'mp_jump_state'
const processedCodes = new Set()
function saveState(state) {
wx.setStorageSync(STORAGE_KEY, { state, timestamp: Date.now() })
}
function consumeState() {
const stored = wx.getStorageSync(STORAGE_KEY)
wx.removeStorageSync(STORAGE_KEY)
if (!stored || !stored.state) return ''
if (Date.now() - stored.timestamp > 5 * 60 * 1000) return ''
return stored.state
}
App({
onShow(options) {
if (options.scene !== 1038) return
const extra = (options.referrerInfo && options.referrerInfo.extraData) || {}
if (!extra.code || processedCodes.has(extra.code)) return
processedCodes.add(extra.code)
const state = consumeState()
if (!state) return
// exchange(extra.code, state)
},
})
页面级 onShow 适合刷新 UI。登录态、票据兑换放在 App.onShow(或 wx.onAppShow),避免页面还没创建时把回包丢掉。热启动时用 wx.getEnterOptionsSync() 读最近一次进入参数;wx.getLaunchOptionsSync() 一直是冷启动那次,不适合拿来读「刚刚返回」的数据。
开发版、体验版和开发者工具
联调目标小程序的开发版时:
- 当前小程序本身也必须是开发版或体验版,
envVersion: 'develop'才会生效。 - 扫码的微信号要在目标小程序的开发者名单里,否则开发版打不开。
- 开发者工具里调用这个 API 不会真的打开另一个小程序,工具只校验这次调用是否成功。接收参数要在工具的「模拟跳转」里调试,完整回跳以真机为准。
shortLink 从基础库 2.18.1 可用。链接来自目标小程序菜单里的「复制链接」,或服务端 generateShortLink。传入后可以不传 appId 和 path:
wx.navigateToMiniProgram({
shortLink: '#小程序://示例/页面/xxxxxxxx',
})
noRelaunchIfPathUnchanged 适合重复打开同一个页面、希望保留目标小程序现场的场景。它生效要同时满足:目标小程序进程还在,并且本次 path、query 与上次启动相同。不满足时仍会重新启动。
真机上常见的 fail
| 现象 | 原因 | 处理 |
|---|---|---|
fail cancel | 用户在系统弹窗点了取消 | 静默结束,不要当成系统错误重试 |
can only be invoked by user TAP gesture | 不在点击行为里调用 | 放到按钮 bindtap 里,点击后尽快调用 |
点击后先 await 一个慢接口再跳转 | 点击上下文已经丢失 | 跳转所需参数在点击前准备好 |
| 开发者工具显示成功,真机没打开 | 工具只做校验 | 用真机预览 |
正式版包里 envVersion: 'develop' 仍打开正式版 | 正式版忽略该参数 | 用开发版或体验版包联调 |
返回后 onShow 没有 extraData | 看错了生命周期,或目标小程序没有调用 navigateBackMiniProgram | 在 App.onShow 看 scene === 1038 |
| 同一次授权回调走了两遍 | 1038 被再次带上 | 按 code 去重,并一次性消费 state |
| 插件页里调用失败 | 插件页与宿主小程序不能互相调用该接口 | 在对应一方的页面里调用;插件侧需要基础库 ≥ 2.18.1 |
多端框架(把小程序编译成 App)不支持 wx.navigateToMiniProgram,对应能力是 wx.miniapp.launchMiniProgram,参数是小程序原始 ID userName,和这里的 appId 不是同一个字段。
朋友圈快照(browseOnly)、视频号原生功能化场景、聊天工具场景里,这个接口会被禁用。半屏打开用 wx.openEmbeddedMiniProgram,限制与全屏跳转不同,不要混用。
一个可落地的登录跳转顺序
- 用户点击「使用 xx 账号登录」。
- 点击前或点击同步阶段向自己的服务端要一个短期
state,立刻写入本地存储。 - 调用
wx.navigateToMiniProgram,extraData里带上授权范围和业务标识。 - 用户在系统弹窗确认后进入目标小程序,对方
onShow读到场景值 1037。 - 对方完成授权,调用
wx.navigateBackMiniProgram,extraData带回code。 - 自己的
App.onShow见到场景值 1038,校验appId,消费state,用code + state向服务端换会话。 - 用户取消弹窗时什么都不做。
success 里不要开始轮询登录结果。登录结果只来自 1038 的那一次回包,以及随后的服务端兑换。
更多推荐




所有评论(0)