Bootstrap

uniapp使用webview嵌入vue页面及通信

最近刚做的一个需求,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。条件编译处理多端差异,这是官网,大家有兴趣可以去瞅瞅

;