1. uni-app中web-view的使用
uni-app中的web-view是一个 web 浏览器组件,可以用来承载网页的容器,uni-app开发的app与web-view实现交互的方式相关简单,应用通过属性@message绑定触发事件,然后在web-view的网页向应用 postMessage 触发并收到消息即可,详细请参考官网:web-view | uni-app官网 (https://uniapp.dcloud.net.cn/component/web-view.html#)主要实现代码参考下图所示。
1.1. uni-app中web-view的使用
1.1.1. app页面
<template>
<web-view :src="url" @message="handleMessage"></web-view>
</template>
<script>
export default {
data() {
return {
url: null //要打开的外部链接
};
},
methods: {
//通过 @message 事件接收外部链接传过来的内容
handleMessage(event) {
if (event.detail.data[0].isClose) {
uni.reLaunch({
url: '/main/main'
});
}
}
}
};
</script>
1.1.2.外部链接H5
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>我是在app里打开的页面</title>
<script src="./jquery-3.4.1.js" type="text/javascript" charset="utf-8"></script>
<!-- 微信JS-SDK 兼容微信小程序 引入此文件 -->
<script type="text/javascript" src="https://res.wx.qq.com/open/js/jweixin-1.4.0.js"></script>
<!-- uni-app SDK -->
<script type="text/javascript" src="https://js.cdn.aliyun.dcloud.net.cn/dev/uni-app/uni.webview.1.5.2.js"></script>
</head>
<body>
<div style="width: 100%;height: 100%;position: absolute;left: 0;top: 0;right: 0;bottom: 0;">
<button id="btn">按钮</button>
</div>
</body>
<script>
document.addEventListener('UniAppJSBridgeReady', function() {
console.log("加载完成,可以使用uni相关接口");
});
$("#btn").click(function() {
uni.postMessage({
data: {
isClose: true
}
})
uni.navigateBack();
})
</script>
</html>
1.1.3.问题
但是,以上方法只适合于APP,在H5环境中是不支持的,官方说明如下:
那么,在uni-app如何实现在H5环境中与web-view中的网页交互通讯呢,按照官方的说法,使用window.postMessage方式实现!
1.2. window.postMessage
关于window.postMessage的通讯原理,请参考官方档window.postMessage - Web API 接口参考 | MDN(https://developer.mozilla.org/zh-CN/docs/Web/API/Window/postMessage)
1.2.1. 应用端代码
onLoad: function() {
window.addEventListener('message', function(e) { // 监听 message 事件
console.log(e.origin);
console.log("从" + e.origin + "收到消息: " + e.data);
});
}
1.2.2. 网页端代码(H5)
//向uniapp发送信息
function sendMsgToUniapp(value) {
parent.postMessage(value, "*");
}
说明:uni-app的web-view,在H5环境中其实就是通过iframe来实现的,因此在iframe中的页面可直接通过jquery中的parent来获取父页面对象,并通过parent.postMessage的方式传递消息。
1.3. 完整代码
1.3.1. pageWeb.vue(uiapp)
<template>
<view>
<web-view @message="message"
:src="webViewUrl"></web-view>
</view>
</template>
<script>
import pageUtil from '../../../utils/pageUtil.js';
export default {
data() {
return {
webViewUrl: '/hybrid/html/html/pageUniWeb.html?data=您好',
//webViewUrl: 'http://120.224.9.76:18080/app/news/html/pageUniWeb.html?data=efhejr',
wv: ''
}
},
onReady() {
pageUtil.setTitleBar('业务协同')
},
onLoad() {
var that = this;
// #ifdef APP-PLUS
//此对象相当于html5plus里的plus.webview.currentWebview()。
// 在uni-app里vue页面直接使用plus.webview.currentWebview()无效
let currentWebview = this.$scope.$getAppWebview()
setTimeout(() => {
this.wv = currentWebview.children()[0]
}, 300)
// #endif
try {
//信息交互(H5浏览器使用),监听 message 事件
window.addEventListener('message',
function (e) {
console.log("从" + e.origin, "收到消息: ", e);
var resData = e.data.data.arg;
if (resData.myType) {
uni.showToast({
icon: 'none',
title: "uniap获取H5发送数据:" + JSON.stringify(resData)
});
}
});
} catch (e) {
}
},
methods: {
//信息交互(app真机使用)
message(e) {
var that = this
var resData = e.detail.data;
console.log("uniap获取H5发送数据:", e)
uni.showToast({
icon: 'none',
title: "uniap获取H5发送数据:" + JSON.stringify(resData)
});
var myObj = {}
myObj.msg="我是uniapp"
//这里必须序列化!!!
myObj = JSON.stringify(myObj)
this.wv.evalJS(`postJS(${myObj})`);
},
}
}
</script>
<style>
</style>
1.3.2. pageUniWeb.html(H5)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>web-view</title>
<script type="text/javascript" src="../static/js/init-rem.js"></script>
<script type="text/javascript" src="../static/js/jquery-3.1.1.min.js"></script>
<script type="text/javascript" src="../static/helper/init-helper.js"></script>
<!-- <script src="https://js.cdn.aliyun.dcloud.net.cn/dev/uni-app/uni.webview.1.5.2.js"-->
<!-- type="text/javascript" charset="utf-8"></script>-->
<script type="text/javascript" src="../static/helper/web-view-custom.js"></script>
<script type="text/javascript" src="../static/js/vconsole.min.js"></script>
<script type="text/javascript">
var vc = new VConsole()
</script>
<style>
.btn-layout {
display: flex;
flex-direction: column;
align-items: center;
}
.btn-item {
display: inline;
font-size: 0.32rem;
color: white;
text-align: center;
padding: 0.2rem 0.5rem;
border-radius: 0.3rem;
margin-top: 0.5rem;
background-color: #1AAD19;
}
.content {
display: inline;
font-size: 0.32rem;
color: black;
text-align: center;
padding: 0.2rem 0.5rem;
border-radius: 0.3rem;
margin-top: 0.5rem;
}
</style>
</head>
<body>
<div class="btn-layout">
<span id="h5ToUniappId" class="btn-item">H5发送数据到uniapp</span>
<div id="contentId" class="content"></div>
<div id="content2Id" class="content"></div>
</div>
</body>
<script type="text/javascript">
var itemData = getParamByKey('data');
console.log("获取uniapp链接传递数据:", itemData)
$(function () {
$('#contentId').html("获取uniapp链接传递数据:"+itemData)
});
/**
* 获取uniapp传递数据方法一
* 定义全局方法,接收来自应用的信息
*/
// window.postJS = (msg) => {
// console.log('来自应用的消息', msg)
// }
/**
* 获取uniapp传递数据方法二
*/
function postJS(e) {
console.log("获取uniapp传递数据:", e)
$('#content2Id').html("获取uniapp传递数据:"+JSON.stringify(e))
}
document.addEventListener('UniAppJSBridgeReady',
function () {
webUni.webView.getEnv(function (res) {
console.log('当前环境:' + JSON.stringify(res));
});
document.querySelector('#h5ToUniappId')
.addEventListener('click', function (evt) {
// webUni.webView.navigateBack();
//向uniapp传值
//方法一
//window.parent.postMessage("", '*')
//parent.postMessage("sdcec", "*");
//方法二
webUni.postMessage({
data: {
action: 'message',
msg: '我是H5',
myType: 'typeH5',
}
});
});
});
</script>
</html>
1.3.3. web-view-custom.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
}));
1.3.4. init-helper.js
var u = navigator.userAgent;
// 是否为ios设备
var isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/);
// 是否为PC端
var isPC = typeof window.orientation === 'undefined';
// 是否为android端
var isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > -1;
// 是否为微信端,此处不能为===,因为match的结果可能是null或数组
var isWx = u.toLowerCase().match(/MicroMessenger/i) == 'micromessenger';
var strConstant = {
/**
* 业务协同
*/
//待办
eventWaitDeal: '我的待办',
//已办
eventHasDeal: '我的已办',
//考勤打卡
workCheckSign: '考勤打卡',
//添加
pageTypeAdd: 'add',
//编辑
pageTypeEdit: 'edit',
/**
* 跳转类型
*/
jumpTypeMapLoc: 'jumpTypeMapLoc',
}
/**
* @desc 返回上一页
*/
function backPage() {
//单击Android左上角返回键执行该方法
history.pushState(null, null, location.href);
window.addEventListener('popstate',
function (event) {
if (isAndroid) {
if (window.androidJsHook) {
history.pushState(null, null, location.href);
window.androidJsHook.htmlFinish();
} else {
history.go(-1)
}
} else if (isiOS) {
if (window.webkit) {
history.pushState(null, null, location.href);
window.webkit.postMessage(null);
} else {
history.go(-1)
}
} else {
history.go(-1)
}
});
};
/**
* 跳换页面
*/
function openPage(webUrl, webTitle, webParams) {
if (window.androidJsHook) {
window.androidJsHook
.startForResult(webUrl, webTitle, webParams);
} else {
mui.openWindow({
url: webUrl + webParams,
id: webUrl,
styles: {
top: '0px',
bottom: '0px'
},
waiting: {
autoShow: false, //自动显示等待框,默认为true
}
});
}
}
/**
* @description 通过传入key值,得到页面key的初始化传值
* plus情况为plus.webview.currentWebview.***
* h5情况为 window.location.href 中的参数的值
* @param {String} key
*/
function getParamByKey(key) {
if (!key) {
return null;
}
return GetQueryString(key) || getExtraDataByKey(key);
};
function GetQueryString(name) {
// var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
// var r = window.location.search.substr(1).match(reg);
// if (r != null) return unescape(r[2]); return null;
/*中文不乱码*/
return decodeURIComponent((new RegExp('[?|&]' + name
+ '=' + '([^&;]+?)(&|#|;|$)').exec(location.search) || [, ""])[1]
.replace(/\+/g, '%20')) || null;
}
/**
* @description 通过传入key值,得到页面key的初始化传值
* plus情况为plus.webview.currentWebview.***
* h5情况为 window.location.href 中的参数的值
* @param {String} key
*/
function getExtraDataByKey(key) {
if (!key) {
return null;
}
var value = null;
//h5
value = getUrlParamsValue(window.location.href, key);
if (value === 'undefined') {
value = null;
}
return value;
};
/**
* 删掉遮罩
*/
function removeDrop() {
$('.mui-popup-backdrop').remove();
}
/**
* 合并json对象
* @param {Object} target 第一个json对象,同时也是合并后的json对象
* @param {Object} source 第二个json对象
*/
function extend(target, source) {
for (var obj in source) {
target[obj] = source[obj];
}
return target;
}
/**
* 转为json数据格式
* @param {Object} idStr form表单的id
*/
function transformToJson(idStr) {
var formData = $("#" + idStr).serializeArray();
var obj = {};
var j = 1;
for (var i in formData) {
obj[formData[i].name] = formData[i]['value'];
//伪数组length字段,用于遍历改造json对象
obj['length'] = j;
j++
}
return obj;
}
/**
* 通用方法封装处理
* commonUtils.isEmpty(jsonParam.countyLabel
*/
var commonUtils = {
// 判断字符串是否为空
isEmpty: function (value) {
if (value == null || this.trim(value) == "" || value == undefined) {
return true;
}
return false;
},
// 判断一个字符串是否为非空串
isNotEmpty: function (value) {
return !commonUtils.isEmpty(value);
},
// 空格截取
trim: function (value) {
if (value == null) {
return "";
}
return value.toString().replace(/(^\s*)|(\s*$)|\r|\n/g, "");
},
// 判断字符串是否是以start开头
startWith: function (value, start) {
var reg = new RegExp("^" + start);
return reg.test(value)
}
};
/**
* 重置form表单
*/
function resetForm(resetArr) {
var textInputArr = $("input[type='text']")
var numberInput = $("input[type='number']")
var domId = null
for (var i = 0; i < textInputArr.length; i++) {
domId = $(textInputArr[i]).attr('id')
var isContain = false
resetArr.forEach(function (item, index) {
if (domId == item) {
isContain = true
}
})
if (!isContain) {
$(textInputArr[i]).val("")
}
}
for (var i = 0; i < numberInput.length; i++) {
domId = $(numberInput[i]).attr('id')
var isContain = false
resetArr.forEach(function (item, index) {
if (domId == item) {
isContain = true
}
})
if (!isContain) {
$(numberInput[i]).val("")
}
}
}
/**
* 更新三级联动字典
* @param {Object} name
* @param {Object} nameMS
* @author zzs
*/
function updateDictLevel3(name, nameMsArr, urlStr) {
var queryParam = {};
if (isNotNull(name)) {
queryParam.name = name;
}
getNetData(urlStr, queryParam, function (result) {
if (result.code == '200') {
var nameA = [];
if (result.data && result.data.length == 0) {
nameA = [{
"id": "",
"value": "请选择"
}];
} else {
$.each(result.data, function (i, item) {
var flag = {};
flag.id = item.value;
flag.value = item.label;
//二级
var childsA = [];
if (item.children && item.children.length > 0) {
$.each(item.children, function (j, childsItem) {
var childsFlag = {};
childsFlag.id = childsItem.value;
childsFlag.value = childsItem.label;
//三级
var childsB = [];
if (childsItem.children && childsItem.children.length > 0) {
$.each(childsItem.children, function (k, grandsonItem) {
var grandsonFlag = {};
grandsonFlag.id = grandsonItem.value;
grandsonFlag.value = grandsonItem.label;
childsB.push(grandsonFlag);
});
}
childsFlag.childs = childsB;
childsA.push(childsFlag);
});
}
flag.childs = childsA;
nameA.push(flag);
});
}
if (nameMsArr != null && nameMsArr.length > 0) {
nameMsArr.forEach(function (nameMs, index) {
nameMs.updateWheels(nameA);
})
}
}
})
}