从jsBridge功能说起
H5页面被webview承载,需要实现 js 和 native 的通信,常用的功能如图:
例如:
// 打开新容器承载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);复制代码
所以整个通信流程就像是这样
客户端会将jsBridge对象注入到window对象下,当调用jsBridge触发(例如获取登录态)事件方法都会被客户端特有的拦截器所拦截并处理。
jsBrige通信模型优化
当js触发jsBridge对象触发某一个方法,其实都会走到客户端的拦截器里面然后native响应,这个过程就是在向客户端发消息。而客户端回调前端callback函数并且将数据返回其实就是在向H5页面发消息。那基于这种消息发送机制,可以将通信模型进一步优化。
优点:
1.前端和客户端不需要过多维护jsBridge方法,只需要维护功能短链,例如:
// 打开新容器,并且要求用户已登录
this.state.jsBridge.emit({
name:'page1Event',
action: 'XXX/openWebView',
globle: false,
params: {
login: true
}
})复制代码
其中action就是指的功能短链,而且emit方法就是通用发起消息的bridge。
而用于接收返回消息的函数,在老通信模型中是需要通过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
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()消息
ios
使用 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
webview性能优化
webview渲染过程
( 摘自:美团技术团队 tech.meituan.com/2017/06/09/… )
从用户使用的过程,大致如下:
1.打开了一个新的窗口
2.页面白屏
3.页面基本骨架渲染出来,但是没有数据
4.数据获取完成,页面整体渲染结束
慢的一部分原因:webview去加载url并不像是 浏览器 加载url的过程,webview存在一个初始化的过程。
webview init
为了提升init时间,常用做法是:
app启动时初始化一个隐藏的webview等待使用,当用户点击需要加载URL,直接使用这个webview 来加载,从而减少webview init 初始化时间。弊端就是带来了额外的内存开销。
webview缓存使用
为了提升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渲染?
如果采用纯web技术渲染小程序,在复杂业务场景必然带来性能问题。因为UI渲染跟js脚本都在一个单线程中执行,就容易导致js逻辑任务抢占UI资源。
本文参考:
美团技术团队:tech.meituan.com/2017/06/09/…
51NB:mp.weixin.qq.com/s/BjKeh7gk-…
腾讯Bugly:blog.csdn.net/Tencent_Bug…