一、关于jsBridge
1、说起h5通信,一个绕不开的话题就是jsBridge,什么是jsBridge?
随着HTML5兴起,其独特的新特性,完全能够满足app开发需求,并且,相比于原生更容易入门,缩短开发周期,现在很少有纯原生的APP。但是,由于H5页面是内嵌到原生应用的WebView组件(一个浏览器内核)中,而手机浏览器Javascript引擎是在一个沙箱环境中运行,因此JavaScript的权限受到严格限制,不能够操作系统功能,我们常常在react项目中使用相机功能,需要使用桥接代码,不能直接去操作原生的api,所以,如果JavaScript要用到这些受限的能力时,就需要委托原生去实现,原生完成后,再将结果通知JavaScript,因此,JavaScript和原生之间就需要一个通信的桥梁,而这个桥梁本质上就是原生的浏览器组件(我们统一称之为WebView)与Javascript 通信的通道,一般称为 WebView JavaScript Bridge, 为了简单,一般简称为 JS bridge。
2、JS 调用 Native
JS 调用 Native 的实现方式较多,主要有拦截 URL Scheme (钉钉小程序路由跳转支持)、重写 prompt 、注入 API 等方法。
然而,上述方法都是native内部做了集成,有相应的处理函数用来做出处理。然而,小程序内部并没有这些集成方法,因此,都不太适用于小程序。
微信小程序有一句话:“ web-view 网页与小程序之间不支持除 JSSDK 提供的接口之外的通信 ”,也就是小程序通信必须使用小程序自己提供的JSSDK,不支持,其他任何通信方式。
二、js之间通信
- 上边介绍了js和非js之间的通信(jsBridge),那么,js(小程序)和js(h5)之间如何通信呢?
还记得我关于谷歌浏览器插件技术分享吗?谷歌浏览器插件后台js和当前页面通信是通过chrome.runtime完成消息传递(sendMessage)。
- 能使用window上的postMessage来传递信息吗?
答案显然不能,微信小程序也好,钉钉小程序也好,都对window进行了代理,h5的window和小程序window,不再是同一个window,这中间还存在着某些差异,因此,不能直接使用。
- h5和小程序有哪些隔阂?
由于h5是通过web-view内嵌在小程序中,因此,使用状态管理方式有可能就是不一样的,比如,有些使用redux,有些使用mobox,有些使用dvajs,有些使用react.Context。由于域名也可能是不同的,因此,公用localStorage和sessionStorage来共享数据也是不可取的。因此,需要中间一个桥梁来传递参数来共享数据。好在,各个小程序平台,设计时候都考虑到了这一点,可以借助各个平台提供的postMessage来实现双向通信。
三、各个平台消息传递汇总(rax语法)
1、钉钉小程序(阿里小程序)
(1)、小程序向h5发送消息
- 小程序端
注意: dd.createWebViewContext里参数必须设置为Embed的id,否则将会失效
import { createElement, useEffect, useState } from 'rax';
import View from 'rax-view';
import styles from './index.module.scss';
import Embed from 'rax-embed';
function WebView({ src = 'http://127.0.0.1/index.html' }) {
const [content, setContent] = useState(null);
useEffect(() => {
const contentWeb = dd.createWebViewContext('web-view-page');
setContent(contentWeb);
}, []);
function handleSendMessage() {
content.postMessage({ items: '20', age: '490859' });
}
return (
<View className={styles['web-view']}>
<Embed
src={src}
id="web-view-page"
onLoad={() => handleSendMessage()}
style={{
height: '100%',
width: '100%',
}}
/>
</View>
);
}
export default WebView;
- h5端
使用前先向h5端引入钉钉web-view包。
<script type="text/javascript" src="https://appx/web-view.min.js"></script>
// 如该H5页面需要同时在非钉钉客户端内使用,为避免该请求404,可参考以下写法
// 请尽量在html头部执行以下脚本
<script>
if (navigator.userAgent.toLowerCase().indexOf('dingtalk') > -1) {
document.writeln('<script src="https://appx/web-view.min.js"' + '>' + '<' + '/' + 'script>');
}
// 接收来自小程序的消息。
dd.onMessage = function(e) {
console.log(e); //{'sendToWebView': '1'}
}
</script>
(2)、h5向钉钉发送消息
- h5端
dd.postMessage({name:“测试web-view”});可以在具体某个页面使用,如果,需要统一拦截,可以在app.js入口文件进行修改逻辑。
<!-- html -->
<script type="text/javascript" src="https://appx/web-view.min.js"></script>
// 如该H5页面需要同时在非钉钉客户端内使用,为避免该请求404,可参考以下写法
// 请尽量在html头部执行以下脚本
<script>
if (navigator.userAgent.toLowerCase().indexOf('dingtalk') > -1) {
document.writeln('<script src="https://appx/web-view.min.js"' + '>' + '<' + '/' + 'script>');
}
// 网页向小程序 postMessage 消息
dd.postMessage({name:"测试web-view"});
</script>
- 小程序端(rax)
import { createElement, useEffect, useState } from 'rax';
import View from 'rax-view';
import styles from './index.module.scss';
import Embed from 'rax-embed';
function WebView({ src = 'http://127.0.0.1:9002/demo/index.html' }) {
function handleMessage(message) {
console.log(message, 'message', message.detail);
}
return (
<View className={styles['web-view']}>
<Embed
src={src}
onMessage={handleMessage}
style={{
height: '100%',
width: '100%',
}}
/>
</View>
);
}
export default WebView;
2、微信小程序
微信小程序h5向小程序发送消息,需要借助wx.miniProgram.postMessage 的JSSDK 1.3.2 向小程序发送消息,会在特定时机(小程序后退、组件销毁、分享)触发组件的 message 事件,执行onMessage事件。因此,必须执行跳转后,才能传递参数。
(1)、h5向小程序传参
- h5端
//引入wx插件
<script type="text/javascript" src="https://res.wx.qq.com/open/js/jweixin-1.3.2.js"></script>
//返回小程序带参
<script type="text/javascript">
wx.miniProgram.getEnv(function(res) {
if(res.miniprogram) {
wx.miniProgram.switchTab({
url: '/pages/index/index'
});
wx.miniProgram.postMessage({
data: {
name: name,
passwords :passwords
}
}); // 传的参数
}
});
</script>
由于微信sdk需要路由跳转时候才能发送消息,因此,有些场景使用不了,可以借助uni的SDK 来实现。更多端兼容配置可以参考uni官网web-view
<!DOCTYPE html>
<html lang="en">
<!-- wx和uni的SDK -->
<script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
<script type="text/javascript" src="https://unpkg.com/@dcloudio/uni-webview-js@0.0.3/index.js"></script>
<script>
document.addEventListener('UniAppJSBridgeReady', function() {
uni.postMessage({
data: {
action: 'message'
}
});
});
</script>
</html>
- 小程序端
import { createElement, useEffect, useState } from 'rax';
import View from 'rax-view';
import styles from './index.module.scss';
import Embed from 'rax-embed';
function WebView({ src = 'http://127.0.0.1:9002/demo/index.html' }) {
function handleMessage(message) {
console.log(message, 'message', message.detail);
}
return (
<View className={styles['web-view']}>
<Embed
src={src}
onMessage={handleMessage}
style={{
height: '100%',
width: '100%',
}}
/>
</View>
);
}
export default WebView;
(2)、小程序向h5传参
微信小程序不支持postMessage消息到h5端。可以通过url传参,注意,token和中文需要加密传输。