H5在WebView上开发小结

背景

来自我司业务方要求,需开发一款APP。但由于时间限制,只能采取套壳app方式,即原生app内嵌webview展示前端页面。本文主要记述JavaScript与原生app间通信,以及内嵌webview开发时,前端方面可能踩的一些坑。

技术架构

前端:vue+vuex+vue-router+webpack全家桶开发
后端:Node(express框架)简单转发接口至java-真后端接口。

js与原生通信

采用jsBridge技术和原生APP通信
android 传送门 和ios 传送门,因为两个平台初始化方式不同,因此在开发过程中,需针对每个平台做对应操作。 具体做法

  1. 按照库要求,声明好初始化函数
//android
function connectWebViewJavascriptBridge{if (window.WebViewJavascriptBridge) {//do your work here} else {document.addEventListener('WebViewJavascriptBridgeReady', function() {//do your work here},false);}
}
//ios
setupWebViewJavascriptBridge(function(bridge) {/* Initialize your app here */bridge.registerHandler('JS Echo', function(data, responseCallback) {console.log("JS Echo called with:", data)responseCallback(data)})bridge.callHandler('ObjC Echo', {'key':'value'}, function responseCallback(responseData) {console.log("JS received response:", responseData)})
})
复制代码
  1. 初始化,得到bride对象。则可调用原生app已定义方法或注册js方法供原生调用
setupWebViewJavascriptBridge(function(bridge) {/* Initialize your app here */bridge.registerHandler('JS Echo', function(data, responseCallback) {console.log("JS Echo called with:", data)responseCallback(data) // })bridge.callHandler('ObjC Echo', {'key':'value'}, function responseCallback(responseData) {console.log("JS received response:", responseData)})
})
复制代码

Tips:

  • Android 与 IOS初化方式不同,需要判断平台后再进行调用。另外Android初始化时,需额外引入一些方法。
  • 调用Android定义方法时,返回值只能为字符串。而IOS可为JSON对象。需要在callHandler时,对返回值进行封装处理或统一规定好数据格式。
  • 完整业务代码文末给出

踩坑

  1. 调用bridge属性方法registerHandler,callHandler,在回调函数内处理页面逻辑时,最好避免使用this
  2. vue组件下,在registerHandler,callHandler回调函数内使用vue实例时,无法获取实例对象。正确做法是在回调函数内调用window对象下方法,再通过该方法去使用vue实例对象。
//vue 组件
mounted(){window['handleServicePushMessage'] = (res) => {vm.handleServicePushMessage(res)};bridge.registerHandler("servicePushMessage", function (data, responseCallback) {handleServicePushMessage(data)responseCallback(data) //可传值到App})
}
复制代码
  1. 桌面推送消息点击跳转至App内详情情况下,js注册方法供调用时,可能会引起重复调用的问题。故在方法内需做好重复调用判断
  2. IOS-12.0版本下,在有输入框的页面,输入时软键盘会顶起webview,当失去焦点时,webview不会自动回弹。需调用APP做处理拉回界面。
//解决ios 12版本 ui不自动回拉问题
document.addEventListener('focusout', function (event) {let curTarget = event.target || event.srcElement;let isInput= ['input', 'textarea'];//处理页面连续点击都为输入框的情况let curTargetTagName= curTarget.tagName.toLowerCase();if (isInput.includes(curTargetTagName)) {//事件处理//延迟获取activeElement再进行判断setTimeout(function () {let activeEle = document.activeElement;let activeEleTagName= activeEle.tagName.toLowerCase();if (!isInput.includes(activeEleTagName)) {// console.log(document.activeElement.tagName);//调用app桥拉回webviewperformMethod('scrollTotop', null);}}, 200);}
}, true);
复制代码

5.当js调用app不存在的桥时,无法捕获异常,页面不会报错
6.导航栏显示问题,由于项目时间紧迫,并且app开发人员不承载太多开发任务,所以路由控制放在前端处理。此时就有导航栏电池时间栏的适配问题。本项目采用顶部下调20PX处理,电池时间栏字体颜色的控制也是通过桥调用来设置;另外iPhone X适配另外处理。
7.当app加载完网页时,js立即调用原生方法桥时,可能出现原生方法桥未注册完情况。故特殊情况需延迟调用桥操作。

完整代码

/*判断平台*/
function (window) {window.device = {};var ua = navigator.userAgent;var android = ua.match(/(Android);?[\s\/]+([\d.]+)?/);var ipad = ua.match(/(iPad).*OS\s([\d_]+)/);var ipod = ua.match(/(iPod)(.*OS\s([\d_]+))?/);var iphone = !ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/);device.ios = device.android = device.iphone = device.ipad = device.androidChrome = false;if (android) {device.os = 'android';device.osVersion = android[2];device.android = true;device.androidChrome = ua.toLowerCase().indexOf('chrome') >= 0}if (ipad || iphone || ipod) {device.os = 'ios';device.ios = true}
}(window)
/*引入Android需要的初始化,IOS不执行,如执行IOS端桥调用会受影响*/
(function () {if (window.WebViewJavascriptBridge || device.ios) {return false;}var messagingIframe;var sendMessageQueue = [];var receiveMessageQueue = [];var messageHandlers = {};var CUSTOM_PROTOCOL_SCHEME = 'yy';var QUEUE_HAS_MESSAGE = '__QUEUE_MESSAGE__/';var responseCallbacks = {};var uniqueId = 1;function _createQueueReadyIframe(doc) {messagingIframe = doc.createElement('iframe');messagingIframe.style.display = 'none';doc.documentElement.appendChild(messagingIframe);}/*set default messageHandler*/function init(messageHandler) {if (WebViewJavascriptBridge._messageHandler) {throw new Error('WebViewJavascriptBridge.init called twice');}WebViewJavascriptBridge._messageHandler = messageHandler;var receivedMessages = receiveMessageQueue;receiveMessageQueue = null;for (var i = 0; i < receivedMessages.length; i++) {_dispatchMessageFromNative(receivedMessages[i]);}}function send(data, responseCallback) {_doSend({data: data}, responseCallback);}function registerHandler(handlerName, handler) {messageHandlers[handlerName] = handler;}function callHandler(handlerName, data, responseCallback) {_doSend({handlerName: handlerName, data: data}, responseCallback);}/*sendMessage add message, 触发native处理 sendMessage*/function _doSend(message, responseCallback) {if (responseCallback) {var callbackId = 'cb_' + (uniqueId++) + '_' + new Date().getTime();responseCallbacks[callbackId] = responseCallback;message.callbackId = callbackId;}sendMessageQueue.push(message);messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;}/* 提供给native调用,该函数作用:获取sendMessageQueue返回给native,由于android不能直接获取返回的内容,所以使用url shouldOverrideUrlLoading 的方式返回内容*/function _fetchQueue() {var messageQueueString = JSON.stringify(sendMessageQueue);sendMessageQueue = [];/*android can't read directly the return data, so we can reload iframe src to communicate with java*/messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://return/_fetchQueue/' + encodeURIComponent(messageQueueString);}/*提供给native使用,*/function _dispatchMessageFromNative(messageJSON) {setTimeout(function () {var message = JSON.parse(messageJSON);var responseCallback;/*java call finished, now need to call js callback function*/if (message.responseId) {responseCallback = responseCallbacks[message.responseId];if (!responseCallback) {return;}responseCallback(message.responseData);delete responseCallbacks[message.responseId];} else {/*直接发送*/if (message.callbackId) {var callbackResponseId = message.callbackId;responseCallback = function (responseData) {_doSend({responseId: callbackResponseId, responseData: responseData});};}var handler = WebViewJavascriptBridge._messageHandler;if (message.handlerName) {handler = messageHandlers[message.handlerName];}/*查找指定handler*/try {handler(message.data, responseCallback);} catch (exception) {if (typeof console != 'undefined') {console.log("WebViewJavascriptBridge: WARNING: javascript handler threw.", message, exception);}}}});}/*提供给native调用,receiveMessageQueue 在会在页面加载完后赋值为null,所以*/function _handleMessageFromNative(messageJSON) {if (receiveMessageQueue && receiveMessageQueue.length > 0) {receiveMessageQueue.push(messageJSON);} else {_dispatchMessageFromNative(messageJSON);}}var WebViewJavascriptBridge = window.WebViewJavascriptBridge = {init: init,send: send,registerHandler: registerHandler,callHandler: callHandler,_fetchQueue: _fetchQueue,_handleMessageFromNative: _handleMessageFromNative};var doc = document;_createQueueReadyIframe(doc);var readyEvent = doc.createEvent('Events');readyEvent.initEvent('WebViewJavascriptBridgeReady');readyEvent.bridge = WebViewJavascriptBridge;doc.dispatchEvent(readyEvent);
})();
/*Android端初始化函数*/
function connectWebViewJavascriptBridge(callback) {if (window.WebViewJavascriptBridge) {callback(WebViewJavascriptBridge)} else {document.addEventListener('WebViewJavascriptBridgeReady', function () {callback(WebViewJavascriptBridge)}, false);}
}
/*IOS端初始化函数*/
function setupWebViewJavascriptBridge(callback) {if (window.WebViewJavascriptBridge) {return callback(WebViewJavascriptBridge)} else {}if (window.WVJBCallbacks) {return window.WVJBCallbacks.push(callback)}window.WVJBCallbacks = [callback];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)
}
if(device.ios){setupWebViewJavascriptBridge(function(bridge){/*挂载上全局对象*/window.BRIDGE= brige;})
}
if(device.android){connectWebViewJavascriptBridge(function(bridge){/*挂载上全局对象*/window.BRIDGE= brige;})
}复制代码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/280048.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C#的?和??

1.&#xff1f;&#xff1f; 为了实现Nullable数据类型转换成non-Nullable类型数据&#xff0c;才有的一个操作符&#xff1b; 意义&#xff1a;一变量取值&#xff0c;取符号左边的值&#xff0c;若左边为null&#xff0c;那么取赋值&#xff1f;&#xff1f;右边的&#xff1…

odoo 自定义视图_如何使用Windows的五个模板自定义文件夹视图

odoo 自定义视图If you’re particular about how Windows displays the contents of your folders, you can cut your customization time down considerably by taking advantage of File Explorer’s five built-in folder templates. 如果您特别想知道Windows如何显示文件夹…

C#之ILC和C++的CLR前者更快?

楔子ILC是C#写的&#xff0c;CLR是C。.Net 7中&#xff0c;为何微软执意用一个托管的模型去尝试取代非托管框架呢&#xff1f;至少native code方面它是这么做的这个问题一直萦绕脑海。非托管和托管十年前出版的那本久负盛名的《CLR via C#》至今都是不可或缺的存在&#xff0c;…

历史

python的历史 kfsaldkfsdf fdskfdsa fdsjkafsjda fdshkfjsdja View Codefjdskaffdsjkaffdsjakflsad;fjdsklaf 转载于:https://www.cnblogs.com/jin-xin/articles/10448286.html

typescript+react+antd基础环境搭建

typescriptreactantd基础环境搭建&#xff08;包含样式定制&#xff09; tsconfig.json 配置 // 具体配置可以看上面的链接 这里module moduleResolution的配置都会影响到antd的显示 // allowSyntheticDefaultImports 是antd官网给的配置 必须加上 {"compilerOptions&quo…

最小生成树Prim算法和Kruskal算法

https://www.cnblogs.com/JoshuaMK/p/prim_kruskal.html 转载于:https://www.cnblogs.com/DixinFan/p/9225105.html

如何重新打开Windows防火墙提示?

If you are setting up a new program that needs network access, but are not paying close enough attention, you might end up accidentally causing Windows firewall to block the program. How do you fix such a mistake? Today’s SuperUser Q&A post helps a f…

判断字符串出现次数最多的字符 及 次数

分析 题目的意思大致就是找出每个字符出现的次数&#xff0c;然后比较大小。那么每个字符都应该对应它出现的次数。既然是一一对应的&#xff0c;那我们就想到用对象的key和value来储存字符和其出现的次数。具体做法 新建一个空对象obj 遍历给定的字符串接下来就是最重要的 把字…

AI x 量化:华尔街老司机解密智能投资正确姿势

随着中国经济的腾飞&#xff0c;中产阶级的崛起&#xff0c;投资管理逐渐步入寻常百姓家。 值得注意的是&#xff0c;在十年前“无财可理”问题解决后&#xff0c;另一个矛盾愈发凸显——层次不齐的投资素质。据wind数据统计&#xff0c;2004年至2015年12年间&#xff0c;只有3…

如何远程调试 MAUI blazor / Blazor Hybrid

我们知道浏览器模式下 Blazor 可以使用 F12 打开开发工具,调试js查看页面元素,那当 Maui Blazor 提示烦人的 an unhandled error has occurred 该怎么进行调试呢?1. VS 运行工程于 Debug 模式下,只要 BlazorWebview 控件处于焦点,直接按F12就可以打开开发工具了. 没有焦点就鼠…

笔记本触摸键盘驱动自动禁用_如何为iPad的蓝牙键盘禁用自动更正

笔记本触摸键盘驱动自动禁用The take-for-granted features we enjoy when using an on-screen keyboard—like auto-corrections and auto-capitalization–quickly become a hindrance if you’re using a physical keyboard with your iOS device. Let’s look at how to qu…

发票的作用

目录 发票上的两个章&#xff1a;税种&#xff1a;发票的作用&#xff1a;征税方式&#xff1a;发票限额&#xff1a;参考链接发票上的两个章&#xff1a; 税务局的发票监制章商家的发票专用章税种&#xff1a; 增值税&#xff1a;商家在卖东西时为获利&#xff0c;而提高价格的…

opencv-原图基础上添加指定颜色

前言 项目中需要将某些区域使用不同的颜色表示出来&#xff0c;同时能够看到原图作为底色。 代码 #include "opencv2/highgui/highgui.hpp" #include <opencv2/imgproc.hpp> #include <iostream> using namespace cv;int main() {Mat image imread( &q…

微软发布Azure Application Insights for Node.js 1.0版本

在北美举行的Node.js交互大会上&#xff0c;微软发布了用于Node.js的Application Insights SDK。\\来自微软JavaScript平台和工具部门的高级经理Arunesh Chandra在博客上发布了这一消息&#xff0c;他说&#xff0c;微软“希望能够提升开发者在Azure上构建和运行Node.js应用程序…

正则表达式应用:实现一个简单的计算器

实现一个简单的计算器&#xff0c;代码如下&#xff1a; 下面的函数用来检验数学表达式的合规性&#xff0c;当然此处只实现两个检验&#xff1a;(1)括号应该闭合 (2)不能出现字母 def check_expression(str):check_result Trueif str.count(() ! str.count()):print(表达式有…

软考复盘:我的一些复习经验分享

大家好&#xff0c;我是Edison。最近全身乏力头疼&#xff0c;38.5度高烧&#xff0c;好在症状较轻&#xff0c;经过一天躺平加吃了芬必得&#xff08;简直神药&#xff09;后&#xff0c;退烧了&#xff0c;也不乏力了&#xff0c;也就趁娃娃睡觉时间跟大家分享一下软考的复习…

自定义注解在拦截器中为空_如何在Android中为特定联系人设置自定义铃声

自定义注解在拦截器中为空Everyone likes to know who’s calling before they actually pick up the phone, and the easiest way to achieve that is with custom ringtones for specific callers. That way, when your phone starts blasting “Cherry Pie,” you know it’…

对象的成员的初始化

变量类型&#xff1a; 1. 内置基本类型: int, char, bool, 2. 复合类型 &#xff08;compound type&#xff09;: 指针、引用、数组 3. 类类型&#xff1a; struct, class (string,vector等) 定义变量时&#xff1a; 一&#xff1a;进行“初始化”&#xff1a;可分为 ①指…

Golang面向API编程-interface(接口)

Golang面向API编程-interface&#xff08;接口&#xff09; 作者&#xff1a;尹正杰 版权声明&#xff1a;原创作品&#xff0c;谢绝转载&#xff01;否则将追究法律责任。 Golang并不是一种典型的面向对象编程&#xff08;Object Oriented Programming&#xff0c;OOP&#xf…

Linux学习_菜鸟教程_3

我是在UBANTO上运行Linux的&#xff0c;开机启动时按下shift或者Esc都不能进入到grub,没有百度到可靠的教程。 暂时先这样吧。免得我把系统搞坏了&#xff0c;先学点实用的知识~~ Next Chapter转载于:https://www.cnblogs.com/sggggr/p/9233627.html