H5与小程序通信

uni-app 新手必读:5 分钟搞懂 H5 与微信小程序的"破冰对话"

一句话拿走:H5 嵌进小程序 web-view 后,想传数据、想跳转页面,靠的不是普通 JS,而是微信给的一根"桥"------这篇文章把这根桥的用法和最容易踩的 2 个坑一次讲清。


一、先说一个你可能正在经历的痛

你用 uni-app 辛辛苦苦写了一个 H5 页面,老板来了一句:"这东西挺好,塞进咱们小程序里吧。"

你心想这还不简单?小程序不是有个 <web-view> 组件么,把链接往里一填,完事。

结果一跑起来傻眼了:H5 里想告诉小程序"用户下单了",小程序那边纹丝不动;H5 里想跳到小程序的某个原生页面,也跳不过去。两边就像隔着一道玻璃墙,看得见,摸不着。

这不是你代码写错了,而是你还没搭"桥"。


二、为什么必须搭桥?

<web-view> 本质是一个浏览器容器 。你的 H5 页面跑在这个容器里,而小程序原生的页面跑在容器外面。它们之间是隔离的,普通的 localStoragewindow 变量,甚至普通的事件,都穿不过那道墙。

要想让墙里墙外说上话,就得用微信官方提供的一套通信机制。你可以把它理解成:墙上有个对讲机,H5 按住说话,小程序松开收听。


三、这根"桥"长什么样?

先理清两个东西,新手特别容易搞混:

  • 微信官方提供的 :微信在 <web-view> 里给 H5 开放了一套通信接口,叫 wx.miniProgram.* (比如 wx.miniProgram.postMessagewx.miniProgram.navigateTo)。它属于微信 JS-SDK------也就是我们项目里 jweixin-1.6.0.js 那份,从 res.wx.qq.com 加载。这是"微信原生"的接口。
  • DCloud(uni-app 官方)提供的 :叫 uni.webview.js 。它是一层跨平台封装 ------你只管写统一的 uni.* API,它会自动检测当前平台,把调用转发到对应原生桥:在微信里转给 wx.miniProgram.*,在 App 里转给 plus.webview.*,在百度/字节小程序里各自转给它们自己的桥。

对 uni-app 开发者来说,直接用 uni.webview.js 最省事:一份代码,多端通用。所以本文示例都用 uni.* 来写(记住:这里的 uni 来自 DCloud 的桥接 SDK,不是微信的,也不是 uni-app 框架运行时的那个 uni)。

它给我们几个关键能力(下面统一用 webView 表示你保存的桥接引用 window.__uniWebView,别直接写全局 uni,原因见后面的坑 2):

  • webView.getEnv(callback):判断当前到底运行在什么环境(微信小程序?App?还是普通浏览器?)
  • webView.postMessage({ data }):H5 向小程序"喊话",把数据传过去
  • webView.navigateTo / navigateBack / switchTab / reLaunch / redirectTo 等:确实能指挥小程序做页面跳转 。在微信端,这些调用最终等价于微信官方为 web-view 内 H5 开放的 wx.miniProgram.navigateTo 等接口(微信文档明确支持)。但要注意两点:
    1. url 必须是小程序原生页面路径 (如 /pages/detail/detail),且要在宿主小程序的 pages.json 里注册过,否则跳转会失败;
    2. 一旦跳转,当前 H5 页面会被原生页面替换掉------你是从 web-view "回到/切到"小程序页面,而不是在 H5 内部跳。

而小程序那头,只要在 <web-view> 上挂一个 bindmessage 监听器,就能收到 H5 喊的话:

html 复制代码
<web-view src="https://你的H5地址.com/" bindmessage="onWebviewMessage" />
js 复制代码
// 小程序页面的 js
Page({
  onWebviewMessage(e) {
    // e.detail = { data },data 是一个【数组】
    // 数组里的每一个元素,都是 H5 某一次 postMessage({ data: X }) 里传的那个 X
    // 注意:是"多次 postMessage 的参数"累积成的数组,不是单个对象
    console.log('收到 H5 的消息数组:', e.detail.data)
    e.detail.data.forEach((msg, i) => {
      console.log(`第 ${i + 1} 条消息:`, msg)
    })
  }
})

H5 那一头,发消息就一句话:

js 复制代码
webView.postMessage({
  data: { action: 'userMessage', content: '你好,小程序' }
})

看起来是不是很轻松?别急,新手 99% 会卡在下面两个坑。


四、新手最容易踩的 2 个坑

坑 1:postMessage 发了,小程序却"听不见"

很多新手写完上面的代码,兴冲冲测试,发现小程序端的 onWebviewMessage 根本不触发。

原因很反直觉:postMessage 的消息不是实时送达的。 微信规定,H5 发的数据,只有在下面这些特定时机,bindmessage 回调才会被触发、小程序才能收到:

  1. 小程序后退的时候
  2. 组件被销毁的时候
  3. 用户点击分享的时候
  4. 用户点击复制链接 的时候(基础库 2.31.1 起支持)

所以如果你只是发个消息、页面还停在原地,小程序当然收不到。

另外要强调回调参数的结构:e.detail = { data },其中 data 是一个数组 ,数组里的每个元素对应 H5 的每一次 postMessage({ data: X }) 传进去的那个 X。也就是说,从"上一次回调"到"这一次回调"之间,你调了几次 postMessage,数组里就有几个元素。经常有新手直接 e.detail.data.content 去取值,结果拿到 undefined------因为 data 是数组,要 e.detail.data[0].content 或遍历才对。

破解办法 :发完消息后,紧跟一个 navigateBack,让页面后退,消息就会"顺手"被带过去:

js 复制代码
webView.postMessage({ data: { action: 'userMessage', content: this.message } })
webView.navigateBack({ delta: 1 }) // 让小程序即时收到

坑 2:uni 被"掉包"了

这个坑特别阴。我们项目 index.html 里先加载了 uni.webview.js,它会往 window.uni 上挂一个"桥接版 uni"(来自 DCloud)。可随后 uni-app 框架跑起来,又会在 window.uni 上挂它自己的运行时对象,于是桥接版 uni 被框架版 uni 覆盖了

结果你调用 webView.postMessage(你以为调的是桥接版),实际调的却是 uni-app 框架的版本------它并不负责 web-view 通信,自然就失灵了。

破解办法 :在入口 index.html 里,趁着 uni.webview.js 刚加载完、还没被框架覆盖,先把桥接版真身存一份:

html 复制代码
<script src="/static/uni.webview.1.5.8.js"></script>
<script>
  // 抢先保存 web-view SDK 的引用,防止被 uni-app 框架的 uni 覆盖
  window.__uniWebView = window.uni;
</script>

之后 H5 页面里统一用这份"备份"来通信:

js 复制代码
const webView = window.__uniWebView || {}
webView.getEnv((res) => { /* 判断环境 */ })
webView.postMessage({ data: { action: 'userMessage', content: 'hello' } })

记住这句话就够了:在 uni-app 项目里玩 web-view 通信,永远用你自己保存的那份 uni,别用全局的。


五、一个开箱即用的"调试桥接页"

为了少走弯路,我做了一个最小可运行的调试页(项目名 h5-debug-bridge),专门用来联调 H5 和小程序的通信。它干了几件贴心的事:

  1. 自动显示当前环境:打开页面顶部就告诉你现在是在"微信小程序""App"还是"普通浏览器",不用自己打 log 猜。
  2. 一键测试发消息 :输入框填内容,点按钮就用 postMessage 发过去(并自动后退触发接收)。
  3. 页面跳转全家桶 :把 navigateTo / navigateBack / switchTab / reLaunch / redirectTo 都做成按钮,挨个试跳转行为。
  4. 内置 VConsole:真机扫码就能看 console 日志,不用连电脑调试。

对初学者来说,把这个项目跑起来、配合一个小程序壳子,几分钟就能把整套通信逻辑摸透。


六、上手三步走(照抄版)

第一步:小程序端放一个 web-view 容器

html 复制代码
<web-view src="https://你的H5域名.com/" bindmessage="onWebviewMessage" />

第二步:H5 端引入 uni.webview.js,并抢先保存引用

index.html 里加上面"坑 2"那段保存代码,页面里统一用 window.__uniWebView

第三步:传数据 + 接收数据

  • H5 发:webView.postMessage({ data: {...} }),需要即时送达就紧跟 navigateBack
  • 小程序收:在 bindmessage 对应的 onWebviewMessage 里读 e.detail.data

七、写在最后

H5 与微信小程序的通信,核心就三件事:引入 JSSDK、用 postMessage 传话、在 bindmessage 里收话。剩下的,基本都是围绕上面那两个坑打转。

把"提前保存 uni 引用"和"发完消息要后退"这两点刻进肌肉记忆,你就能甩开 90% 的新手。

文中提到的"调试桥接页"完整可运行源码已开源,github.com/NightCoderH...


相关推荐
hunterandroid1 小时前
Fragment 事务与状态丢失:从崩溃现场到稳定治理
前端
不一样的少年_1 小时前
原来 AI Agent 的核心循环这么简单:手搓一个 Agent Loop
前端·后端·agent
爱勇宝2 小时前
你以为自己性格不好,其实只是被环境反复训练
前端·后端·程序员
phltxy2 小时前
LangChain_v1_Agent快速开发和更新说明
前端·javascript·langchain
Cobyte2 小时前
通过 Vite 运行手写的模板编译器
前端·javascript·vue.js
Hyyy2 小时前
Agent 工作流
前端
Bigger2 小时前
🔥每天最难的问题不是做饭,而是今天到底吃什么——我做了「烟火食间」
前端·人工智能·agent
Hyyy2 小时前
Electron多进程
前端
swipe2 小时前
08|(前端转全栈)一个商品详情接口背后的完整链路:HTTP、Redis、MySQL 与 JSON
前端·后端·全栈