uniapp 中webview与h5通信

app 端

ux-webview.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
<template>
<web-view
:ref="webviewRefName"
:src="webViewUrl"
:allow="allow"
:sandbox="sandbox"
@message="message"
@onPostMessage="onPostMessage" />
</template>

<script>
import WebView from "./WebView.js";

export default {
props: {
src: {
type: String
},
allow: {
type: String
},
sandbox: {
type: String
},
UniJsBridgeName: {
type: String,
default: "wv"
}
},
data() {
return {
webView: null,
webViewUrl: "",
webviewRefName: null
};
},
created() {
this.webViewUrl = this.src || this.$Route.query.url || "";
this.webviewRefName =
"webview_" + new Date().getTime() + "_" + Math.ceil(Math.random() * 10);
},
mounted() {
let webviewRef = this.$refs[this.webviewRefName];
this.webView = new WebView(webviewRef, this.UniJsBridgeName);
},
methods: {
message() {
this.$emit("message", { ...arguments });
},
onPostMessage(data) {
let message = data.detail;
if (message) {
let { data, callbackId, handlerName } = message.data[0];
/**
* handlerName为response的时候,这个时候的callbackId为客户端的回调函数ID
* 其他的时候,callbackId为H5的回调函数ID
*/
if (handlerName === "response") {
/**
* 找到对应的回调函数
* 执行完毕后立马删除,否则集合中的东西太多了
*/
let responseCallback = this.webView.responseCallbacks[callbackId];
if (responseCallback) {
responseCallback(data);
delete this.webView.responseCallbacks[callbackId];
}
} else {
let messageHandler = this.webView.messageHandlers[handlerName];
let call = (data) => {
this.webView.call(null, data, null, callbackId);
};

if (messageHandler) {
messageHandler(data, call);
}
}
}
this.$emit("onPostMessage", data);
},
call() {
this.webView.call.apply(this.webView, arguments);
},
regist() {
this.webView.regist.apply(this.webView, arguments);
},
init() {
this.webView.init.apply(this.webView, arguments);
},
send() {
this.webView.send.apply(this.webView, arguments);
}
}
};
</script>
webView.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
class WebView {
messageHandlers = {};
responseCallbacks = {};
uniqueId = 1;
/**
* webView: uni-app 的webview组件
* UniJsBridgeName: 前端挂载在window下的全局方法,全局变量为wv
*/
constructor(webView, UniJsBridgeName = "wv") {
this.webViewRef = webView;
this.UniJsBridgeName = UniJsBridgeName;
}

/**
* 调用前端JS,都需要通过此函数
*
* @param {String} handlerName 前端注册的函数名
* @param {Object} data 给前端的回调的参数
* @param {Function} callback 调用完前端的回调
*/
call(handlerName, data, responseCallback, responseId) {
let messageJSON = {
handlerName, // 前端注册的函数名
responseId, // 前端对应的自己的回调函数
responseData: data // 给前端的参数
};

/**
* 把回调函数存在集合中
* 等待H5来调用
* @param {Object} responseCallback
*/
if (responseCallback) {
let callbackId =
"uni_app_cb_" + this.uniqueId++ + "_" + new Date().getTime();
this.responseCallbacks[callbackId] = responseCallback;
messageJSON.callbackId = callbackId;
}

messageJSON = JSON.stringify(messageJSON);
this.webViewRef.evalJs(
`${this.UniJsBridgeName}._handleMessageFromNative(${messageJSON})`
);
}

/**
* 注册线程 往数组里面添加值,被动的等待着H5调用的
* @param {Object} handlerName
* @param {Object} handler
*/
regist(handlerName, handler) {
this.messageHandlers[handlerName] = handler;
}

/**
* 设置默认处理函数,如果前端用send函数给客户端发消息,就走此函数
* 其实和regist是一样的,只不过可以省略一个参数
*/
init(handler) {
this.messageHandlers["send"] = handler;
}

/**
* 向H5发消息
* @param {Object} message
*/
send(message, responseCallback) {
this.call(null, message, responseCallback);
}
}

export default WebView;

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<template>
<view class="page">
<view class="fromH5Message">
<view class="message">来着H5的消息: {{ fromH5Message }}</view>
</view>
<button type="default" @tap="callH5">调用H5提前注册好的函数</button>
<button type="default" @tap="initH5">调用H5提前注册好的默认处理函数</button>
<ux-webview ref="wv" class="webview" src="http://0.0.0.0:8000" />
</view>
</template>

<script>
export default {
data() {
return {
count: 0,
fromH5Message: ""
};
},
onLoad() {
setTimeout(() => {
/**
* 先注册一个函数给H5调用
*/
this.$refs.wv.regist("nativeShow", (data, cb) => {
this.fromH5Message = JSON.stringify(data);

cb({
data: "nativeShow的消息" + new Date().getTime()
});
});

/**
* 设置一个默认处理函数
*/
this.$refs.wv.init((data, cb) => {
cb({ send: "给H5的" });
});
});
},
methods: {
callH5() {
this.$refs.wv.call(
"test",
{ toH5Data: "给H5的参数" + Date.now() },
(data) => {
this.fromH5Message = JSON.stringify(data);
}
);
},
initH5() {
this.$refs.wv.send({ fff: "1000.013213" }, function (data) {
this.fromH5Message = "send" + JSON.stringify(data);
});
}
}
};
</script>

h5 端

index.nvue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
<template>
<view>
<h1>这个是H5页面</h1>
<div class="fromNative">来着uniApp的消息: {{ msg }}</div>
<div class="btn-box">
<ux-button type="info" @click="handleNative">
调用Uniapp提前注册好的nativeShow方法
</ux-button>
<ux-button type="primary" @click="handleDefault"
>调用Uniapp提前注册好的默认处理函数</ux-button
>
<ux-button type="success" @click="handleToPage">跳转页面</ux-button>
</div>
</view>
</template>

<script>
export default {
data() {
return {
msg: ""
};
},
onLoad() {
/**
* 注册了一个函数,提供给Uni-app调用
*/
wv.regist("test", (data, cb) => {
// data: 是客户端给H5的参数
this.msg = "来着uniApp的消息111:" + JSON.stringify(data);
// 把参数传递给客户端,如果不执行的话,uni-app是无法执行回调的
cb("我是给uni-app的参数" + new Date().getTime());
});

/**
* 在H5端配置一个默认处理函数,提供给uni-app调用
*/
wv.init((data, cb) => {
alert(JSON.stringify(data));

cb({
initData: "给uni-app使用的"
});
});
},

methods: {
handleNative() {
/**
* 触发uniapp中注册好的函数
*/
wv.call(
"nativeShow",
{
nativeShowData: "当前时间:" + new Date().getTime()
},
(res) => {
this.msg = "来着uniApp的消息2222:" + JSON.stringify(res);
}
);
},
handleDefault() {
wv.send({ sendData: "给uni-app的" }, (res) => {
this.msg = "来着uniApp的消息333:" + JSON.stringify(res);
});
},
handleToPage() {
wv.uni.navigateTo({
url: "/pages/about/index"
});
}
}
};
</script>
uni.webview.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
!(function (e, n) {
"object" == typeof exports && "undefined" != typeof module
? (module.exports = n())
: "function" == typeof define && define.amd
? define(n)
: ((e = e || self).uni = 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 uni ? uni : {};
if (!E.navigateTo) for (var b in l) t(l, b) && (E[b] = l[b]);
return (E.webView = l), E;
});
(function () {
if (window.wv) {
throw new Error("wv已经存在了");
}
var uniqueId = 1; // 全局唯一标识,主要用来生产callbackId
var responseCallbacks = {}; // 给客户端发送消息后的回调函数集合
var messageHandlers = {}; // 在H5中注册的函数(就是等待客户端调用的函数)的集合
var receiveMessageQueue = []; // 消息队列,Native给H5发消息的队列,所有的消息都集中在此,依次调用
var wv = {
init: init,
send: send,
call: call,
regist: regist,
uni: { ...uni, ...uni.webView, webView: uni.webView },
_handleMessageFromNative: _handleMessageFromNative
};
// Native调用H5函数必须是要挂载到window下
window.wv = wv;
function init(messageHandler) {
if (wv._messageHandler) {
throw new Error("wv._messageHandler 被调用了两次");
}
wv._messageHandler = messageHandler;
var receiveMessage = receiveMessageQueue;
receiveMessageQueue = null;
for (var i = 0; i < receiveMessage.length; i++) {
_dispatchMessageFromNative(receiveMessage[i]);
}
}
/**
* 发送消息给客户端
* PS:主动发送
* @param data 给客户端的数据
* @param responseCallback 回调函数
*/
function send(data, responseCallback) {
_doSend("send", data, responseCallback);
}
// 调用线程
function call(handlerName, data, responseCallback) {
_doSend(handlerName, data, responseCallback);
}
/**
* 注册一个函数,待客户端调用
* PS:被动等待调用
* @param handlerName
* @param handler: 接收两个参数,data(客户端回复给H5的数据),responseCallback(一个回调函数:调用该函数,客户端才可以执行回调,且可以传参数给客户端)
*/
function regist(handlerName, handler) {
messageHandlers[handlerName] = handler;
}
/**
* 发送给客户端的核心函数,依赖uni-app SDk
* PS:主动发送
* @param handlerName
* @param message
* @param responseCallback: 可接收String和Function,如果为String的话就是给客户端的一个回调ID,这个ID其实本来就是由客户端自己传过来的
* 如果为Function的话就是H5的回调函数
* @private
*/
function _doSend(handlerName, message, responseCallback) {
var callbackId;
if (typeof responseCallback === "string") {
callbackId = responseCallback;
} else if (responseCallback) {
/**
* 把回调函数塞进集合中去,待客户端代码执行完毕就去找到对应的函数开始执行
*/
callbackId = "cb_" + uniqueId++ + "_" + new Date().getTime();
responseCallbacks[callbackId] = responseCallback;
} else {
callbackId = "";
}

const postMes = uni.postMessage || uni.webView.postMessage;
if (!postMes) {
return console.error(
"此接口依赖uni.webview,具体参考:https://uniapp.dcloud.io/component/web-view"
);
}
// 给客户端发送消息
postMes({
data: {
data: message,
callbackId: callbackId,
handlerName: handlerName
}
});
}
function _dispatchMessageFromNative(messageJSON) {
if (!messageJSON) {
return console.warn("客户端没有回复任何消息");
}
try {
/**
* responseId: 客户端回复前端的回调函数的ID
* callbackId: 给客户端自己用的回调ID
* handlerName: 前后端统一约定的一个名字[约定不同的名字对应不同的功能,需要后期加,这里只是封装的一个方法]
* responseData: 客户端给前端的参数
*/
var callbackId = messageJSON.callbackId,
responseId = messageJSON.responseId,
handlerName = messageJSON.handlerName,
responseData = messageJSON.responseData;
var responseCallback = void 0;
if (responseId) {
responseCallbacks[responseId] &&
responseCallbacks[responseId](responseData);
delete responseCallbacks[responseId];
} else {
if (callbackId) {
var callbackResponseId_1 = callbackId;
responseCallback = function (responseData) {
_doSend("response", responseData, callbackResponseId_1);
};
}
var handler = wv._messageHandler;
if (handlerName) {
handler = messageHandlers[handlerName];
}
handler(responseData, responseCallback);
}
} catch (e) {
console.log(e);
}
}
/**
* 提供给客户端调用的函数,
* 约定客户端只能通过此函数发消息给H5
* @param messageJSON
* @private
*/
function _handleMessageFromNative(messageJSON) {
if (receiveMessageQueue) {
receiveMessageQueue.push(messageJSON);
}
_dispatchMessageFromNative(messageJSON);
}
})();