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…