注:因為不了解Dart,所以本文不對flutter相關內容進行闡述, 實在抱歉
其實寫這篇文章的時候,我就知道,肯定有人問我:為什么不寫flutter?抱歉了,flutter的大名我當然知道,可我只是一個寫JS的,同時了解一些Java的知識,而flutter采用的編程語言,我暫時沒有碰過,所以自然不敢妄加猜度,還請諒解
Hybrid
Hybird是一種混合開發應用,可以實現JS和Java代碼的互通,單純使用ios/android原生實現,開發進度和成本受不了,而單純使用h5/js開發,頁面體驗更加受不了。Hybird的目的是實現H5和Naive兩者之間的權衡
Hybird的實現方式
Hybrid是基於原生webview控件實現的,它主要要解決的問題有兩個:
-
原生端怎么調用JS代碼
-
JS代碼怎么調用原生端
這個問題進一步擴展,又細分為以下4個問題:
-
Q1: JS怎么調用Android代碼
-
Q2: Android代碼怎么調用JS
-
Q3: JS怎么調用IOS代碼
-
Q4: IOS代碼怎么調用JS
Q1: JS怎么調用Android代碼
我們先講下JS是怎么調Android代碼的
主要有3種
-
JSInterface
-
JSBridge
-
UrlRouter
1)JSInterface
從我們前端的角度看啊,是這樣子滴~ :在Android中啊,有個叫做WebView的控件,這個控件的作用是可以在里面放一個網頁然后運行它!我們前端就暫時把它理解成一個安卓APP里嵌入的微型瀏覽器吧,哈哈。然后呢,這個WebView控件對象還可以調用一個方法。
一個叫webView.addJavascriptInterface(接口對象,接口名)的方法,調用后,webView控件里面的HTML頁面里的JS代碼,就可以調用剛才addJavascriptInterface方法里的接口對象的原生方法了! 於是就這樣,我們可以從JS間接調用原生Android代碼,從此橋梁建立
例如,比如說我們下面定一個JSInterface的類,里面的showToast方法可以彈出一個原生的Toast
Android的原生代碼
webView.addJavascriptInterface(new JsInterface(), "control”); // … public class JsInterface { @JavascriptInterface public void showToast(String toast) { Toast.makeText(MainActivity.this, toast, Toast.LENGTH_SHORT).show(); log("show toast success"); } }
JS代碼
// WebView中的JS代碼,注意control就是上面addJavascriptInterface定義的命名空間 function showToast(toast) { javascript:control.showToast(toast); }
參考資料
2)JSbridge
從我們前端的角度看啊,其實是這樣子滴~:就是在Android中啊,有這么一個WebChromeClient的組件,它就是上面講到的WebView控件的一個子類。沒錯,它也可以在里面放一個網頁去運行它,而且它牛啤的地方在於:你這個內部網頁里的JS干的三件事可以被外層WebChromeClient給監聽到:分別是前端的alert方法,confirm方法和prompt方法。只要你動了這三個方法,它們傳遞的數據就會被外部的WebChromeClient攔截和獲取,這就為JS調Android的代碼提供了一種方便的渠道。哎呀,三個方法這么多選哪個呢? 一般情況下,我們會選prompt方法,因為alert方法JS相對用的比較頻繁,存在起沖突的可能
3)UrlRouter
這個東東還是和上面是一樣的,Android的WebChromeClient控件這個類,它有個shouldOverrideUrlLoading這個方法,這個方法可以把控件內部網頁的JS的Url請求給攔截了,當然了,你寫在Url中的數據也同時被一並獲取了。
總結:說白了JSInterface,JSBridge和UrlRouter主要的作用就是提供JS調原生代碼的方式,搭一座橋梁
Q2: Android怎么調JS代碼?
1)web view.loadUrl
有了上面的經驗你肯定知道,這事還是webview這位老哥來做的,它可以通過調用webview.loadUrl方法加載一個HTML頁面,這樣HTML中的JS腳本不就被調用了嗎?
webView.loadUrl(“...//my.html”);
2)webView.evaluateJavascript
上面的loadUrl有一個問題,它會導致頁面刷新,而且通過加載文件的方式執行JS代碼總不是我們認為最優雅的方式,我們可能期望的是執行一段指定的代碼,而非一個文件,webView.evaluateJavascript就是做這件事情的,以下的代碼可以執行一段JS代碼
webView.evaluateJavascript(“JS代碼”,Callback對象)
哦,對了,不好意思,上面講的是Android的,下面講下IOS怎么做
Q3: IOS代碼怎么調用JS
1.可通過webview.stringByEvaluatingJavaScriptFromString方法調用JS方法,但前提是該JS方法在頂層Window對象上
webview.stringByEvaluatingJavaScriptFromString("方法名(參數)”)
Q4: JS怎么調用IOS代碼
可通過 shouldStartLoadWithRequest方法進行攔截JS請求,從而感知JS的調用發起,並進行相應處理,以達到JS調用ios的效果
Hybrid也曾在移動端連接H5的童話世界中風靡一時,但由於對webview以及H5的過度依賴,導致它的體驗性問題一直讓人困擾,所以自從React-Native橫空出世后,后者便蠶食了前者的半壁江山。
React-Native
RN的作用
-
跨平台:可以為IOS/Android,甚至Windows Phone開發原生應用
-
相對良好的UI體驗,平衡開發成本和用戶體驗后相對合理的選擇
RN的本質
-
不是WebView,和Cordova等Hybrid方案划清界限
-
不將JavaScript預編譯為Native代碼,和Xamarin等方案划清界限。Xammarin的方案是AOT的,運行前就編譯為原生代碼,RN則采用JIT+解釋器的方案(IOS另當別論)
-
RN是虛擬機類的方案,依靠運行時系統JavaScriptCore運行
RN的4個線程
-
UI線程:也成為主線程,負責本機的Android/iOS的UI呈現,在android中它負責android測量/布局/繪制
-
JS線程:執行JS/React代碼,進行API調用,處理觸摸事件等,對視圖的更新被進行批處理,並在事件循環結束時發送給UI線程
-
Shadow線程:處理虛擬DOM布局變更的線程
-
本機模塊線程: 如android/ios系統自帶的原生API
RN的3部分
-
Native端(IOS/android)
-
JavaScript端
-
Bridge:上面介紹的多個線程之間相互通信,以及JS和Native端通信的方式的統稱
線程協調過程示例
以下面一段RN代碼的執行為例,它在JS線程中執行
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}> <View style={{width: 100, height: 100, backgroundColor: "red"}}></View> </View>
- 注意原生端有自己的布局實現,所以上面的flex屬性和CSS是沒有任何關系的。
- 為了實現布局,同時又不阻塞JS線程,布局計算將轉移到Shadow線程中進行。
- Shadow線程進行計算,並最終將計算結果得到的布局參數傳遞給主線程(UI線程),實現UI的構建
RN中的Bridge做了什么? && RN線程如何交互?
-
異步:線程之間,例如JS線程和UI線程,以異步的方式進行通信,這樣它們就不會互相阻塞了
-
批處理: 以優化的方式, 把消息從一個線程傳遞到另外一個線程
-
序列化: 兩個線程不會操作或者共享同一塊數據,它們之間會通過序列化和反序列化的方式交換消息
RN線程異步帶來的某些問題 && 未來的解決方案
RN中的JS線程和UI線程之間是沒有同步的方式的,這可能造成一些問題,但RN未來的Fabric也許能提供這一功能
RN的Web化:react-native-web
react-native-web 組件的內部,會把 React Native 的 API 映射成了瀏覽器支持的 API。將RN的代碼轉化成瀏覽器能支持的代碼
RN-web和普通的React的區別?
RN-web盡量做到不侵入RN代碼,不影響RN代碼的邏輯,爭取能夠在基本不動RN項目代碼的情況下,將其H5化,RN-web項目的基本邏輯還是RN,不是React
RN-WEB的作用
實現IOS/Android/Web的三端構建