日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 前端技术 > javascript >内容正文

javascript

JSBridge实战

發(fā)布時(shí)間:2023/12/2 javascript 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JSBridge实战 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前言

H5 VS Native 一直是前端技術(shù)界爭(zhēng)執(zhí)不下的話題。react、vue等技術(shù)棧引領(lǐng)著純H5開(kāi)發(fā),rn、week則倡導(dǎo)原生體驗(yàn)。但在項(xiàng)目實(shí)戰(zhàn)中,經(jīng)常會(huì)選擇一個(gè)中立的方案:混合開(kāi)發(fā)。大眾稱呼:Hybrid。

本人目前從事新聞?lì)惍a(chǎn)品研發(fā),對(duì)于大家來(lái)講,就是熟知的如今日頭條、百度新聞、網(wǎng)易新聞等。在產(chǎn)品設(shè)計(jì)初期,考慮到一些實(shí)現(xiàn)難易程度問(wèn)題(如新聞詳情頁(yè),圖文混排,NA實(shí)現(xiàn)起來(lái)不如H5這樣自如),一些部分選擇了Hybrid方式開(kāi)發(fā),本篇就把開(kāi)發(fā)過(guò)程中的一些想法分享一下,以供大家參考。

JSBridge解決的問(wèn)題

混合開(kāi)發(fā),最重要的問(wèn)題是:H5和Native的雙向通信。 但現(xiàn)實(shí)中JS和NA的交互方法非常有限,下面會(huì)詳細(xì)說(shuō)明。開(kāi)發(fā)中如只是單純的方法調(diào)用,既無(wú)法確保調(diào)用成功率,也無(wú)法確保代碼足夠簡(jiǎn)潔。于是就有了JSBridge。JSBridge,是一種JS實(shí)現(xiàn)的Bridge,是一種思路,可以有不同理解,不同的代碼實(shí)現(xiàn)。主旨思想是在H5和NA之間搭建一個(gè)橋梁(Bridge),給兩端留好更友好、更合理的接口。

H5和NA的雙向通信通用方法

H5通信方式和兼容性如下表所示。指的是借助Native的webview加載H5頁(yè)面,H5和NA之間通過(guò)API、URL攔截、全局調(diào)用等形式,實(shí)現(xiàn)消息通信。站在大廠的角度考慮,在實(shí)戰(zhàn)的時(shí)候,會(huì)選擇更兼容的方式。

H5調(diào)用NA方法梳理

平臺(tái)方法備注
AndroidshouldOverrideUrlLoadingscheme攔截方法
AndroidaddJavascriptInterfaceAPI
AndroidonJsAlert()、onJsConfirm()、onJsPrompt()
IOS攔截URL
IOS(UIwebview)JavaScriptCoreAPI方法,IOS7 支持
IOS(WKwebview)window.webkit.messageHandlersAPi方法,IOS8 支持

NA調(diào)用H5方法梳理

平臺(tái)方法備注
Androidloadurl()
AndroidevaluateJavascript()Android 4.4
IOS(UIwebview)stringByEvaluatingJavaScriptFromString
IOS(UIwebview)JavaScriptCoreIOS7.0
IOS(Wkwebview)evaluateJavaScript:javaScriptStringiOS8.0

通過(guò)上面兩端調(diào)用方法梳理表,不難分析出,URL攔截 & 執(zhí)行JS是 安卓和IOS比較通用且兼容性較好的方案。我們混合開(kāi)發(fā)的基礎(chǔ)正是基于這種方法來(lái)實(shí)現(xiàn)的。

常規(guī)混合開(kāi)發(fā)思路

H5和NA通信方面,最簡(jiǎn)單直接的思路是:NA攔截H5的URL獲取消息(一般是通過(guò)修改iframe的src來(lái)實(shí)現(xiàn) ①),經(jīng)過(guò)業(yè)務(wù)處理,NA執(zhí)行JS(在H5側(cè)提前注冊(cè)好的全局方法③)回調(diào)通知H5(如下圖)。

H5代碼實(shí)現(xiàn)如下:

<html> ... <body><div class="content">XXXXX</div> </body><script>// ① 注冊(cè)全局函數(shù),以便端調(diào)用window.setAllContent = function(){}// ② 通用方法函數(shù)var sendschema = function(action,param){let tempnode = document.createElement('iframe');tempnode.src = "bdnews://" action param;}// ③ H5邏輯開(kāi)始 運(yùn)行函數(shù)document.addEventListener("DOMContentLoaded",function(){sendschema('load_finish');},false); </script>... </html>

Android原理大致如下:

webView.setWebViewClient(new WebViewClient() {public boolean shouldOverrideUrlLoading(WebView view, String url) {// 場(chǎng)景一: 攔截請(qǐng)求、接收schemaif (url.equals("load_url")) {// 處理邏輯dosomething// 回掉view.loadUrl("javascript:setAllContent(" json ");")}// 場(chǎng)景二:端自己調(diào)用H5,沒(méi)有請(qǐng)求發(fā)起clickbutton(){view.loadUrl("javascript:setAllContent(" json ");")}} });

IOS大概邏輯如下:

// 初始化webview UIWebView * view = [[UIWebView alloc]initWithFrame:self.view.frame]; [view loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.xx.com"]]]; [self.view addSubview:view];&nbsp; &nbsp; /* webView協(xié)議中的方法 shouldStartLoadWithRequest //準(zhǔn)備加載內(nèi)容時(shí)調(diào)用的方法,通過(guò)返回值來(lái)進(jìn)行是否加載的設(shè)置 webViewDidStartLoad //開(kāi)始加載時(shí)調(diào)用的方法 webViewDidFinishLoad //結(jié)束加載時(shí)調(diào)用的方法 didFailLoadWithError //加載失敗時(shí)調(diào)用的方法 */ &nbsp; - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {if ([urlString hasPrefix:@"scheme://hybrid?info="]) {if([name isEqualToString:@"load_finish"]){// [self.webView setContent];[self.webView stringByEvaluatingJavaScriptFromString:strFormat];}} }- clickbutton(){[self.webView setContent]; }

但這樣開(kāi)發(fā)存在一些痛點(diǎn):

1)回調(diào)函數(shù)不明確。可以說(shuō)目前沒(méi)有回調(diào)函數(shù)的機(jī)制,這導(dǎo)致一些依賴于回調(diào)函數(shù)的分析及判斷無(wú)法正常使用,如:功能調(diào)用方、調(diào)用是否成功、調(diào)用失敗異常處理等這些CASE;

2)對(duì)應(yīng)關(guān)系不明確。有一些調(diào)用看起來(lái)像是回調(diào),但沒(méi)有把他們放到一起,導(dǎo)致代碼散亂,難以維護(hù)。如上面demo:sendschema('load_finish') 和 setAllContent 本來(lái)含義是 告訴NA頁(yè)面準(zhǔn)備好了,NA收到后,向頁(yè)面塞數(shù)據(jù)。本來(lái)緊密相關(guān)的一對(duì)功能,拆分開(kāi)看不出有什么聯(lián)系;

3)全局函數(shù)冗雜。理想中如果調(diào)用和回調(diào)成對(duì)出現(xiàn),DEMO中注冊(cè)及維護(hù)全局函數(shù)的工作就會(huì)減少很多。提升頁(yè)面可讀性和維護(hù)成本。如 load_finish 和 setAllContent,只保留 load_finish 即可;

4)端內(nèi)代碼冗雜。端內(nèi)注冊(cè)了與H5約定的調(diào)用方法,很顯然也需要維護(hù)一套代碼標(biāo)識(shí)什么時(shí)候調(diào)用。

以上開(kāi)發(fā)中遇到的問(wèn)題,也許剛開(kāi)始功能不多的時(shí)候還察覺(jué)不出問(wèn)題,但是隨著功能增加,后期維護(hù)成本很大。

JSB方案設(shè)計(jì)

在H5和NA之間增加一個(gè)中間層,這層封裝了H5和NA通信的交互方式。H5和NA互不關(guān)心對(duì)方的樣子,通過(guò)中間層暴露的方法進(jìn)行功能調(diào)用即可。

JSB交互模型

H5跟NA交互,從H5角度來(lái)看大致可分為兩大類:有去無(wú)回&有去有回、無(wú)去有回。

第一類交互模型

請(qǐng)求邏輯:有去無(wú)回、有去有回。這里有兩種實(shí)現(xiàn)方案(初步思路稿如下):

① 函數(shù)名關(guān)聯(lián)

let BDAPPnode = {callbacks: {},// 調(diào)用函數(shù)注冊(cè)invoke(action, params, successfnname, successfn) {this.callbacks[successfnname] = {success: successfn};sendschema(action, params);},// NA調(diào)用callbackSuccess(callbackname, params) {try {BDAPPnode.callbackFromNative(callbackname, params, true);} catch (e) {console.log('Error in error callback: ' callbackname ' = ' e);}},callbackFromNative(callbackname, params, isSuccess) {let callback = this.callbacks[callbackname];if (callback) {if (isSuccess) {callback.success && callback.success(params);}};} };

② ID 關(guān)聯(lián)

let BDAPPnode = {callbackId: Math.floor(Math.random() * 2000000000),callbacks: {},invoke(action, params, onSuccess, onFail) {this.callbackId ;this.callbacks[self.callbackId] = {success: onSuccess,fail: onFail};sendschema(action, params, this.callbackId);},callbackSuccess(callbackId, params) {try {BDAPPnode.callbackFromNative(callbackId, params, true);} catch (e) {console.log('Error in error callback: ' callbackId ' = ' e);}},callbackError(callbackId, params) {try {BDAPPnode.callbackFromNative(callbackId, params, false);} catch (e) {console.log('Error in error callback: ' callbackId ' = ' e);}},callbackFromNative(callbackId, params, isSuccess) {let callback = this.callbacks[callbackId];if (callback) {if (isSuccess) {callback.success && callback.success(callbackId, params);} else {callback.fail && callback.fail(callbackId, params);}delete BDAPPnode.callbacks[callbackId];};} };

在發(fā)出請(qǐng)求的時(shí)候,注冊(cè)回調(diào)方法。這么做有兩個(gè)目的:

  • 無(wú)需提前注冊(cè)所有全局回掉函數(shù),減少不必要的初始化,進(jìn)而減少白屏?xí)r間;

  • 不用額外起回掉函數(shù)的名稱,發(fā)起請(qǐng)求的時(shí)候傳入一個(gè)隨機(jī)ID,同時(shí)注冊(cè)此ID的回掉函數(shù)。NA通過(guò)統(tǒng)一封裝好的回掉函數(shù)調(diào)用,回調(diào)ID和參數(shù),進(jìn)而達(dá)到執(zhí)行回調(diào)邏輯。

具體選用那個(gè),還得根據(jù)具體情況具體分析看。

第二類交互模型看

請(qǐng)求邏輯:無(wú)去有回,沒(méi)有發(fā)出請(qǐng)求,NA主動(dòng)調(diào)用。此類還需注冊(cè)全局變量,等待NA調(diào)用。跟非JSBridge的實(shí)現(xiàn)是一個(gè)道理

window.fn1 = () =>{// do fn1 }window.fn2 = () =>{// do fn2 }

方案選擇

實(shí)戰(zhàn)過(guò)程中深刻體會(huì)到,混合開(kāi)發(fā)可以分為兩大類:NA服務(wù)H5,H5服務(wù)NA

前者H5為主,大多數(shù)交互是H5發(fā)起NA請(qǐng)求,等待NA回調(diào),可稱之為:『一對(duì)一請(qǐng)求』,如:H5請(qǐng)求獲取地理位置,NA做完后返回N\S坐標(biāo);

后者主要是為了解決NA成本實(shí)現(xiàn)高的問(wèn)題,多為NA主動(dòng)調(diào)用H5提前注冊(cè)好的方法,可稱之為:『?jiǎn)为?dú)請(qǐng)求』,確保功能順利實(shí)現(xiàn)。

在項(xiàng)目實(shí)戰(zhàn)過(guò)程中,經(jīng)常會(huì)有這種情況:回調(diào)函數(shù)既是一對(duì)一請(qǐng)求,也是單獨(dú)調(diào)用,如:評(píng)論功能,可以頁(yè)面點(diǎn)擊彈出NA輸入框發(fā)送,也可以點(diǎn)擊底BAR上NA實(shí)現(xiàn)的按鈕彈框發(fā)送。對(duì)于頁(yè)面來(lái)講都需要更新。站在H5角度希望NA區(qū)分,H5頁(yè)面調(diào)用的評(píng)論成功和NA調(diào)用的評(píng)論成功進(jìn)行區(qū)分,這樣就可以把模型一和模型二區(qū)分開(kāi)獨(dú)立實(shí)現(xiàn)(同時(shí)也可以區(qū)分頁(yè)面刷新的來(lái)源)。但站在NA角度來(lái)講,不關(guān)心誰(shuí)吊起的,只要評(píng)論成功,就應(yīng)該去調(diào)用更新頁(yè)面的H5方法。不然NA需要從調(diào)用開(kāi)始就攜帶參數(shù),一路到底。跟端溝通后,雙方都妥協(xié)了一步,簡(jiǎn)單功能的進(jìn)行了來(lái)源區(qū)分模型一實(shí)現(xiàn),較為復(fù)雜的模型二實(shí)現(xiàn)。

API封裝

API層處于JSBridge底層和業(yè)務(wù),有些人也把它當(dāng)做JSBridge的一部分,為了更好理解,我將它單獨(dú)抽離出來(lái)。此處主要封裝業(yè)務(wù)層調(diào)用,如下面代碼。

此處多說(shuō)一句:平日開(kāi)發(fā)要有封裝和抽離的思想,一方面減少重復(fù)代碼,一方面不斷抽離將代碼分層,沒(méi)一層可以做一些封裝和擴(kuò)展,可以提高代碼復(fù)用性。

JSB注入時(shí)機(jī)

NA注入

我們肯定是期望JSB注入越早越好,這樣不論在前端頁(yè)面中任何位置都可以隨時(shí)調(diào)用,NA注入JS的方法和時(shí)機(jī)都比較局限。如下表:

平臺(tái)方法時(shí)機(jī)
IOS[UI][self.webView stringByEvaluatingJavaScriptFromString:injectjs]webViewDidFinishLoad(會(huì)有時(shí)機(jī)問(wèn)題)
IOS[wk]evaluateJavaScript:xxxxdidCreateJavaScriptContext
AndroidwebView.loadUrl("javascript:" injectjs);)OnPageFinished

網(wǎng)頁(yè)描述頁(yè)面狀態(tài)的值有以下方法,根據(jù)兼容性及實(shí)現(xiàn)完整性,一般用DOMContentLoaded,IE9以下用readystatechange來(lái)判斷頁(yè)面是否加載成功。

名稱父對(duì)象描述兼容性
DOMContentLoadeddoc頁(yè)面內(nèi)容OKIE9
onloadwin頁(yè)面所有只要加載完成
readystatechangedoc頁(yè)面加載狀態(tài):uninitialized(為初始化):對(duì)象存在但尚未初始化。loading(正在加載):對(duì)象正在加載數(shù)據(jù)。loaded(加載完畢):對(duì)象加載數(shù)據(jù)完成。interactive(交互):可以操作對(duì)象了,但還沒(méi)有完全加載。complete(完成):對(duì)象已經(jīng)加載完畢IE9&IE10有實(shí)現(xiàn)bug

IOS的uiwebview提供了代理WebViewDidFinishLoad,WebViewDidFinishLoad 被調(diào)用時(shí),readyState 可能處在 interactive 和 complete 兩種狀態(tài),所以初始化頁(yè)面直接調(diào)用會(huì)有問(wèn)題。對(duì)于這個(gè)問(wèn)題從NA角度可以實(shí)現(xiàn)一個(gè)NSObject的擴(kuò)展,并實(shí)現(xiàn)webView:didCreateJavaScriptContext:forFrame。從H5角度可以檢測(cè)頁(yè)面狀態(tài),在complete之后再調(diào)用native。

IOS的didCreateJavaScriptContext和Android的OnPageFinished(the page has finished loading)均是在網(wǎng)頁(yè)onload之前完成,所以這兩個(gè)時(shí)機(jī)沒(méi)有調(diào)用順序的問(wèn)題。

優(yōu)點(diǎn):

1)注冊(cè)早,即使在頁(yè)面初始化就調(diào)用端能力,也可以滿足

缺點(diǎn):

由于我們選擇的是uiwebview如果按照上面的考慮,這樣做有幾點(diǎn)不足之處 1)監(jiān)聽(tīng)實(shí)現(xiàn)成本高 2)需要NA注入,NA對(duì)于JS不熟悉,JS往往也不清楚NA邏輯,后面維護(hù)成本不可控制。

如果時(shí)間不充裕的情況下,除了NA注入,還有別的辦法嘛?

JS注入

其實(shí)JS也可以在頁(yè)面一開(kāi)始就注入。比如在head里直接應(yīng)用抽離出來(lái)的Jsbridge代碼,本次8.0我們采用了這種降級(jí)方案,短時(shí)間內(nèi)完成了架構(gòu)搭建。

優(yōu)點(diǎn):

這樣減小了維護(hù)成本,功能完整,提高了調(diào)用成功的幾率。

缺點(diǎn):

增加了頁(yè)面加載解析時(shí)間會(huì)影響白屏?xí)r間。

總結(jié)

Hybrid是一種連接H5跟NA的思路,即可以快速迭代H5功能,又可以有NA的體驗(yàn),是混合開(kāi)發(fā)的典型開(kāi)發(fā)模式。實(shí)踐過(guò)程中需要根據(jù)業(yè)務(wù)形態(tài)模型來(lái)定制代碼實(shí)現(xiàn),注入時(shí)機(jī)也不是一成不變的可以根據(jù)業(yè)務(wù)形態(tài)來(lái)選擇。

參考文獻(xiàn)

移動(dòng)混合開(kāi)發(fā)中的 JSBridge

遠(yuǎn)程過(guò)程調(diào)用

你要的WebView與 JS 交互方式 都在這里了

UIWebView與WKWebView、JavaScript與OC交互

iOS中UIWebView的使用詳解

UIWebView代碼注入時(shí)機(jī)與姿勢(shì)

Hybrid 開(kāi)發(fā)

JavaScriptCore在實(shí)際項(xiàng)目中的使用的坑

總結(jié)

以上是生活随笔為你收集整理的JSBridge实战的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。