最近刚做的一个需求,web端(Vue)使用了FormMaking库,FormMaking是一个拖拉拽的动态设计表单、快速开发的一个东西,拖拽完之后最终可以导出一个很长的json,然后通过json再进行回显,快速开发,但是因为FormMaking是基于Vue和ElementUI的,uniapp中不能直接使用,只能采用webview的方式进行嵌入。就是把Vue项目中的要嵌入的页面加入到白名单,也就是不需要登陆就可以访问该页面,直接将该页面通过webview嵌入到uniapp中即可
本来想着是将uniapp中的token传到webview页面中,在要嵌入的webview页面(Vue页面)中去获取json进行渲染的,但是通过webview嵌入到uniapp中,在webview页面去发请求会有问题,本来uniapp做的就少一些,更是第一次做webview,所以这两天还是碰到不少坑。
最终方案就是在uniapp中获取那一大串儿json,然后传递到webview页面进行渲染,然后提交表单的时候再通知webview页面(Vue页面)将表单json再传递给uniapp进行提交。所以就涉及到了uniapp与webview之间的通信问题,接下来介绍:
1、uniapp中嵌入外部网页:
<template>
<view class="index">
<web-view
:update-title="false"
:src="wvSrc"
></web-view>
</view>
</template>
<script>
export default {
data() {
return {
wvSrc: ""
}
},
onLoad() {
// #ifdef APP-PLUS
this.wvSrc = `http://192.168.124.19:3000/#/index`;
// #endif
},
}
</script>
<style scoped>
.index {
width: 100%;
height: 100%;
position: relative;
}
</style>
就是直接给src复制就可以。上面给webview标签设置了update-title为false,它默认的是true,也就是默认webview嵌入进来上面的标题会跟随webview的自动更新,就会覆盖掉uniapp自身的标题,设置为false就不会覆盖
2、uniapp向webview页面传值
2.1. 先看第一种:通过路径传递
就是在嵌入的时候就直接在后面拼接上参数,但是这个最好只传递字符串,因为传递json对象的话在解析的时候可能会出问题,并不太推荐这种方式,如果只是一两个简短的字符串的话,还是可以的啦。
uniapp页面传递:
let tk = 'test_token'
let id = 'pppppppiiiiiigggg'
let status = 'start'
this.wvSrc = `http://192.168.124.19:3000/#/index?params=${tk}WFT${id}WFT${status}`
webview页面接受:
首先得先引入一下:
public/index.html
<script type="text/javascript" src="https://unpkg.com/@dcloudio/[email protected]/index.js"></script>
或者进入浏览器输入该地址,Ctrl + A, Ctrl + C, 复制粘贴到本地,然后引入本地也是可以的,但是引入的时候一定是 import uni from '你的路径'
这样我们全局打印一下uni就会看到里面的方法:
然后在monted里面监听UniAppJSBridgeReady方法,也就是Vue该页面被嵌入到uniapp中的时候会触发,也就是可以使用uni的API了
mounted(){
console.log(uni)
// 待触发 `UniAppJSBridgeReady` 事件后,即可调用 uni 的 API。
document.addEventListener('UniAppJSBridgeReady', () => {
console.log('UniAppJSBridgeReady-->')
const [tk, id, status] = this.getQuery('params', 'hash').split('WFT')
console.log(tk, 'tk-->>')
console.log(id, 'id-->>')
console.log(status, 'status-->>')
})
},
methods: {
// 解析url传递的参数
getQuery(name, type = 'search') {
// const reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)")
// // const value = window.location.hash.substr(3).match(reg)
// const value = window.location.search.substr(1).match(reg)
// if (value) return decodeURIComponent(value[2])
// return null
// type取值 search | hash
return window.location[type].split(`${name}=`)[1]
}
}
如果你的Vue项目是hash模式路由参数就在window.lacation.hash中,如果不是就是window.location.search中,反正打印一下window.location就可以看到里面的信息了,再截取一下就OK了!
2.2. 第二种:通过调用当前的webview实例的evalJS方法
通过evalJS为这个子webview注入一段js。执行内部的逻辑
还有一点,webview优先级很高,并且默认都是全部覆盖app的,而且直接对其修改样式也是不起作用的,通过当前的webview实例对象也可以对其修改样式
一般我在嵌入的时候,就会拿到当前的webview实例,供后面使用
uniapp代码
// template代码
<button @tap="submit" class="btn">启动</button>
//script代码
onLoad() {
// #ifdef APP-PLUS
this.wvSrc = `http://192.168.124.19:3000/#/index`
setTimeout(() => {
this.curWebview = this.$scope.$getAppWebview().children()[0]
this.curWebview.setStyle({ height: '75%', scalable:true }) // scalable是否开启双指缩放
}, 500) //如果是页面初始化调用时,需要延时一下
// #endif
},
methods: {
//调用webview中的方法(注意getWebviewData一定是window上的方法,所以我们要在Vue页面中将对应的方法挂载到window上)
submit() {
let obj = { name: 'wft' }
//传递的参数如果是引用类型,就要JSON.stringify转一下,否则报错
this.curWebview.evalJS(`getWebviewData(${JSON.stringify(obj)})`)
}
}
webview代码
<script>
export default {
created() {
window.getWebviewData = this.getData
},
methods: {
//我们传递过来的是通过JSON.stringify转过的,但是到这儿之后直接转过来了,不需要我们再转通过JSON.parse转一下了,直接可以用
getData(obj) {
console.log(obj.name) // wft
}
}
}
</script>
这种方式传递参数还是很方便的,我这里方法名写的getData,是因为我的业务里,在点击按钮的时候要拿到webview中的数据,这样我调用webview页面的getData方法,获取到数据然后再通过uni.postMessage传递到uniapp就可以了,也是我接下来要介绍的,webview页面向uniapp传递
三、webview向uniapp传递消息
这个还是需要引入uni中的api,上面也有介绍
<script type="text/javascript" src="https://unpkg.com/@dcloudio/[email protected]/index.js"></script>
通过uni.postMessage传递
uni.postMessage({
data: {
info: {
name: 'wft',
age: 18
}
}
})
这个就很方便了,但是要注意,一定是上面的写法,上面写法我们真正传递的数据是info,data这个对象是必须要写的,官网也有写https://uniapp.dcloud.net.cn/component/web-view.html#web-view
然后在uniapp中监听
//template代码
<web-view :update-title="false" :src="wvSrc" @message="handlerMessage"></web-view>
//script代码
// 接受提交参数
handlerMessage(event) {
let { info } = event.detail.data[0]
console.log(info.name, 'info>>') // wft
console.log(info.age, 'info>>') // 18
}
就是这么接收的,很方便。
有一点细节需要注意一下,就是上面代码中我有使用了 // #ifdef APP-PLUS 和 // #endif
这个是判断当前是不是运行在app中的,因为uniapp平时开发调试可以运行在真机上也可以运行在浏览器上的,如果不加 // #ifdef APP-PLUS 和 // #endif 那么运行在浏览器上的时候就会报错了,因为浏览器上是没有this.$scope ,并不用说上面拿webview实例this.$scope.$getAppWebview().children()[0]了,这样可以确保只在运行在真机上的时候才会用那些api。条件编译处理多端差异,这是官网,大家有兴趣可以去瞅瞅