likes
comments
collection
share

🚀 实现微信小程序web-view与嵌入网页的双向通信

作者站长头像
站长
· 阅读数 15

大家好,我是疯狂的小波

小程序的 web-view,并不支持小程序向嵌入网页的主动通信,比如小程序调用嵌入网页内部的方法、让嵌入网页刷新等。

这篇文章主要是向大家介绍,怎么通过我们自己的方式,实现小程序与web-view嵌入网页的主动通信;以及网页怎么向小程序发送消息。

背景

最近在开发小程序的过程中,有个需求。在小程序的A页面,通过web-view嵌入一个H5H5中有个任务组件,任务组件有完成状态;这里有2个要求:

  1. 如果任务是未完成状态,点击去完成按钮跳转到小程序原生B页面,做指定操作后,就视为完成任务,再返回到A页面,此时H5需要重新请求接口,获取最新的任务状态是否完成。
  2. A页面使用小程序的分享时,分享自定义内容需要使用H5页面接口返回数据

流程大概是这个样子:

🚀 实现微信小程序web-view与嵌入网页的双向通信

我们先来看第1点要求:返回A页面时,H5重新请求接口。由于H5本身并没有页面显示的这种监听方法,那我们就只能通过小程序来通知H5更新。比如:小程序A页面onShow时,调用H5的更新方法。这也是我们要实现这个需求的实现方案:小程序主动更新H5

小程序主动更新H5

查看官方文档,我们可以发现:小程序向H5的通信,只支持嵌入网页链接设置、获取H5发送的消息、H5加载成功/失败的监听;并没有提供小程序直接调用H5内部方法等直接通信的api。

所以我们这里想要更新,只能利用嵌入网页链接的设置这一特性。

链接地址更新及问题

比如要更新页面时,更新我们嵌入的H5地址,如:在地址后添加时间戳来达到页面刷新的效果。

// 小程序A页面
onShow() {
  const t = new Date().getTime();
  this.setData({
    viewUrl: `https://www.baidu.com?t=${t}`
  });
}

可以发现,此时页面会正常刷新。但是这种更新有2个问题:

  1. 会产生一条新的浏览历史记录:此时小程序返回上一页时,返回的是地址更新前的上一个baidu页面
  2. 传统的网页可以正常刷新,但是嵌入的网页如果是单页应用则不会更新

我们可以思考下,为什么会出现这2个问题,以及怎么解决?

这是因为修改web-viewsrc属性,相当于在嵌入的网页中做了一次跳转,跳转地址就是我们修改后地址

所以第一个问题,会产生一条新的浏览历史记录,就很好理解了。

第二个问题,在单页应用中(如Vue),这种跳转相同路由,不同参数的场景,vue-router 发现这是同一个组件,会复用这个组件,所以不会进行重新渲染。

单页应用

那我们拿嵌入的页面是Vue来举例,我们现在的问题就变成了:

  • 相同路由更新参数时,如何实现页面刷新?
  • 地址变更后,会产生新的浏览记录导致返回时没有返回目标页,怎么处理?

相同路由的跳转更新,我们可以使用watch监听$route的变更,再执行我们想要的更新操作。产生新的历史记录,我们可以在路由变更时手动回退,不影响返回操作,需要注意的是手动回退时也会触发路由变更,但是不需要执行更新。

相同路由的跳转更新,除了监听路由,也有其他的方案。可以参考这篇文章的第2条

完整代码如下:

// web-view嵌入的H5页面(Vue)
watch: {
  $route: function() {
    // 如果是手动返回变更hash,不触发重新获取页面数据
    if (this.isBackChange) {
      this.isBackChange = false
      return
    }
    // 执行我们想要的操作,如:调用接口重新获取页面数据
    this.initData()
    // 执行返回操作,小程序webview变更参数后
    // 会使webview历史栈+1。需要手动清除该记录
    setTimeout(() => {
      // 标识是否手动返回,不触发接口请求
      this.isBackChange = true
      this.$router.go(-1)
    })
  }
}

通过上面的方法,我们就能够实现在小程序中更新地址,主动更新H5的这一效果。并且不会产生额外的历史记录,小程序的页面栈也能够正常返回。

传统网页

上面我们通过baidu的例子,通过更改参数,实现了页面更新,但是这种更新是整体页面的刷新,有时并不能满足我们的需求,并且体验也不是很好。那传统网页下,我们怎么实现单页应用这种无感更新呢?

这里我们首先可以思考下,上面Vue的实现原理是什么?

其实就是监听路由的变化,然后执行更新。所以我们可以先简单看看,Vue实现监听$route的原理。

vue-router 有2种路由模式。

hash: url中带 ##后面的值通常就是我们常说的hash值,而hash值只是客户端的一种状态,向服务器发送请求时,hash部分并不会被发送。hash变更时,再通过 window.onhashchange 监听hash的变化,根据不同的hash值展示不同的内容(路由),或者hash变化时,执行变化回调(路由监听)。

history: url中不带 #,利用 History API来实现URL的变化,操作浏览器的历史记录,通过 history.pushState新增一条记录,或者使用history.replaceState替换当前的历史记录。执行该API后,url地址会立即替换但是不会刷新页面。histoty模式使用onpopstate监听浏览记录的回退。

而我们在小程序中改变web-view地址,就可以使用更改hash值的方式。代码如下:

// 小程序A页面
onShow() {
  const t = new Date().getTime();
  this.setData({
    viewUrl: `https://www.baidu.com#${t}`
  });
}
// web-view嵌入的H5页面(传统网页)
let isBackChange = false;
window.addEventListener('hashchange', data => {
    // 如果是手动返回变更hash,不触发重新获取页面数据
    if (isBackChange) {
      isBackChange = false
      return
    }
    // 执行我们想要的操作,如:调用接口重新获取页面数据
    initData()
    // 执行返回操作,小程序webview变更参数后
    // 会使webview历史栈+1。需要手动清除该记录
    setTimeout(() => {
      // 标识是否手动返回,不触发接口请求
      isBackChange = true
      window.history.go(-1)
    })
})

可以看到,这里除了监听的方式不一样,整体的处理逻辑与效果是一致的。

小结

到这里,我们就实现了小程序与 web-view 嵌入网页的主动通信。在监听路由变化的时候,除了可以做页面更新,还可以做其他更多的事情。比如根据不同的路由参数变化,执行不同的操作等,完全可以自定义执行事件,实现主动通信。

小程序使用H5数据进行分享

接下里,我们来看看第二个需求:小程序分享时,要使用嵌入的网页数据

要实现这个效果,就需要网页主动向小程序发送消息。这个比较简单,直接上代码。

// web-view嵌入页面
var wx = require('weixin-js-sdk')

// 接口获取数据 ...
const data = {
  shareDesc,
  shareImage
}
// 向小程序发送消息,传递接口获取的分享标题和图片
wx.miniProgram.postMessage({ data })
// 小程序A页面
<web-view src="{{viewUrl}}" bindmessage="bindPostMessage"></web-view>


// 页面内方法
// 监听h5发送的postMessage消息
bindPostMessage(e) {
    // 发起分享时,获取h5发送的分享内容
    // 这里接收到的数据是一个数组,所以如果有多个数据发送,也可以添加一个type区分
    const shareObj = e.detail.data[0]
    this.shareObj = {
      title: shareObj.shareDesc,
      imageUrl: shareObj.shareImage
    };
}

// 分享页面,获取h5传递过来的自定义分享参数
onShareAppMessage() {
    return this.shareObj;
}

这样就可以轻松实现分享 H5 的数据了。

需要注意的是,小程序只有在特定的场景才会触发接收消息的事件(小程序后退、组件销毁、分享),也就是 bindPostMessage 事件的触发,并不是在 h5 调用 postMessage 时触发,而是在小程序后退、组件销毁、分享时。上面的例子中,可以理解为,小程序发起分享时,先执行了 bindPostMessage 函数,再执行 onShareAppMessage

总结

上面我们介绍了小程序与 web-view 嵌入 H5 的双向通信。

小程序与 H5 的主动通信,可以通过变更 web-viewsrc 地址,然后在 H5 中监听地址的变更,重新请求接口获取最新数据,同时需要处理浏览历史记录新增的问题。在单页应用和传统网页中处理方式有点不一样,但是原理是差不多的。

H5 也可以主动向小程序发送消息,但是小程序只有在特定的场景才会触发接收消息的事件(小程序后退、组件销毁、分享),接收到的数据是多次发送的数据组成的数组。

推荐阅读

转载自:https://juejin.cn/post/7137851280902324232
评论
请登录