H5页面被webview承载,须要实现 js 和 native 的通讯,经常使用的功能如图:html
例如:前端
// 打开新容器承载H5
this.state.jsBridge.openWebView({
'action': 'https://XXX.XXX.com',
'login': true
})复制代码
// 销毁webview
this.state.jsBridge.quitAction()复制代码
// 控制客户端顶部back按钮-触发回调函数
this.state.jsBridge.handleBackAction("openCancelPayModal", window.openCancelPayModal, true);复制代码
因此整个通讯流程就像是这样android
客户端会将jsBridge对象注入到window对象下,当调用jsBridge触发(例如获取登陆态)事件方法都会被客户端特有的拦截器所拦截并处理。ios
当js触发jsBridge对象触发某一个方法,其实都会走到客户端的拦截器里面而后native响应,这个过程就是在向客户端发消息。而客户端回调前端callback函数而且将数据返回其实就是在向H5页面发消息。那基于这种消息发送机制,能够将通讯模型进一步优化。git
优势:github
1.前端和客户端不须要过多维护jsBridge方法,只须要维护功能短链,例如:web
// 打开新容器,而且要求用户已登陆
this.state.jsBridge.emit({
name:'page1Event',
action: 'XXX/openWebView',
globle: false,
params: {
login: true
}
})复制代码
其中action就是指的功能短链,并且emit方法就是通用发起消息的bridge。json
而用于接收返回消息的函数,在老通讯模型中是须要经过callback的形式传递。新的通讯模型再也不须要繁琐的callback传递,而是使用通用的function 来接收,例如:小程序
//通用的顶层方法接收客户端消息
window.onJsBridgeEvent = function (json) { console.log(json) }复制代码
ps:globle是用来处理全局消息,要避免性能浪费要默认为不开启。开启globle须要针对页面name名称进行处理,须要页面事件名称保证惟一性(例如:A页面配置在tab,在其余tab内还可以再次打开A页面,此时A页面有两个,若是页面事件名称一致会致使全局消息被两个A页面处理,可是咱们这时候只想要消息被置于容器顶层的A页面单独接受)微信小程序
2.知足更多的交互场景
webview3中发送消息通知客户端销毁webview2,那么基于消息发送的通讯模型就可以实现。
当前打开的全部webview都能接收客户端全局消息,例如:退登,集体销毁等功能的实现。
刚才有提到jsBridge注入,客户端拦截器。
android 调用 js 代码的 方法:
1.经过webview的loadurl
2.经过webview的evaluateJavascript
jsBridge注入和拦截器
1.经过WebView的addJavascriptInterface()进行对象映射
2.经过 WebViewClient 的shouldOverrideUrlLoading ()方法回调拦截 url
3.经过 WebChromeClient的onJsAlert()、onJsConfirm()、onJsPrompt()方法回调拦截JS对话框alert()、confirm()、prompt()消息
使用 WebViewJavaScriptBridge
function setupWebViewJavascriptBridge(callback) {
if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }
if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }
window.WVJBCallbacks = [callback];
var WVJBIframe = document.createElement('iframe');
WVJBIframe.style.display = 'none';
WVJBIframe.src = 'https://__bridge_loaded__';
document.documentElement.appendChild(WVJBIframe);
setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)
}复制代码
为了搭配理解这段代码的做用,能够点击这里
WebViewJavascriptBridge是ios提供的bridge对象,可是在最开始容器层初始化的时候是不提供的,因此代码会继续向下进行,直到 WVJBCallbacks(储存须要回调的函数) 和 wvjbscheme://__BRIDGE_LOADED__执行。
wvjbscheme://__BRIDGE_LOADED__这个协议的目的就是用来创WebViewJavascriptBridge,相关截图以下:
到这里jsbridge对象就算是提供给了前端使用。
接下来是调用ios能力,例如调用客户端实名认证:
this.state.jsBridge.callHandler("realNameAuthentication");复制代码
callHandler为jsbridge里面的一个对象,callHandler下的_doSend方法用来通知给客户端进行处理,相关截图以下:
这里的 messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;为:
messagingIframe.src = https + '://' + "__wvjb_queue_message__"; 这里的 __wvjb_queue_message__做用为触发ios的拦截器来进行处理。
本质上,最终仍是被拦截器所拦截,可是ios的UI webview和WK webview拦截器会不一样:
UI webview : shouldStartLoadWithRequest
WK webview :decidePolicyForNavigationAction
( 摘自:美团技术团队 tech.meituan.com/2017/06/09/… )
从用户使用的过程,大体以下:
1.打开了一个新的窗口
2.页面白屏
3.页面基本骨架渲染出来,可是没有数据
4.数据获取完成,页面总体渲染结束
慢的一部分缘由:webview去加载url并不像是 浏览器 加载url的过程,webview存在一个初始化的过程。
为了提高init时间,经常使用作法是:
app启动时初始化一个隐藏的webview等待使用,当用户点击须要加载URL,直接使用这个webview 来加载,从而减小webview init 初始化时间。弊端就是带来了额外的内存开销。
为了提高H5加载速度,会使用到webview缓存模式。
以安卓为例:
LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据,
LOAD_DEFAULT:根据cache-control决定是否从网络上取数据,
LOAD_CACHE_NORMAL:API level 17中已经废弃, 从API level 11开始做用同- - LOAD_DEFAULT模式,
LOAD_NO_CACHE: 不使用缓存,只从网络获取数据,
LOAD_CACHE_ELSE_NETWORK,只要本地有,不管是否过时,或者no-cache,都使用缓存中的数据。
若是一个页面的cache-control为no-cache,在模式LOAD_DEFAULT下,不管如何都会从网络上取数据,若是没有网络,就会出现错误页面;在LOAD_CACHE_ELSE_NETWORK模式下,不管是否有网络,只要本地有缓存,都使用缓存。本地没有缓存时才从网络上获取。若是一个页面的cache-control为max-age=60,在两种模式下都使用本地缓存数据。
提高H5在webview中的渲染速度,只是前端支持是不够的,还须要客户端采用合理的缓存模式,详细介绍点击这里。
渲染和逻辑不在同一个环境中执行,逻辑层在纯js环境中,渲染层交给了webview,因此wxml和wxss是在渲染层,这两个线程的通讯会经由微信客户端作中转,逻辑层发送网络请求也经由Native转发。
在安卓则是往 WebView 的 window 对象注入一个原生方法,最终会封装成 WeiXinJSBridge 这样一个兼容层,主要提供了调用(invoke)和监听(on)这两种方法。开发者插入一个原生组件,通常而言,组件运行的时候被插入到 DOM 树中,会调用客户端接口,通知客户端在哪一个位置渲染一块原生界面。在后续开发者更新组件属性时,一样地,也会调用客户端提供的更新接口来更新原生界面的某些部分。
微信提供的js执行环境,由于对控件进行了自定义,所以这个沙箱环境不能有浏览器的接口,只提供js执行环境。
js执行环境采用:在iOS下是用内置的 JavaScriptCore框架,在安卓则是用腾讯x5内核提供的JsCore环境。
若是采用纯web技术渲染小程序,在复杂业务场景必然带来性能问题。由于UI渲染跟js脚本都在一个单线程中执行,就容易致使js逻辑任务抢占UI资源。
本文参考:
美团技术团队:tech.meituan.com/2017/06/09/…
51NB:mp.weixin.qq.com/s/BjKeh7gk-…
腾讯Bugly:blog.csdn.net/Tencent_Bug…