在uni-app里通过web-view组件显示H5项目的踩坑之旅

为啥要在uni-app去显示h5?

说起来话长,最近的项目需求里,有个图超级复杂,uni-app使用echarts之后在真机渲染不出来,但是在h5又可以渲染出来,而这个bug echarts开发者好像还没有修复。所以导致所在图表的一整个页面都只好用H5来实现了。
另外,使用这种方法还有另外一个优点,就是app的这个界面可以热更新,就不需要通过重新安装apk来进行更新了。
只不过,我遇到了很多的坑,后面会一一列出来的。

1.如何在uni-app上使用web-view组件渲染内外部的h5页面?

官网有虽然有教程,但我还是把自己代码铺出来,因为感觉有些地方新手可能会很迷惘。
这是官网的有关这个组件的地址:https://uniapp.dcloud.net.cn/component/web-view.html#web-view

①在uni-app上的页面使用web-view

<template>
  <view class="" style="width: 100%;">
    <web-view :src="url" @message ="message"></web-view>
  </view>
</template>
<script>
export default {
  data() {
    return {
      url:'http://localhost:8080/#/test' //这是本地运行的另一个vue项目的地址
    };
  },
  onReady() {  
     // #ifdef H5
     window.addEventListener("message", this.message, false)  //如果不加这句代码,真机运行的时候,就无法监听到message事件
     // #endif  
  },   
  methods: {
    message(event){
		console.log(event)
		// #ifdef APP-PLUS
		if(event.detail.data[0].action=='success'){
			console.log('当字段为success的时候,跳转页面')
			uni.navigateTo({
				url:'sleepLog/device'
			})
		}
		// #endif  
    }
  }
};
</script>

② uni-app页面如何给H5发消息通信?

方法一:通过evalJS

首先在uni-app端,在页面加载完之后,增加一个全局函数,这个函数的声明放在methods里就好

  sendMsgToWebview() {
	 let _funName='msgFromUniapp'
	 let _data = {
	      msg:'refresh'
	    }
	 let currentWebview = this.$scope.$getAppWebview().children()[0];
	 currentWebview.evalJS(`${_funName}(${JSON.stringify(_data)})`)
},

然后可以在加载完dom之后的声明周期函数里使用,例如onReady

 onReady() {
  	  // #ifdef H5
     window.addEventListener("message", this.message, false)  
     // #endif  
	this.sendMsgToWebview()
  },

然后在h5端的mounted钩子函数里,加上

  mounted() {
		let that=this
		window.msgFromUniapp = function(arg) {
			//注意this指向
		if(arg.msg=='refresh'){
			console.log('shuaxin')
			//当uni-app发送这个refresh的时候,h5可以在这里监听并做想要做的事
		}
		}
  },
方法二:通过url携带参数

例如:
在uni-app的web-view上的url加上参数

 url:'http://localhost:8080'+'/#/index?token='+ uni.getStorageSync("accessToken")+'&userId='+getApp().globalData.global_user.id

在h5上获取url上的参数,可以在钩子函数mounted里获取

	let token=window.location.href.split('?')[1].split('&')[0].split('=')[1]//这是拿到的第一个参数 token
	let userId=window.location.href.split('?')[1].split('&')[1].split('=')[1]//这是拿到的第二个参数  userid

当然,拿到的token也可以把它存到vuex,在这里我就不多说了。

2.如何在H5项目使用uni-app的sdk,从而让h5可以和uni-app进行相互通信?

①引入webview.js

我是新建了个目录和文件用来存webview.js
在这里插入图片描述
web.js如下:

! function (e, n) {
  "object" == typeof exports && "undefined" != typeof module ? module.exports = n() : "function" == typeof define && define.amd ? define(n) : (e = e || self).webUni = n()
}(this, (function () {
  "use strict";
  try {
    var e = {};
    Object.defineProperty(e, "passive", {
      get: function () {
        !0
      }
    }), window.addEventListener("test-passive", null, e)
  } catch (e) {}
  var n = Object.prototype.hasOwnProperty;

  function t(e, t) {
    return n.call(e, t)
  }
  var i = [],
    a = function (e, n) {
      var t = {
        options: {
          timestamp: +new Date
        },
        name: e,
        arg: n
      };
      if (window.__dcloud_weex_postMessage || window.__dcloud_weex_) {
        if ("postMessage" === e) {
          var a = {
            data: [n]
          };
          return window.__dcloud_weex_postMessage ? window.__dcloud_weex_postMessage(a) : window.__dcloud_weex_.postMessage(JSON.stringify(a))
        }
        var o = {
          type: "WEB_INVOKE_APPSERVICE",
          args: {
            data: t,
            webviewIds: i
          }
        };
        window.__dcloud_weex_postMessage ? window.__dcloud_weex_postMessageToService(o) : window.__dcloud_weex_.postMessageToService(JSON.stringify(o))
      }
      if (!window.plus) return window.parent.postMessage({
        type: "WEB_INVOKE_APPSERVICE",
        data: t,
        pageId: ""
      }, "*");
      if (0 === i.length) {
        var r = plus.webview.currentWebview();
        if (!r) throw new Error("plus.webview.currentWebview() is undefined");
        var d = r.parent(),
          s = "";
        s = d ? d.id : r.id, i.push(s)
      }
      if (plus.webview.getWebviewById("__uniapp__service")) plus.webview.postMessageToUniNView({
        type: "WEB_INVOKE_APPSERVICE",
        args: {
          data: t,
          webviewIds: i
        }
      }, "__uniapp__service");
      else {
        var w = JSON.stringify(t);
        plus.webview.getLaunchWebview().evalJS('UniPlusBridge.subscribeHandler("'.concat("WEB_INVOKE_APPSERVICE", '",').concat(w, ",").concat(JSON.stringify(i), ");"))
      }
    },
    o = {
      navigateTo: function () {
        var e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {},
          n = e.url;
        a("navigateTo", {
          url: encodeURI(n)
        })
      },
      navigateBack: function () {
        var e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {},
          n = e.delta;
        a("navigateBack", {
          delta: parseInt(n) || 1
        })
      },
      switchTab: function () {
        var e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {},
          n = e.url;
        a("switchTab", {
          url: encodeURI(n)
        })
      },
      reLaunch: function () {
        var e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {},
          n = e.url;
        a("reLaunch", {
          url: encodeURI(n)
        })
      },
      redirectTo: function () {
        var e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {},
          n = e.url;
        a("redirectTo", {
          url: encodeURI(n)
        })
      },
      getEnv: function (e) {
        window.plus ? e({
          plus: !0
        }) : e({
          h5: !0
        })
      },
      postMessage: function () {
        var e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {};
        a("postMessage", e.data || {})
      }
    },
    r = /uni-app/i.test(navigator.userAgent),
    d = /Html5Plus/i.test(navigator.userAgent),
    s = /complete|loaded|interactive/;
  var w = window.my && navigator.userAgent.indexOf("AlipayClient") > -1;
  var u = window.swan && window.swan.webView && /swan/i.test(navigator.userAgent);
  var c = window.qq && window.qq.miniProgram && /QQ/i.test(navigator.userAgent) && /miniProgram/i.test(navigator.userAgent);
  var g = window.tt && window.tt.miniProgram && /toutiaomicroapp/i.test(navigator.userAgent);
  var v = window.wx && window.wx.miniProgram && /micromessenger/i.test(navigator.userAgent) && /miniProgram/i.test(navigator.userAgent);
  var p = window.qa && /quickapp/i.test(navigator.userAgent);
  for (var l, _ = function () {
      window.UniAppJSBridge = !0, document.dispatchEvent(new CustomEvent("UniAppJSBridgeReady", {
        bubbles: !0,
        cancelable: !0
      }))
    }, f = [function (e) {
      if (r || d) return window.__dcloud_weex_postMessage || window.__dcloud_weex_ ? document.addEventListener("DOMContentLoaded", e) : window.plus && s.test(document.readyState) ? setTimeout(e, 0) : document.addEventListener("plusready", e), o
    }, function (e) {
      if (v) return window.WeixinJSBridge && window.WeixinJSBridge.invoke ? setTimeout(e, 0) : document.addEventListener("WeixinJSBridgeReady", e), window.wx.miniProgram
    }, function (e) {
      if (c) return window.QQJSBridge && window.QQJSBridge.invoke ? setTimeout(e, 0) : document.addEventListener("QQJSBridgeReady", e), window.qq.miniProgram
    }, function (e) {
      if (w) {
        document.addEventListener("DOMContentLoaded", e);
        var n = window.my;
        return {
          navigateTo: n.navigateTo,
          navigateBack: n.navigateBack,
          switchTab: n.switchTab,
          reLaunch: n.reLaunch,
          redirectTo: n.redirectTo,
          postMessage: n.postMessage,
          getEnv: n.getEnv
        }
      }
    }, function (e) {
      if (u) return document.addEventListener("DOMContentLoaded", e), window.swan.webView
    }, function (e) {
      if (g) return document.addEventListener("DOMContentLoaded", e), window.tt.miniProgram
    }, function (e) {
      if (p) {
        window.QaJSBridge && window.QaJSBridge.invoke ? setTimeout(e, 0) : document.addEventListener("QaJSBridgeReady", e);
        var n = window.qa;
        return {
          navigateTo: n.navigateTo,
          navigateBack: n.navigateBack,
          switchTab: n.switchTab,
          reLaunch: n.reLaunch,
          redirectTo: n.redirectTo,
          postMessage: n.postMessage,
          getEnv: n.getEnv
        }
      }
    }, function (e) {
      return document.addEventListener("DOMContentLoaded", e), o
    }], m = 0; m < f.length && !(l = f[m](_)); m++);
  l || (l = {});
  var E = "undefined" != typeof webUni ? webUni : {};
  if (!E.navigateTo)
    for (var b in l) t(l, b) && (E[b] = l[b]);
  return E.webView = l, E
}));

②在main.js上引入和搭桥

import * as uni from './api/web/web.js'  
document.addEventListener("UniAppJSBridgeReady", function() { 
	console.log("h5通信")
  uni.webView.postMessage({  
    data: {  
      action: "onReady",  
    },  
  }); 
    Vue.prototype.myUni = uni  //这样全局就能使用了
});

③H5页面如何给uni-app发消息?

例如点击某个按键,要求uni-app切换界面:
我们就可以在methods里加上这个按键的函数

methods:{
	back(){
		//返回
		this.myUni.webView.postMessage({  
			data: {  
				action: "back",  
			},  
		}); 
 },
}

在uni-app端的message监听函数里,加上‘back’这个字段的监听:

 methods: {
	   sendMsgToWebview() {
	        let _funName='msgFromUniapp'
	        let _data = {
	            msg:'refresh'
	          }
	        let currentWebview = this.$scope.$getAppWebview().children()[0];
	        currentWebview.evalJS(`${_funName}(${JSON.stringify(_data)})`)
		},
    message(event){
		// #ifdef APP-PLUS
		if(event.detail.data[0].action=='back'){
			console.log('关闭当前页面')
			uni.reLaunch({
				url:'/pages/mainPage/mainPage'//这里改成你自己项目的页面,可以不用reLaunch,这是我的需求才用reLaunch
			})
		}
		// #endif  

    }
  }

其次,也要注意看看web-view组件有没有加上message的监听
在这里插入图片描述

3.uni-app加载web-view内嵌H5好慢?

由于uni-app加载web-view内嵌H5会有几秒是白屏的,加载也有点慢,所以我们可以在加载完H5的之前加个提示框,然后加载完之后把提示框关掉。
但是问题来了,假如这个提示框是我们自定义的,就没法完美显示,因为有web-view组件的页面,那个页面是被web-view全部占有了,就算动态分配高度这些给web-view组件,提示框也无法居中,会被web-view遮住,所以只能够用别的骚操作来实现提示框了。
不过一般的需求也不需要用自定义的提示框,所以我这里只分享普通的方式就好了。
可以在uni-app的onLoad里使用
uni.showLoading({ title:'加载中...' })
这个提示框的层级会比web-view的高。
然后在H5的main.js里
在这里插入图片描述
给uni-app传递一个信号,说明H5已经加载好了,uni-app收到这个信号的时候就把提示框关掉就好了

 message(event){
		// #ifdef APP-PLUS
		if(event.detail.data[0].action=='onReady'){
			uni.hideLoading()
		}
		// #endif  
    }

目前遇到的一些坑就是这些了,还有一些别的功能和方法一般应该没啥问题,就不贴出来了。

补充一点:本来h5加上一些echarts的图表渲染之后,加载就更慢了,但是后来通过webpack,把加载web-view的时间从5秒缩短到了2秒!真不错!哈哈哈哈哈

加油吧,未来仍需努力!!!

  • 12
    点赞
  • 66
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 21
    评论
评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一粒程序米

你的鼓励是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值