安卓/iOS 可以调用 JS 侧的全局对象来完成通信,那么使用 React/Vue 的时候我们的方法都封装在框架内部,并没有暴露在全局,原生侧调不到框架内部的方法,怎么办?

使用 EventEmitter!

代码

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
import { eventEmmiter } from "./eventEmmiter";

let Bridge = {
// 统一接口请求参数&类型,方便多平台调用
ios: function(event, params, needCallback = true) {
return new Promise(resolve => {
console.log(
`invoke iOS.event[${event}] with params: ${JSON.stringify(params)}`
);
if (!params) params = {};
const callbackEvent = this.getCallbackEvent(event);
if (needCallback) {
eventEmmiter.once(callbackEvent, res => {
console.log(`callback iOS.event[${callbackEvent}] with data: ${res}`);
if (res && typeof res === "string") res = JSON.parse(res);
resolve(res);
});
}
// 字符串化参数
let msg = JSON.stringify({
method: event,
data: params,
callback: callbackEvent
});
console.log(`send iOS msg ${JSON.stringify(msg)}`);
// native 为 wkwebview 注册的对象
webkit.messageHandlers.native.postMessage(msg);
});
},

// 统一接口请求参数&类型,方便多平台调用
android: function(event, params, needCallback = true) {
return new Promise(resolve => {
console.log(
`invoke Android.event[${event}] with params: ${JSON.stringify(params)}`
);
if (!params) params = {};
const callbackEvent = this.getCallbackEvent(event);
if (needCallback) {
eventEmmiter.once(callbackEvent, res => {
console.log(
`callback Android.event[${callbackEvent}] with data: ${res}`
);
if (res && typeof res === "string") res = JSON.parse(res);
resolve(res);
});
}
let msg = JSON.stringify({
method: event,
data: params,
callback: callbackEvent
});
console.log(`send Android msg ${JSON.stringify(msg)}`);
// Android 为安卓侧注册的对象
Android[event](msg);
});
},

// 生成随机事件,防止多次点击重复调用
getCallbackEvent: function(event) {
return (
event + "-" + new Date().getTime() + "-" + (Math.random() * 100).toFixed()
);
}
};

export default Bridge;

JS 向原生请求消息,并接受回调

JS 侧注册 EventEmitter 事件,给原生发消息和当前事件,当原生返回消息的时候,调用当前事件的回调,并将数据返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 根据 URL 约定的字段来判断当前在什么平台
// eg: /detail/123?sitecode=ios
// 调用时根据平台调用 Bridge.ios / Bridge.android
if (inIOSAPP()) {
// 给 iOS 侧发登录请求,iOS侧弹出原生登录页,完成登录后回调
Bridge.ios(LOGIN_EVENT).then(res => {
// save user info
});
} else if (inAndroidAPP()) {
Bridge.android(LOGIN_EVENT).then(res => {
// save user info
});
} else {
// 原生浏览器,弹出登录框
this.toggleLoginModal();
}

原生向 JS 请求消息

JS 侧注册 EventEmitter 事件,原生调用当前事件的回调,并将数据返回。

1
2
3
4
5
componentDidMount() {
eventEmmiter.on(SOME_EVENT, (res) => {
console.log(res)
});
}