热门标签 | HotTags
当前位置:  开发笔记 > 前端 > 正文

微信小程序webview与h5通过postMessage实现实时通讯的实现

这篇文章主要介绍了微信小程序webview与h5通过postMessage实现实时通讯的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

在做 React Native 应用时,如果需要在 App 里面内嵌 H5 页面,那么 H5 与 App 之间可以通过 Webview 的 PostMessage 功能实现实时的通讯,但是在小程序里面,虽然也提供了一个 webview 组件,但是,在进行 postMessage 通讯时,官方文档里面给出了一条很变态的说明:

网页向小程序 postMessage 时,会在特定时机(小程序后退、组件销毁、分享)触发并收到消息。e.detail = { data },data 是多次 postMessage 的参数组成的数组
这里面已经说的很明白了,不管我们从 H5 页面里面 postMessage 多少次,小程序都是收不到的,除非:

  1. 用户做了回退到上一页的操作
  2. 组件销毁
  3. 用户点击了分享

这里面其实我没有完全说对,官方其实说的是 小程序后退,并没有说是用户做回退操作,经过我的实测,确实人家表达得很清楚了,我们通过微信官方的SDK调起的回退也是完全可行的:

wx.miniProgram.navigateBack()

大体思路

从上面的分析和实测中我们可以知道,要实现无需要用户操作即可完成的通讯,第三种情况我们是完全不需要考虑了的,那么来仔细考虑第 1 和第 2 种场景。

第 1 种方式:回退

当我们想通过网页向小程序发送数据,同时还可以回退到上一个页面时,我们可以在 wx.miniProgram.postMessage 之后,立马调用一次 wx.miniProgram.navigateBack(),此时小程序的操作是:

  1. 处理 postMessage 信息
  2. 回退到上一页

我们在处理 postMessage 的时候做一些特殊操作,可以将这些数据保存下来

第 2 种方式:组件销毁

这是我感觉最合适的一种方式,可以让小程序拿到数据,同时还保留在当前页面,只需要销毁一次 webview 即可,大概的流程就是:

  1. 小程序 postMessage
  2. 小程序 navigateTo 将小程序页面导向一个特殊的页面
  3. 小程序的那个特殊页面立马回退到 webview 所在的页面
  4. webview 所在的页面的 onShow 里面,做一次处理,将 webview 销毁,然后再次打开
  5. 触发 onMessage 拿到数据
  6. H5 页面再次被打开

这种方式虽然变态,但是至少可以做到实时拿到数据,同时还保留在当前 H5 页面,唯一需要解决的是,在做这整套操作前,H5 页面需要做好状态的缓存,要不然,再次打开之后,H5 的数据就清空了。

第 1 种方式:通过回退,将数据提交给小程序之后传递给 webview 的上一页面

这种方式实现起来其实是很简单的,我们现在新建两个页面:

sandbox/canvas-by-webapp/index.js

const app = getApp();

Page({
 data: {
  url: '',
  dimension: null,
  mime: '',
 },
 handleSaveTap: function() {
  wx.navigateTo({
   url: '/apps/browser/index',
   events: {
    receiveData: data => {
     console.log('receiveData from web browser: ', data);
     if (typeof data === 'object') {
      const { url, mime, dimension } = data;
      if (url && mime && dimension) {
       this.setData({
        url,
        dimension,
        mime,
       });

       this.save(data);
      }
     }
    }
   }
  })
 },

 save: async function({ url, mime, dimension }) {
  try {
   await app.saveImages([url]);
   app.toast('保存成功!');
  } catch (error) {
   console.log(error);
   app.toast(error.message || error);
  }
 },
});

上面的代码中,核心点,就在于 wx.navigateTo 调用时,里面的 events 参数,这是用来进行与 /apps/browser/index 页面通讯,接收数据用的。

apps/browser/index.js

我省略了绝大多数与本文无关的代码,保存最主要的三个:

Page({
 onLoad() {
  if (this.getOpenerEventChannel) {
   this.eventChannel = this.getOpenerEventChannel();
  }
 },
 handleMessage: function(message) {
  const { action, data } = message;
  if (action === 'postData') {
   if (this.eventChannel) {
    this.eventChannel.emit('receiveData', data);
   }
  }
 },

 handlePostMessage: function(e) {
  const { data } = e.detail;
  if (Array.isArray(data)) {
   const messages = data.map(item => {
    try {
     const object = JSON.parse(item);
     this.handleMessage(object);
     return object;
    } catch (error) {
     return item;
    }
   });

   this.setData({
    messages: [...messages],
   });
  }
 },
})

其实,onLoad 方法中,我们使用了自微信 SDK 2.7.3 版本开始提供的 getOpenerEventChannel 方法,它可以创建一个与上一个页面的事件通讯通道,这个我们会在 handleMessage 中使用。

handlePostMessage 就是被 bindmessage 至 webview 上面的方法,它用于处理从 H5 页面中 postMessage 过来的消息,由于小程序是将多次 postMessage 的消息放在一起发送过来的,所以,与其它的Webview不同点在于,我们拿到的是一个数组: e.detail.data, handlePostMessage 的作用就是遍历这个数组,取出每一条消息,然后交由 handleMessage 处理。

handleMessage 在拿到 message 对象之后,将 message.action 与 message.data 取出来(*这里需要注意,这是我们在 H5 里面的设计的一种数据结构,你完全可以在自己的项目中设计自己的结构),根据 action 作不同的操作,我在这里面的处理是,当 action === 'postData' 时,就通过 getOpenerEventChannel 得到的消息通道 this.eventChannel 将数据推送给上一级页面,也就是 /sandbox/canvas-by-webapp,但是不需要自己执行 navigateBack ,因为这个需要交由 H5 页面去执行。

H5 页面的实现

我的 H5 主要就是使用 html2canvas 库生成 Canvas 图(没办法,自己在小程序里面画太麻烦了),但是这个不在本文讨论过程中,我们就当是已经生成了 canvas 图片了,将其转为 base64 文本了,然后像下面这样做:

wx.miniProgram.postMessage({
 data: JSON.stringify({
  action: 'postData',
  data: 'BASE 64 IMAGE STRING'
 })
});

wx.miniProgram.navigateBack()

将数据 postMessage 之后,立即 navigateBack() ,来触发一次回退,也就触发了 bindmessage 事件。

使用销毁 webview 实现实时通讯
接下来,咱就开始本文的重点了,比较变态的方式,但是也没想到更好的办法,所以,大家将就着交流吧。

H5 页面的改变

wx.miniProgram.postMessage({
 data: JSON.stringify({
  action: 'postData',
  data: 'BASE 64 IMAGE STRING'
 })
});

wx.miniProgram.navigateTo('/apps/browser/placeholder');

H5 页面只是将 wx.miniProgram.navigateBack() 改成了 wx.miniProgram.navigateTo('/apps/browser/placeholder') ,其它的事情就先都交由小程序处理了。

/apps/browser/placeholder

这个页面的功能其实很简单,当打开它了之后,做一点点小操作,立马回退到上一个页面(就是 webview 所在的页面。

Page({
 data: { loading: true },
 onLoad(options) {

  const pages = getCurrentPages();

  const webviewPage = pages[pages.length - 2];

  webviewPage.setData(
   {
    shouldReattachWebview: true
   },
   () => {
    app.wechat.navigateBack();
   }
  );
 },
});

我们一行一行来看:

const pages = getCurrentPages();

这个可以拿到当前整个小程序的页面栈,由于这个页面我们只允许从小程序的 Webview 页面过来,所以,它的上一个页面一定是 webview 所在的页面:

const webviewPage = pages[pages.length - 2];

拿到 webviewPage 这个页面对象之后,调用它的方法 setData 更新一个值:

  webviewPage.setData(
   {
    shouldReattachWebview: true
   },
   () => {
    app.wechat.navigateBack();
   }
  );

shouldReattachWebview 这个值为 true 的时候,表示需要重新 attach 一次 webview,这个页面的事件现在已经做完了,回到 webview 所在的页面

apps/browser/index.js 页面

我同样只保留最核心的代码,具体的逻辑,我就直接写进代码里面了。

Page({
 data: {
  shouldReattachWebview: false, // 是否需要重新 attach 一次 webview 组件
  webviewReattached: false,   // 是否已经 attach 过一次 webview 了
  hideWebview: false      // 是否隐藏 webview 组件
 },
 onShow() {
  // 如果 webview 需要重新 attach 
  if (this.data.shouldReattachWebview) {
   this.setData(
    {
     // 隐藏 webview
     hideWebview: true,
    },
    () => {
     this.setData(
      {
       // 隐藏之后立马显示它,此时完成一次 webview 的销毁,拿到了 postMessage 中的数据
       hideWebview: false,
       webviewReattached: true,
      },
      () => {
       // 拿到数据之后,处理 canvasData
       this.handleCanvasData();
      }
     );
    }
   );
  }
 },
 // 当 webview 被销毁时,该方法被触发
 handlePostMessage: function(e) {
  const { data } = e.detail;
  if (Array.isArray(data)) {
   const messages = data.map(item => {
    try {
     const object = JSON.parse(item);
     this.handleMessage(object);
     return object;
    } catch (error) {
     return item;
    }
   });

   this.setData({
    messages: [...messages],
   });
  }
 },
 // 处理每一条消息
 handleMessage: function(message) {
  const {action, data} = message
  // 如果 saveCanvas action
  if (action === 'saveCanvas') {
   // 将数据先缓存进 Snap 中
   const { canvasData } = this.data;
   // app.checksum 是我自己封装的方法,计算任何数据的 checksum,我拿它来当作 key
   // 这可以保证同一条数据只会被处理一次
   const snapKey = app.checksum(data);
   // 只要未处理过的数据,才需要再次数据
   if (canvasData[snapKey] !== true) {
    if (canvasData[snapKey] === undefined) {
     // 将数据从缓存进 `snap` 中
     // 这也是我自己封装的一个方法,可以将数据缓存起来,并且只能被读取一次
     app.snap(snapKey, data);
     // 设置 canvasData 中 snapKey 字段为 `false`
     canvasData[snapKey] = false;
     this.setData({
      canvasData,
     });
    }
   }
  }
 },
 // 当 webview 被重新 attach 之后,canvas 数据已经被保存进 snap 中了,
 handleCanvasData: async function handleCanvasData() {
  const { canvasData } = this.data;
  // 从 canvasData 中拿到所有的 key,并过滤到已经处理过的数据
  const keys = Object.keys(canvasData).filter(key => canvasData[key] === false);

  if (keys.length === 0) {
   return;
  }

  for (let i = 0; i 

对应的 index.wxml 文件内容如下:

流程回顾与总结

  1. 打开 webview 页面,打开 h5
  2. h5 页面生成 canvas 图,并转为 base64 字符
  3. 通过 wx.miniProgram.postMessage 将 base64 发送给小程序
  4. 调用 wx.miniProgram.navigateTo 将页面导向一个特殊页面
  5. 在特殊页面中,将 webview 所在页面的 shouldReattachWebview 设置为 true
  6. 在特殊页面中回退至 webview 所在页面
  7. webview 所在页面的 onShow 事件被触发
  8. 在 onShow 事件检测 shouldReattachWebview 是否为 true,若为 true
  9. 将 hideWebview 设置为 true,引起 web-view 组件的销毁
  10. handlePostMessage 被触发,解析所有的 message 之后交给 handleMessage 逐条处理
  11. handleMessage 发现 action === 'saveCanvas' 的事件,拿到 data
  12. 根据 data 计算 checksum ,以 checksum 为 key 缓存下来数据,并将这个 checksum 保存到 canvasData 对象中
  13. 此时 hideWebview 被 onShow 里面 setData 的回调中的 setData 重新置为 false,web-view 重新加 attach,H5页面重新加载
  14. webview 重新 attach 之后, this.handleCanvasData 被触发,
  15. handleCanvasData 检测是否有需要保存的 canvas 数据,如果有,保存,修改 canvasData 状态

整个流程看旧去很繁琐,但是写起来其实还好,这里面最主要的是需要注意,数据去重,微信的 postMessage 里面拿到的永远 都是 H5 页面从被打开到关闭的所有数据。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


推荐阅读
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • 本文介绍了前端人员必须知道的三个问题,即前端都做哪些事、前端都需要哪些技术,以及前端的发展阶段。初级阶段包括HTML、CSS、JavaScript和jQuery的基础知识。进阶阶段涵盖了面向对象编程、响应式设计、Ajax、HTML5等新兴技术。高级阶段包括架构基础、模块化开发、预编译和前沿规范等内容。此外,还介绍了一些后端服务,如Node.js。 ... [详细]
  • 小程序自动授权和手动接入的方式及操作步骤
    本文介绍了小程序支持的两种接入方式:自动授权和手动接入,并详细说明了它们的操作步骤。同时还介绍了如何在两种方式之间切换,以及手动接入后如何下载代码包和提交审核。 ... [详细]
  • 微信小程序导航跟随的实现方法
    本文介绍了在微信小程序中实现导航跟随的方法。通过设置导航的position属性和绑定滚动事件,可以实现页面向下滚动到导航位置时,导航固定在页面最上方;页面向上滚动到导航位置时,导航恢复到原始位置;点击导航可以平滑跳转到相应位置。代码示例也给出了具体实现方法。 ... [详细]
  • 微信民众号商城/小顺序商城开源项目介绍及使用教程
    本文介绍了一个基于WeiPHP5.0开发的微信民众号商城/小顺序商城的开源项目,包括前端和后端的目录结构,以及所使用的技术栈。同时提供了项目的运行和打包方法,并分享了一些调试和开发经验。最后还附上了在线预览和GitHub商城源码的链接,以及加入前端交流QQ群的方式。 ... [详细]
  • PHP小习题:PHP小练习题前几天在百度知道里面看到有位网友询问如何制作一下的小程序:用php语言设计一个小程序,计算今天到达下月的天数、全部输出这些天数,并使得每天的日期以三种颜 ... [详细]
  • 校园表白墙微信小程序,校园小情书、告白墙、论坛,大学表白墙搭建教程
    小程序的名字必须和你微信注册的名称一模一样在后台注册好小程序。mp.wx-union.cn后台域名https。mp.wx-union.cn ... [详细]
  • 项目需要实现弹幕,网上参考了各种方法,最后觉得transform+transition实现的效果在移动设备上性能最好,在iphone6和红米4上测试,看不到 ... [详细]
  • 本文讨论了如何在微信支付宝两套小程序中生成一张二维码,实现支付宝扫码进入支付宝小程序和微信扫码进入微信小程序的对应桌号进行点餐的功能,提供了一些实现方案供参考。 ... [详细]
  • jmeter实践:从csv中获取带引号的数据详情的技巧和运行全部数据的方法
    本文分享了jmeter实践中从csv中获取带引号的数据的解决办法,包括设置CSV Data Set Config和运行脚本获取数据的方法。另外还介绍了循环运行csv中全部数据的解决方法,避免每次修改csv用例都需要修改脚本的麻烦。通过了解和掌握工具的细节点,可以更好地解决问题和提高技术水平。 ... [详细]
  • flowable工作流 流程变量_信也科技工作流平台的技术实践
    1背景随着公司业务发展及内部业务流程诉求的增长,目前信息化系统不能够很好满足期望,主要体现如下:目前OA流程引擎无法满足企业特定业务流程需求,且移动端体 ... [详细]
  • 微信开放外链的第二阶段:腾讯和阿里巴巴的博弈
    2021年11月30日,微信开始进行“开放外链”的第二阶段,允许在微信个人会话中打开外部链接和在微信群中打开电商链接。虽然这是腾讯和阿里巴巴都能接受的阶段性结果,但双方都不会太满意。接下来几个月,腾讯和阿里将展开复杂的博弈,我们作为外人很难看清全过程。工信部从未要求腾讯无条件开放微信API,本次开放的也只是普通的HTTP链接。 ... [详细]
  • 小程序获取用户信息按钮返回中文地址
    1.我是根据官方文档中描述去写的按钮 可以看到button中加了zh_CNopen-typegetUserInfobindgetuserinfogetU ... [详细]
  • iOS超签签名服务器搭建及其优劣势
    本文介绍了搭建iOS超签签名服务器的原因和优势,包括不掉签、用户可以直接安装不需要信任、体验好等。同时也提到了超签的劣势,即一个证书只能安装100个,成本较高。文章还详细介绍了超签的实现原理,包括用户请求服务器安装mobileconfig文件、服务器调用苹果接口添加udid等步骤。最后,还提到了生成mobileconfig文件和导出AppleWorldwideDeveloperRelationsCertificationAuthority证书的方法。 ... [详细]
  • 抖音大话西游情侣头像怎么制作 紫霞至尊宝情侣头像来袭
    抖音上这几天七夕紫霞仙子和至尊宝火爆了,相信你也想要制造一张属于自己的大话西游吧,要是不知道怎么设置的话可以看看下面的教程哦。1、微信搜索小程序【粉丝头像】;2、选择原图;3、选择 ... [详细]
author-avatar
liuleyi
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有