uni-app实现web-view和App之间的相互通信

8,529 阅读2分钟

前些时候使用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.scope.scope.getAppWebview() 可以直接获取到当前页面的webview对象。

getAppWebview 相当于html5plus里的plus.webview.currentWebview()。 在uni-app里vue页面直接使用plus.webview.currentWebview()无效, 使用getAppWebview 获取到的webview对象,并不是我们的子应用的webview, 还需要在后边加上.children()[0]才行。在uniapp的官方文档web-view介绍中有提到,具体可查看uniapp官方文档 web-view文档

1683257044655.png

子应用(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)
}