前些时候使用uni-app开发的app,需要和web-view引入的网页(Vue项目生成的H5)相互通信,踩了点小坑,特此记录下来。
web-view是一个 web 浏览器组件,可以用来承载网页的容器,会自动铺满整个页面(nvue 使用需要手动指定宽高)。具体它有哪些属性、方法我就不啰嗦了,uni-app官方web-view文档有介绍,我们直接进入正题。
现在我们事先做个约定,将主应用uni-app项目定为父应用,web-view引入的页面定为子应用。
一、web-view引入的页面向App主应用传递数据(子 => 父)
只需要以下简单的几个步骤即可,官方有介绍。
1、在官网中下载最新的 uni.webview.js(截止目前最新uni.webview.1.5.4.js),uni.web-view.js是一个可以帮助我们在非uniapp的项目中使用uni-app方法的库。
我们只需在子应用的index.html中引入uni.webview.js,你可下载后本地引入、亦或者采用sdn方式引入都行。
2、在子应用需要通信的地方使用uni.postMessage即可向父应用通信。
<script>
export default {
mounted() {
document.addEventListener('UniAppJSBridgeReady', () => {
// uni.webView.getEnv( (res) =>{
// console.log('当前环境:' + JSON.stringify(res));
// });
uni.postMessage({
data : {
action : "h5向app传值"
}
})
});
},
};
</script>
3、父应用的web-view只需添加 message 事件即可接受来自子页面的数据
<web-view :src="webUrl" @message="getMessage"></web-view>
getMessage(evt){
console.log('来自子页面的数据=',evt)
},
二、App向web-view引入的页面传递数据(父 => 子 )
1、通过web-view属性src带参数通信
这是最直接最方便的通信方式,好处就是方便直接,只需在web-view的src中带上参数即可,缺点就是无法在我们需要的时候实时传递信息,不够灵活。
<web-view :src="webUrl" v-if="webUrl"></web-view>
<script>
import qs from "qs"
export default {
data() {
return {
webUrl : ""
}
},
onLoad(opts) {
this.start(opts)
},
methods: {
start(opts){
const baseUrl = this.$store.state.env.VUE_APP_PATROL_H5_URL
const user = this.$store.state.userInfo.user
const config ={
hstk : this.$store.state.token,
userId : opts.id ? opts.id : user.userId,
nickName : opts.name ? opts.name : user.nickName ,
from : opts.from ? opts.from : undefined
}
const configStr = qs.stringify(config)
const webUrl = `${baseUrl}?${configStr}`
console.log('---patrol-h5---webUrl--',webUrl)
this.webUrl = webUrl
}
}
}
</script>
2、evalJS方法
evalJS完美解决了我们带参传递的痛点,有了它,我们可以在我们需要的任何时刻向子应用传递数据。
使用evalJS的关键点是要正确获取到 webview,否则使用evalJS将会报错方法不存在,自然就无法顺利传递数据了。
父应用页面(patrol-h5.vue):
<template>
<view class="container">
<NavBar title="执法巡查" />
<view class="content-wrap">
<web-view :src="webUrl" v-if="webUrl" @message="getMessage"></web-view>
</view>
</view>
</template>
<script>
import qs from "qs"
export default {
data() {
return {
webUrl : "",
wvNode : null ,
}
},
onLoad(opts) {
this.start(opts)
},
onReady() {
// #ifdef APP-PLUS
let currentWebview = this.$scope.$getAppWebview();
this.wvNode = currentWebview.children()[0]
// #endif
},
onHide(){
// console.log('patrol-h5-onHide')
// #ifdef APP-PLUS
const name = 'stop';
this.wvNode.evalJS("msgFromApp('" + name + "')");
// console.log('---currentWebview---wvNode---onHide=',this.wvNode.evalJS)
// #endif
},
onUnload() {
// console.log('patrol-h5-onUnload')
// #ifdef APP-PLUS
const name = 'stop';
this.wvNode.evalJS("msgFromApp('" + name + "')");
// console.log('---currentWebview---wvNode---onUnload=',this.wvNode.evalJS)
// #endif
},
methods: {
start(opts){
const baseUrl = this.$store.state.env.VUE_APP_PATROL_H5_URL
const user = this.$store.state.userInfo.user
const config ={
hstk : this.$store.state.token,
userId : opts.id ? opts.id : user.userId,
nickName : opts.name ? opts.name : user.nickName ,
from : opts.from ? opts.from : undefined
}
const configStr = qs.stringify(config)
const webUrl = `${baseUrl}?${configStr}`
console.log('---patrol-h5---webUrl--',webUrl)
this.webUrl = webUrl
}
}
}
</script>
上面代码实现了在页面隐藏(onHide)或者销毁(onUnload)的时候向子应用传递消息。
如果将获取webview的代码放到了onLoad中,那么我们必须加上足够的延时才行。
this.getAppWebview() 可以直接获取到当前页面的webview对象。
getAppWebview 相当于html5plus里的plus.webview.currentWebview()。 在uni-app里vue页面直接使用plus.webview.currentWebview()无效, 使用getAppWebview 获取到的webview对象,并不是我们的子应用的webview, 还需要在后边加上.children()[0]才行。在uniapp的官方文档web-view介绍中有提到,具体可查看uniapp官方文档 web-view文档 。
子应用(Vue项目patrol.vue页面)
export default {
created() {
this.initPage()
window.msgFromApp = this.getMsgFromApp
},
methods: {
getMsgFromApp(data){
console.log("---getMsgFromApp---",data)
if(data==='stop'){
this.endPatrol()
}
}
}
}
注意在由Vue构建的子应用项目中,接收父应用信息的方式:
window.msgFromApp = this.getMsgFromApp
这里的 msgFromApp 为 父应用中evalJS方法传递的方法名称。
如果是常规的html页面,只需要在页面中直接申明此方法即可,如下:
function msgFromApp(data) {
console.log("---getMsgFromApp---",data)
}