uniapp的app与webview的通讯及踩坑记录

4,982 阅读4分钟

由于需要在uniapp开发的app项目中集成游戏,网上查询一些集成的资料得知可以通过webview间接集成,所以就需要学习uniapp项目与webview的通讯,下面记录一下踩坑经验。

首先这是webview官方文档,文档讲述webview的属性是指向网页的链接,我这边没有放服务器,项目内直接集成的本地文件,App 平台是同时支持网络网页和本地网页(小程序仅支持加载网络网页,不支持本地html) ,但本地网页及相关资源(js、css等文件)必须放在 uni-app 项目根目录->hybrid->html 文件夹下或者 static 目录下, 像这样--


然后必须要在html中引入uni.webview.js才能使用uniAPI,其实就是把webview当做与html通讯的子组件了,子组件通过uniAPI派发给uniapp事件进行子传父通讯,下面记录一下两边通讯的方法。

webview向uniapp通讯

接受消息:

在uniapp页面通过监听 web-view 组件的 @message 事件(H5 暂不支持,可以直接使用 window.postMessage),可以在特定时机(后退、组件销毁、分享)触发并收到形参的evt.detail.data拿到消息。如果是nvue页面(必须手动指定宽高),可以直接使用@onPostMessage实时获取消息。

<template>
  <view>
    <web-view src="/hybrid/html/ceshi.html"
      @message="handleMessage" 
      @onPostMessage="handleMessage"></web-view>
  </view>
</template>

<script>
	export default {
		data() {
			return {}
		},
		methods: {
			handleMessage(evt) {
				console.log('这里是webview接收到的消息:' + JSON.stringify(evt.detail.data));
			}
		},
	}
</script>
<style scoped lang="scss"></style>

发送消息:

webview向应用发送消息使用uni.postMessage API,传递的消息数据需要写在data中,而且要先监听 UniAppJSBridgeReady 事件触发后,才能安全调用 uni 的 API

<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
  </head>
  <body>
    <p>web-view 组件加载网络 html 示例。点击下列按钮,跳转至其它页面。</p>
    <button id="navigateTo">navigateTo</button>
    <p class="desc">网页向应用发送消息,注意:小程序端应用会在此页面后退时接收到消息。</p>
    <button id="postMessage">postMessage</button>

    <!-- uni 的 SDK -->
    <script type="text/javascript" src="./uniWebView.js">
    <script type="text/javascript">
      // 待触发 `UniAppJSBridgeReady` 事件后,即可调用 uni 的 API。
      document.addEventListener('UniAppJSBridgeReady', function() {
        uni.postMessage({
            data: {
                action: 'message'
            }
        });
        document.getElementById('navigateTo').addEventListener('click', function() {
            uni.switchTab({
               url: '/pages/tabBar/API/API'
            });
        });
        document.getElementById('postMessage').addEventListener('click', function() {
          uni.postMessage({
            data: {
              action: 'message'
            }
          });
        });
      });
    </script>
  </body>
</html>

uniapp向webview的html通讯

1、nvue页面向webview的通讯

发送消息:

根据官网的通讯示例,为webview组件设定ref属性,通过点击事件调用evalJs函数即可发送消息。

<web-view ref="webviews" :webview-styles="webviewStyles" src="/hybrid/html/ceshi.html"
	@message="handleMessage" @onPostMessage="handleMessage"></web-view>
<button class="button" @click="evalJs">evalJs(点击向webview传参)</button>
evalJs() {
    this.$refs.webview.evalJs("document.body.style.background ='#00FF00'");
    //也可以直接调用webview中的函数传参通讯
    this.$refs.webviews.evalJs(`msgFromApp(${this.token})`);
}

接收消息:

直接在UniAppJSBridgeReady事件回调中添加函数,nvue页面在调用evalJs后就会触发,简单方便。

document.addEventListener('UniAppJSBridgeReady', () => {
    window.msgFromApp = (ev) => {
        alert('接收到uniapp参数' + JSON.stringify(ev))
    }
});

2、vue页面向webview的通讯(未实践)

发送消息:

查看官方的webview扩展文档,可以通过this.$scope.$getAppWebview()获取当前应用的所有 WebView 实例,再拿到当前web-view组件对应的js对象后,任意地方即可通过evalJS函数发送消息。

注意:通过这个帖子(为什么evalJS无效,明明官方就是这样写的 - DCloud问答),了解到wv变量要在export default 外面定义才可以;当然也可能现在直接在data中定义也可以,具体没实操。

var wv;//计划创建的webview
export default {
    methods: {  
        setMessage() {  
            wv.evalJs(`msgFromApp(${this.token})`);
        } 
    },
    onReady() {
	    // #ifdef APP-PLUS
	      var currentWebview = this.$scope.$getAppWebview() 
	        //此对象相当于html5plus里的plus.webview.currentWebview()。
	        //在uni-app里vue页面直接使用plus.webview.currentWebview()无效
	      setTimeout(function() {
	        wv = currentWebview.children()[0]
	      }, 1000); //如果是页面初始化调用时,需要延时一下
	    // #endif
    }
};

接受消息:

和nvue一样,直接在html页面中添加函数,vue页面在调用evalJs后就会触发,简单方便。

    window.msgFromApp = (ev) => {
        alert('接收到uniapp参数' + JSON.stringify(ev))
    }

3、vue页面通过web-view的src属性带参数向webview的通讯(未实践)

这种只适合传一次固定参数值的场景,并且是单向通讯,否则不建议。

额外注意URL参数特殊字符要记得先编码转义,html接受时再解码回来。

vue代码传参:

<template>
  <view>
    <web-view v-if="webViewSrc" :src="webViewSrc"></web-view>
  </view>
</template>

<script>
export default {
  data() {
    return {
      webViewSrc: ''
    };
  },
  onload: {
      const lat = 123.456; // 示例纬度
      const long = 78.910; // 示例经度
      this.webViewSrc = `https://xxx.com/home.html?lat=${lat}&long=${long}`;
  }
};
</script>

html代码接收:

window.location.search直接获取URL中的参数值,使用URLSearchParams API来解析参数。

  const params = new URLSearchParams(window.location.search);
  const lat = params.get('lat');
  const long = params.get('long');

三、注意事项

1、webview默认铺满整个页面,但在nvue页面在必须手动指定宽高,否则不显示;可以通过webview-styles属性动态绑定宽高,通过更改变量中的width、height实现--

				webviewStyles: {
					progress: {
						color: '#FF3333'
					},
					width: '750rpx',
					height: '700rpx'
				},

2、uni的sdk<script type="text/javascript" src="https://gitcode.net/dcloud/uni-app/-/raw/dev/dist/uni.webview.1.5.6.js"></script>直接引用我这边无效,建议复制粘贴放html本地文件或者下载到自己的服务器

3、HBuilderX的终端不会打印html页面的console.log日志,可以使用alert调试。

4、引入的uni的SDK仅支持getEnv、postMessage、navigateBack、navigateTo、reLaunch、redirectTo、switchTab这几种API。

5、App端安卓不用管,iOS有UIWebview和WKWebview两种webview,如果是老HBuilder和HBuilderX 2.2.5之前的版本,则额外要注意此条,简单说就是老版本编译器的web-view组件默认使用UIWebview,App Store已经不接受使用UIWebView的新App,点击查看官方文档

6、WKWebview有着更严格的跨域访问限制,本地html通过js访问网络及本地文件都算跨域访问(标准的xhr或jq的ajax,都无法跨域)

7、如果H5页面是uni写的,uni方法冲突可以从参考这位博主的改进方法

四、参考文档

web-view | uni-app官网 (dcloud.net.cn)

在web-view加载的本地及远程HTML中调用uni的API及网页和vue页面通讯 - DCloud问答

uniapp实现与webview之间的相互通讯_uniapp webview 通信-CSDN博客

uniapp向webview中发送消息 - DCloud问答

uni-app实现web-view和App之间的相互通信 - 掘金 (juejin.cn)

欢迎补充交流~