网上查了一圈,没有找到符合我内心的描述,所以就算忽略吧,反正优点很多,这不是我们要说的重点,忽略几百字或者几十字总有的
Hybrid网上很多,比较大型的方案还是推荐jsbridge,其最大优势在于方便于扩展,下面文章都是基于jsbridge来说的。
安利两个成熟的库
iOS WebViewJavascriptBridge
Android JsBridge
基本原理
这也不是这篇帖子的重点,上张网上盗来的图,个人看来这个图有些地方有点过期了,回头写jsbridge的原理的时候,再详细来说。
1、获取Native植入对象,后续的调用都是使用的这个对象
/** * 获取客户端的桥接对象 * @returns {Promise<any>} */ setupWebViewJavascriptBridge: function () { return new Promise(resolve => { if (window.WebViewJavascriptBridge) { // 项目正常运行时window上已经挂载了客户端的桥接对象WebViewJavascriptBridge,直接使用对象 return resolve(WebViewJavascriptBridge); } if (window.WVJBCallbacks) { // 已创建了回调队列,直接往回调队列添加回调函数 return window.WVJBCallbacks.push(resolve); } // 页面初始化时在客户端注入桥接对象前定义个回调函数队列,客户端在初始化后会依次调用队列中的函数 window.WVJBCallbacks = [resolve]; // 用隐藏的iframe,来触发url scheme,从而触发WebViewJavascriptBridge的注入 var WVJBIframe = document.createElement("iframe"); WVJBIframe.style.display = "none"; WVJBIframe.src = "wvjbscheme://__BRIDGE_LOADED__"; document.documentElement.appendChild(WVJBIframe); setTimeout(function () { document.documentElement.removeChild(WVJBIframe); }, 0); }) }
2、js调用客户端
/** * h5调用客户端协议 * @param bridgeOpStr 方法名称 * @param paras 调用协议的参数 * @returns {PromiseLike<any | never> | Promise<any | never>} */ callHandler: function (bridgeOpStr, paras) { return this.setupWebViewJavascriptBridge().then(bridge => { return new Promise(resolve => { bridge.callHandler(bridgeOpStr, paras, (response) => { resolve(response); }); }) }); }
3、客户端调用js
/** * 客户端调js * @param bridgeOpStr 方法名 * @returns {PromiseLike<any | never> | Promise<any | never>} */ registerHandler: function (bridgeOpStr) { return this.setupWebViewJavascriptBridge().then(bridge => { return new Promise(resolve => { bridge.registerHandler(bridgeOpStr, (data, responseCallback) => { // data 传过来的数据, responseCallback 回调函数 resolve(data, responseCallback); }); }) }); }
散装的js函数足够可以使我们完成项目了,不过为了更好的使用,我们应该将其封装到一个对象中,然后全局使用这个对象,这样做有几个优点。
1、方便调用。
2、因为jsbridge天生异步,所以我们有时候需要借助队列来控制一些逻辑。
3、版本等函数管理
主要封装内容:
算了吹多了没意思,直接上代码
const dev = process.env.NODE_ENV !== 'production' /** * 业务函数最终返回的是 promise, 其回调结果都是对象, * result 状态: 1 成功, 0 失败; * msg 提示信息。 * data 业务数据。 */ let bridgefunc = { // 当前协议版本 version: -1, /** * 获取当前协议版本,都支持 * @returns {*} */ getAppProtocolVersion() { let jsonData = {}; jsonData.type = "getAppProtocolVersion"; return this.callHandler("phonebridge", jsonData); }, // 初始化版本号 initVersion() { this.getAppProtocolVersion().then(res => { if (res.result == 1) { this.version = res.data; this.consumeNoViersionQueue() } }) }, // 没有版本的时候,交互先放入队列等版本回来再消费 noVersionQueue: [], // 消费没有version队列 consumeNoViersionQueue() { while (this.noVersionQueue.length) { let callback = this.noVersionQueue.shift() callback.call(this, true); } }, // 函数版本和测试支持,如果不包含函数对象,表示都支持。 funcSupportVersion: { scanCode: { version: 1.0, devValue: '1234567890' // 开发环境回调值 }, pageWillShow: { version: 2.5 // 回调场景默认值没意义,立即是不正确的,不写开发环境回到值,就不会触发 } }, // 检查协议是否支持 checkSupport (bridgeOpStr, resolve, callback) { if (dev) { if (this.funcSupportVersion[bridgeOpStr] && this.funcSupportVersion[bridgeOpStr].devValue != undefined) { resolve({ result: 1, msg: '这是开发环境回调', data: this.funcSupportVersion[bridgeOpStr].devValue }) } else { resolve({ result: 0, msg: '开发环境,不支持调用' }) } callback(false); } else if (bridgeOpStr == 'getAppProtocolVersion') { // 获取version直接继续,否则死循环 callback(true); } else if (this.version == -1) { this.initVersion() // 还没有获取到版本号,加入等待队列 this.noVersionQueue.push(callback) } else if (!this.funcSupportVersion[bridgeOpStr] || !this.funcSupportVersion[bridgeOpStr].version || this.version >= this.funcSupportVersion[bridgeOpStr].version) { // 没有设置支持函数支持版本 callback(true); } else { resolve({ result: 0, msg: '当前APP版本过老,不支持该函数,函数名:' + bridgeOpStr }) callback(false); } }, /** * h5调用客户端协议 * @param bridgeOpStr 方法名称 * @param paras 调用协议的参数 * @returns {PromiseLike<any | never> | Promise<any | never>} */ callHandler (bridgeOpStr, paras) { return this.setupWebViewJavascriptBridge().then(bridge => { return new Promise(resolve => { this.checkSupport(bridgeOpStr, resolve, (result) => { if (result) { bridge.callHandler(bridgeOpStr, paras, (response) => { resolve({ result: 1, data: response }); }); } }) }) }); }, /** * 客户端调js * @param bridgeOpStr 方法名 * @returns {PromiseLike<any | never> | Promise<any | never>} */ registerHandler (bridgeOpStr) { return this.setupWebViewJavascriptBridge().then(bridge => { return new Promise(resolve => { this.checkSupport(bridgeOpStr, resolve, (result) => { if (result) { bridge.registerHandler(bridgeOpStr, (data, responseCallback) => { // data 传过来的数据, responseCallback 回调函数 resolve({ result: 1, data: data, responseCallback: responseCallback }); }); } }) }) }); }, /** * 获取客户端的桥接对象 * @returns {Promise<any>} */ setupWebViewJavascriptBridge () { return new Promise(resolve => { if (window.WebViewJavascriptBridge) { // 项目正常运行时window上已经挂载了客户端的桥接对象WebViewJavascriptBridge,直接使用对象 return resolve(WebViewJavascriptBridge); } if (window.WVJBCallbacks) { // 已创建了回调队列,直接往回调队列添加回调函数 return window.WVJBCallbacks.push(resolve); } // 页面初始化时在客户端注入桥接对象前定义个回调函数队列,客户端在初始化后会依次调用队列中的函数 window.WVJBCallbacks = [resolve]; // 用隐藏的iframe,来触发url scheme,从而触发WebViewJavascriptBridge的注入 var WVJBIframe = document.createElement("iframe"); WVJBIframe.style.display = "none"; WVJBIframe.src = "wvjbscheme://__BRIDGE_LOADED__"; document.documentElement.appendChild(WVJBIframe); setTimeout(function () { document.documentElement.removeChild(WVJBIframe); }, 0); }) }, /** * // 扫一扫,版本1.0及之后可以调用 * @returns {*} promise 返回 code */ scanCode() { let jsonData = {}; return this.callHandler("scanCode", jsonData); }, /** * 注册回调函数,页面从其他地方回来了,版本2.5及之后可以调用 * @returns {*} */ pageWillShow() { let jsonData = {}; return this.registerHandler("pageWillShow", jsonData); } }; export default bridgefunc;
使用如下:
bridgefunc.scanCode().then(res => { if (res.result == 1) { console.log('扫码结果:', res.data); } })
原创不易,欢迎留言指正。