我们在app的页面中内嵌H5页面的明显好处就是不用频繁的去打包发布,只需要修改h5的页面就可以了
方便是方便, 但麻烦也是真的多, 比如测试的时候,要注意app 和 h5的关系
本次我们主要说的时 两者交互时,用户如何可以同步
首先,我们说一下原理
我们一般是在 app 的页面中, 添加一个 webview 的页面 src 指向我们要加载的 H5 页面就好了。要想让其可以通信,我们采用的是回调的方式
由H5主动来发消息,由app 来接收消息, 再由 app来调用回调函数来返回数据给H5
这里有三个点要清楚,
- h5是怎么主动发的消息-> uni.webview.postMessage
- app 怎么接收消息 -> <-webview- src="..." @message="handleMessage"><-/web-view>
- app把消息返回给h5 -> 回调函数
由此可见 uni.webview.postMessage是H5用的,所以打包在H5的页面中,
handleMessage是app中使用的,所以在 app 的包中
回调函数呢, 它是定义在 H5代码中,却是给 app使用的 webviewObj.evalJS(回调函数)
一般在vue 项目中,我们会在 main.js中 引入
传递的消息信息,必须写在 data 对象中。
event.detail.data 中的数据,以数组的形式接收每次 post 的消息
javascript
import * as uni from '@/utils/uni.webview.1.5.6.js'
Vue.prototype.$h5SendToApp = function(action,params, timeout=3000){
return new Promise((resove,reject)=>{
//每一次调用这个方法, 我们都来定义一个专用的回调函数
const callbackName = `app-callback_${Math.random().toSting(36).substr(2,8)}`;
const timeoutid = setTimeout(()=>{
if(window[callbackName]){ //如果系统已有这个回调函数,就删除它
delete window[callbackName]
// 返回一个错误, 每一次调用的 callbackname都是唯一的才对, 这里报出异常
reject(new Error(`调用 App action "${action}" 超时(${timeout}ms)`));
}
},timeout)
//如果没有报出异常就创建这个回调函数
window[callbackName] = (result) => {
clearTimeout(timeoutid);
delete window[callbackName];
resolve(result)
}
//**传递的消息信息,必须写在 data 对象中。
//event.detail.data 中的数据,以数组的形式接收每次 post 的消息**
uni.webView.postMessage({data:{action,params,callbackName}}) //这里把这次调用对应的回调函数的名称也传过去,方便app端来调用
//这里要注意参数的格式, 是对象中的data属性
})
}
当我们在app的页面中,使用了 webview 加载了 h5 的页面时,要获取用户信息, 我们可以调用
javascript
this.$h5SendToApp("getUserInfo",{}).then(res=>{
//这里的罗辑暂时放着
})
有了上面的代码, app端的 @message = "handleMessage" 就会接收到消息
javascript
handleMessage(event){
let paramsInfo = event.detail.data; //这是可以得到一个数组参数
//data:{action,params,callbackName} 得到就是这些参数
//app端的程序 可以根据不同的 action 和 params 来做不同的操作
//当app 的 action 完成之后, 可能通过 this.$scope.$getAppWebview().children()[0].evalJS(`callbackName(${JSON.stringify(data)})`)
}
this.scope.scope.scope.getAppWebview().children()0.evalJS(
callbackName(${JSON.stringify(data)}))这里说明一下 scope 和 getAppWebview.children()\[\]
uni-app 里 Vue 组件实例 ≠ 页面实例,是两个不同的对象,$scope 就是它们之间的桥。
A**pp 进程里同时存在一大堆 webview: 每个页面一个 webview, 还会再嵌一层子 webview。
this → Vue 组件实例(你写的 data()、methods 都在这,只有 JS 能力)
this.$scope → 页面实例(uni-app 按小程序 Page 规范创建的外壳,持有原生能力)
scope.scope.scope.getAppWebview() → 这个页面所在的原生 webview 对象(plus.webview.WebviewObject,有 evalJS / setStyle / addEventListener)
**
那为什么还要 .children()0?
$getAppWebview() 拿到的是页面自己的 webview(外层壳),而我们真正要 evalJS 的是里面那个加载远程 H5 的子 webview。