文章转自:https://www.jb51.net/article/167957.htm

这篇文章主要介绍了微信小程序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调起的回退也是完全可行的:

1

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

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

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

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 文本了,然后像下面这样做:

1

2

3

4

5

6

7

8

wx.miniProgram.postMessage({

 data: JSON.stringify({

  action: 'postData',

  data: 'BASE 64 IMAGE STRING'

 })

});

wx.miniProgram.navigateBack()

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

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

H5 页面的改变

1

2

3

4

5

6

7

8

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 所在的页面。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

Page({

 data: { loading: true },

 onLoad(options) {

  const pages = getCurrentPages();

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

  webviewPage.setData(

   {

    shouldReattachWebview: true

   },

   () => {

    app.wechat.navigateBack();

   }

  );

 },

});

我们一行一行来看:

1

const pages = getCurrentPages();

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

1

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

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

1

2

3

4

5

6

7

8

webviewPage.setData(

 {

  shouldReattachWebview: true

 },

 () => {

  app.wechat.navigateBack();

 }

);

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

apps/browser/index.js 页面

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

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 < keys.length; i += 1) {

   try {

    const key = keys[i];

    const { url } = app.snap(key);

    // 通过自己封装的方法,将 url(也就是Base64字符)保存至相册

    const saved = await app.saveImages(url);

    // 更新 canvasData 对象

    canvasData[key] = true

    this.setData({

     canvasData

    })

    console.log('saved: ', saved);

   } catch (error) {

    app.toast(error.message);

    return;

   }

  }

 },

})

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

1

<web-view src="{{src}}" wx:if="{{src}}" bindmessage="handlePostMessage" wx:if="{{!hideWebview}}" />

流程回顾与总结

  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 页面从被打开到关闭的所有数据。

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

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

  1. 微信小程序web-view 外部引用h5页面调用摄像头录制视频 配有提示音

    微信小程序web-view 外部引用h5页面调用摄像头录制视频 配有提示音 1.目前的需求是什么 2.都踩了那些坑 1.小程序 2.h5语音提示 3.语音合成声音录制不进去,ios有时候是麦克风,有时 ...

  2. 微信小程序web-view与H5之间交互(含支付)

    第一章 了解web-view与H5的交互(含支付) 文章目录 第一章 了解web-view与H5的交互(含支付) 前言 一.web-view是什么? 二.使用步骤 三.业务场景 1.小程序带参数跳转链 ...

  3. 微信小程序web-view与H5 通信方式探索

    本文作者为奇舞团前端开发工程师 小程序简介 小程序是一种全新的连接用户与服务的方式,它可以在微信内被便捷地获取和传播,同时具有出色的使用体验. 需求 微信小程序 H5 混合开发就是 在一个小程序中,采 ...

  4. 微信里嵌入html5页面,微信小程序web-view嵌套H5实现微信支付功能解决方案

    一. 产品现状 首先,在接入微信支付功能以前,我们的产品情况是这样的: 1 有公众号和app的h5站点及相关配套功能 2 小程序已经有一些基础功能,这些功能没有使用web-view 3 小程序之前的服 ...

  5. 微信小程序web-view嵌入h5页面,分享当前页

    ①  index.wxml 为web-view标签添加bindload事件  web-view bindload | 微信开放文档 <web-view src="{{webview_u ...

  6. 微信小程序web-view嵌入h5———微信名包含emoji表情,导致数据无法存入数据库

    由于部分用户微信名包含emoji表情导致无法存入数据库. 报错为:[2019-08-27 16:21:12] [ERROR] [org.hibernate.engine.jdbc.spi.SqlExc ...

  7. [微信小程序]WebView内嵌H5实现本地文件上传

    [官方文档] 小程序与H5如何互相跳转 小程序与H5交互以上传文件为例 微信小程序开放能力web-view使用之h5页面与小程序页面交互传值 快速小程序开发之微信小程序内嵌 H5 微信小程序web-v ...

  8. 微信小程序webview(H5页面)调用微信小程序支付

    1.业务描述:微信小程序商城入口进入的页面是商城H5页面,在H5页面进行微信支付如何实现: 2.微信小程序(webview访问H5页面)必须使用微信小程序支付: 如何实现以及实现方式以及支付后页面返回 ...

  9. 实现微信小程序web-view内嵌H5中的下载功能(大文件切片下载)

    实现微信小程序内嵌H5中的下载功能 一.项目场景: 难点 解决方案: 1.H5微信小程序: a.首先必不可少的是安装jweixin-module模块: b.在main.js中将依赖绑定: c.H5对应 ...

最新文章

  1. 计算机视觉与深度学习 | 基于边缘与形态学的细胞检测
  2. 七牛云存储:通过SDK上传图片
  3. php自定义能过滤器,过滤器与自定义过滤器的介绍
  4. HDU 1999 不可摸数
  5. MySQL查询多表定义实体类_自己设计一个 JAVA + MyBatis 解析实体类多表通用查询
  6. bool类型数组转换成一个整数_「PHP」常用的数组键值操作函数,面试重点
  7. springcloud config服务端配置(二)
  8. 面向小姐姐的编程——java面相对象之抽象
  9. 在mysql支持关系模型中_MySQL支持关系模型中、和三种不同的完整性约束
  10. linux http 访问限制,51CTO博客-专业IT技术博客创作平台-技术成就梦想
  11. 五分钟带你了解什么是PID模糊算法
  12. HTML5前端教程:jQuery项目实战
  13. shoppping collection
  14. 计算机主板 华硕 游戏用,华硕Z390-A大师主板,带你畅享极致游戏体验-华硕主板bios设置...
  15. Maya火球特效制作
  16. 外贸出口流程全图(转)
  17. Educational Codeforces Round 101 (Rated for Div. 2)
  18. 什么是支付通道,一篇文章了解支付通道理念
  19. unity 3d开发的大型网络游戏
  20. 中基鸿业工薪家庭理财方法

热门文章

  1. Java 基础 Collectors 使用
  2. leetCode85.最大矩形
  3. 用java搭建一个分布式服务器(一)
  4. Navicat工具中建立数据库索引
  5. proxy代理服务器的使用
  6. 浏览器访问Web服务器流程详细解析
  7. Managing Index-Organized Tables
  8. armbian在玩客云玩法
  9. UVA 208 救火车
  10. JavaScript判断用户使用的浏览器