跨平臺一直是老生常談的話題,cordova、ionic、react-native、weex、kotlin-native、flutter等跨平臺框架的百花齊放,很有一股推倒原生開發者的勢頭。javascript
爲何咱們須要跨平臺開發? 本質上,跨平臺開發是爲了增長代碼複用,減小開發者對多個平臺差別適配的工做量,下降開發成本,提升業務專一的同時,提供比web更好的體驗。嗯~通俗了說就是:省錢、偷懶。php
目前移動端跨平臺開發中,備受關注的方案大體概括爲如下幾種狀況:html
1)react native、weex均使用JavaScript做爲編程語言,目前JavaScript在跨平臺開發中,可謂佔據半壁江山,大有「一統天下」的趨勢;前端
2)kotlin-native開始支持 iOS 和 Web 開發,(kotlin已經成爲android的一級語言)也想嘗試「一統天下」;vue
3)flutter是Google跨平臺移動UI框架,Dart做爲谷歌的親兒子,毫無疑問Dart成爲flutter的編程語言。做爲巨頭新生兒,在flutter官網也能夠看出,flutter一樣「心懷天下」(可支持Web端、Android端、iOS端等)。java
本篇主要以react-native、weex、flutter,深刻聊聊當前最火的這3種跨平臺移動開發方案的實現原理、現狀與將來。至於爲何只講它們,由於對比ionic、phoneGap,它們更於 「naive」 (˶ ⁻̫ ˵)。看完本篇,相信你會對於當下跨平臺移動開發的現狀、實現原理、框架的選擇等有更深刻的理解。node
(本文同步發佈於:http://www.52im.net/thread-1870-1-1.html)react
React Native技術關鍵詞:android
1)Facebook 出品;webpack
2)JavaScript語言;
3)JSCore引擎;
4)React設計模式;
5)原生渲染。
「Learn once, write anywhere」 ,表明着 Facebook對 react native 的定義:學習 react ,同時掌握 web 與 app 兩種開發技能。 react native 用了 react 的設計模式,但UI渲染、動畫效果、網絡請求等均由原生端實現。開發者編寫的js代碼,經過 react native 的中間層轉化爲原生控件和操做,比ionic等跨平臺應用,大大提升了的用戶體驗。
總結起來其實就是:React Native是利用 JS 來調用 Native 端的組件,從而實現相應的功能。
以下圖所示,react native 的跨平臺是實現主要由三層構成,其中 C++ 實現的動態連結庫(.so),做爲中間適配層橋接,實現了js端與原生端的雙向通訊交互。這裏最主要是封裝了 JavaScriptCore 執行js的解析,而 react native 運行在JavaScriptCore中,因此不存在瀏覽器兼容的問題。
其中在IOS上直接使用內置的javascriptcore, 在Android 則使用webkit.org官方開源的jsc.so。
和前端開發不一樣:react native 全部的標籤都不是真實控件,JS代碼中所寫控件的做用,相似 Map 中的 key 值。JS端經過這個 key 組合的 Dom ,最後Native端會解析這個 Dom ,獲得對應的Native控件渲染,如 Android 中 標籤對應 ViewGroup 控件。
在 react native 中,JS端是運行在獨立的線程中(稱爲JS Thread )。JS Thread 做爲單線程邏輯,不可能處理耗時的操做。那麼如 fetch 、圖片加載 、 數據持久化 等操做,在 Android 中實際對應的是 okhttp 、Fresco 、SharedPreferences等。而跨線程通訊,也意味着 Js Thread 和原生之間交互與通信是異步的。
能夠看出,react native 跨平臺的關鍵在於C++層,開發人員大部分時候,只專一於JS 端的代碼實現。 在原生端提供的各類 Native Module 模塊(如網絡請求,ViewGroup控件),和 JS 端提供的各類 JS Module(如JS EventEmiter模塊),都會在C++實現的so中保存起來,雙方的通信經過C++中的保存的映射,最終實現兩端的交互。通訊的數據和指令,在中間層會被轉爲String字符串傳輸,雙向的調用流程以下圖。
最終:JS代碼會被打包成一個 bundle 文件,自動添加到 App 的資源目錄下。react native 的打包腳本目錄爲/node_modules/react-native/local-cli,打包最後會經過 metro 模塊壓縮 bundle 文件。而bundle文件只會打包js代碼,天然不會包含圖片等靜態資源,因此打包後的靜態資源,實際上是被拷貝到對應的平臺資源文件夾中。
其中圖片等存在資源的映射規則,好比放在 react native 項目根目錄下的 img/pic/logo.png 的資源,編譯時,會被重命名後,根據大小 merged 到對應的是drawable目錄下,修更名稱爲img_pic_logo.png。
打包Android和IOS,確定須要相應的平臺項目存在,在 react-native init 時建立的項目,就已經包含了 android 和 ios 的模版工程,打包完的工程會加載bundle文件,而後啓動項目,以下圖。這裏就不展開了,有興趣的能夠看QQ空間移動開發團隊分享的《React Native For Android 架構初探》。
▲ react native完成啓動流程圖(圖片來源於QQ空間移動開發團隊)
WEEX技術關鍵詞:
1)Alibaba 出品;
2)JavaScript語言;
3)JS V8引擎;
4)Vue設計模式;
5)原生渲染。
「Write once, run everywhere」:weex的定義就像是:寫個 vue 前端,順便幫你編譯成性能還不錯的 apk 和 ipa(固然,現實有時很骨感)。基於 Vue 設計模式,支持 web、android、ios 三端,原生端一樣經過中間層轉化,將控件和操做轉化爲原生邏輯來提升用戶體驗。
在 weex 中,主要包括三大部分:JS Bridge、Render、Dom,分別對應WXBridgeManager、WXRenderManager、WXDomManager,三部分經過WXSDKManager統一管理。其中 JS Bridge 和 Dom 都運行在獨立的 HandlerThread 中,而 Render 運行在 UI 線程。
JS Bridge 主要用來和 JS 端實現進行雙向通訊,好比把 JS 端的 dom 結構傳遞給 Dom 線程。Dom 主要是用於負責 dom 的解析、映射、添加等等的操做,最後通知UI線程更新。而 Render 負責在UI線程中對 dom 實現渲染。
和 react native同樣——weex 全部的標籤也不是真實控件,JS 代碼中所生成存的 dom,最後都是由 Native 端解析,再獲得對應的Native控件渲染,如 Android 中 標籤對應 WXTextView 控件。
weex 中文件默認爲 .vue ,而 vue 文件是被沒法直接運行的,因此 vue 會被編譯成 .js 格式的文件,Weex SDK會負責加載渲染這個js文件。Weex能夠作到跨三端的原理在於:在開發過程當中,代碼模式、編譯過程、模板組件、數據綁定、生命週期等上層語法是一致的。不一樣的是在 JS Framework 層的最後,web 平臺和 Native 平臺,對 Virtual DOM 執行的解析方法是有區別的。
實際上,在 Native 中對 bundle 文件的加載大體經歷如下階段:
1)weex 接收到 js 文件之後,JS Framework 根據文件爲 Vue 模式,會調用weex-vue-framework 中提供的 createInstance方法建立實例。(也多是Rax模式);
2)createInstance 中會執行 Js Entry 代碼裏 new Vue() 建立一個組件,經過其 render 函數建立出 Virtual DOM 節點;
3)由JS V8 引擎上解析 Virtual DOM ,獲得 Json 數據發送至 Dom 線,這裏輸出 Json 也是方便跨端的數據傳輸;
4)Dom 線程解析 Json 數據,獲得對應的 WxDomObject,而後建立對應的WxComponent 提交 Render;
5)Render在原生端最終處理處理渲染任務,並回調裏JS方法。
得益於上層的統一性,只是經過 weex-vue-framework 判斷是由Vue.js 生成真實的 Dom ,仍是經過 Native Api 渲染組件,weex 必定程度上上,用JS 實現了 vue 一統天下的效果。
weex 在原生渲染 Render 時,在接收到渲染指令後,會逐步將數據渲染成原生組件。Render 經過解析渲染數據的描述,而後分發給不一樣的模塊。
好比:控件渲染屬於 dom 模塊中,頁面跳轉屬於navigator模塊等。模塊的渲染過程並不是一個執行完,再執行另外一個的流程,而是相似流式的過程。如上一個 的組件還沒渲染好,下一個
的渲染又發了過來。這樣當一個組件的嵌套組件不少時,或者能夠看到這個大組件內的UI,一個一個渲染出來的過程。
weex 比起react native,主要是在JS V8的引擎上,多了 JS Framework 承當了重要的職責,使得上層具有統一性,能夠支持跨三個平臺。
總的來講JS Framework主要負責的是:
1)管理Weex的生命週期;
2)解析JS Bundle,轉爲Virtual DOM,再經過所在平臺不一樣的API方法構建頁面;
3)進行雙向的數據交互和響應。
weex 做爲 react-native 以後出現的跨平臺實現方案,天然能夠站在前人的肩膀上優化問題,好比:Bundle文件過大問題。
Bundle文件的大小,很大程度上影響了框架的性能,而 weex 選擇將 JS Framework 集成到 WeexSDK 中,必定程度減小了JS Bundle的體積,使得 bundle 裏面只保留業務代碼。
打包時,weex 是經過 webpack 打包出 bundle 文件的。bundle 文件的打包和 entry.js 文件的配置數量有關,默認狀況下以後一個 entry 文件,天然也就只有一個bundle文件。
在 weex 項目的 webpack.common.conf.js 中能夠看到,其實打包也是區分了 webConfig 和 weexConfig 的不一樣打包方式。以下圖,其中weexEntry 就是 weex 打包配置的地方,能夠看到原本已經有 index 和 entry.js 存在了。若是有須要,可配置上你須要的打包頁面,具體這裏就不詳細展開了。有興趣可看《Weex原理之帶你去蹲坑》。
Flutter技術關鍵詞:
1)Google 出品;
2)Dart語言;
3)Flutter Engine引擎;
4)響應式設計模式;
5)原生渲染。
Flutter 是谷歌2018年發佈的跨平臺移動UI框架。相較於本人已經在項目中使用過 react native 和 Weex,Flutter目前僅僅是簡單運行過Demo,畢竟仍是beta 階段,這裏更多的聊一下它的實現機制和效果。
與 react native 和 weex 的經過 Javascript 開發不一樣,Flutter 的編程語言是Drat,因此執行時並不須要 Javascript 引擎,但實際效果最終也經過原生渲染。
如上圖,Flutter 主要分爲 Framework 和 Engine,咱們基於Framework 開發App,運行在 Engine 上。Engine 是 Flutter 的獨立虛擬機,由它適配和提供跨平臺支持,目前猜想 Flutter 應用程序在 Android 上,是直接運行 Engine 上 因此在是不須要Dalvik虛擬機(這是比kotlin更完全,拋棄JVM的糾纏?)
以下圖,得益於 Engine 層,Flutter 甚至不使用移動平臺的原生控件, 而是使用本身 Engine 來繪製 Widget (Flutter的顯示單元),而 Dart 代碼都是經過 AOT 編譯爲平臺的原生代碼,因此 Flutter 能夠 直接與平臺通訊,不須要JS引擎的橋接。同時 Flutter 惟一要求系統提供的是 canvas,以實現UI的繪製。咦?這麼想來,支持web端也沒問題吧!
在Flutter中,大多數東西都是widget,而widget是不可變的,僅支持一幀,而且在每一幀上不會直接更新,要更新而必須使用Widget的狀態。無狀態和有狀態 widget 的核心特性是相同的,每一幀它們都會從新構建,有一個State對象,它能夠跨幀存儲狀態數據並恢復它。
Flutter 上 Android 自帶了 Skia,Skia是一個 2D的繪圖引擎庫,跨平臺,因此能夠被嵌入到 Flutter的 iOS SDK中,也使得 Flutter Android SDK要比 iOS SDK小不少。
熱門話題:爲何Flutter會選擇 Dart做爲開發語言?
八卦消息認爲:「是由於 Drat 項目組就在 Flutter 隔壁而被選上」。
實際上真實的緣由是:早期的Flutter團隊評估了十多種語言,並選擇了Dart,由於它符合他們構建用戶界面的方式。
Dart之因此成爲Flutter不可或缺的一部分,根本緣由仍是由於其具備如下特性:
1)Dart是AOT(Ahead Of Time)編譯的,編譯成快速、可預測的本地代碼,使Flutter幾乎均可以使用Dart編寫。這不只使Flutter變得更快,並且幾乎全部的東西(包括全部的小部件)均可以定製;
2)Dart也能夠JIT(Just In Time)編譯,開發週期異常快,工做流顛覆常規(包括Flutter流行的亞秒級有狀態熱重載);
3)Dart能夠更輕鬆地建立以60fps運行的流暢動畫和轉場。Dart能夠在沒有鎖的狀況下進行對象分配和垃圾回收。就像JavaScript同樣,Dart避免了搶佔式調度和共享內存(於是也不須要鎖)。因爲Flutter應用程序被編譯爲本地代碼,所以它們不須要在領域之間創建緩慢的橋樑(例如,JavaScript到本地代碼)。它的啓動速度也快得多;
4)Dart使Flutter不須要單獨的聲明式佈局語言,如JSX或XML,或單獨的可視化界面構建器,由於Dart的聲明式編程佈局易於閱讀和可視化。全部的佈局使用一種語言,彙集在一處,Flutter很容易提供高級工具,使佈局更簡單;
5)開發人員發現Dart特別容易學習,由於它具備靜態和動態語言用戶都熟悉的特性。
並不是全部這些功能都是Dart獨有的,但它們的組合卻恰到好處,使Dart在實現Flutter方面獨一無二。所以,沒有Dart,很難想象Flutter像如今這樣強大。
有關此話題的詳細文章請見《爲何Flutter會選擇 Dart ?》。
這算是互相傷害的環節了吧。(///▽///)
以Android平臺爲例,上面Apk大小是經過 react-native init、weex create 和 flutter 建立出的工程後,直接不添加任何代碼,打包出來的 release 簽名 apk 大小。從下圖能夠看出,其中大比例都是在so庫。
react native 做爲 Facebook 主力開源項目之一,至今已有各種豐富的第三方庫,甚至如 realm、lottie 等開源項目也有 react native 相關的版本,社羣實際無需質疑。固然,由於並徹底正統開發平臺,第三庫的健壯性和兼容性有時候老是參差不齊。
weex 其實有種生錯在國內的感受。其實 weex 的設計和理念都很優秀,性能也不錯,可是對比 react native 的第三方支持,就顯得有點後媽養的。2016年開源至今,社區和各種文檔都顯得有點疲弱,做爲跨平臺開發人員,大多時候確定不會但願,須要頻繁的本身增長原生功能支持,由於這樣的工做一多,反而會與跨平臺開發的理念背道而馳,帶來開發成本被維護難度增長。
Flutter 目前還處理beta階段,可是谷歌的號召力一直很可觀,這一點無需質疑。
理論上 flutter 的性能應該是最好的,可是目前實際體驗中,卻並無感覺出來太大的差距,和 react native(0.5.0以後)、weex 在性能上我的體驗差別不是很大。固然,這裏並無實測渲染的毫秒時間和幀率數據。
Weex的多頁面實現問題:
weex 在 native 端是不支持 的,這一點和 react-native 不一樣在與,若是在 native 須要實現頁面跳轉,使用 vue-router 將會慘不忍睹:返回後頁面不作特別處理時,是會空白一片。參考官方Demo playground,native 端 的採用 weex.requireModule('navigator') 跳轉 Activity 是才正確實現。
同時,weex中 navigator 跳轉的設計,也致使了多頁面的頁面間通信的差別。weex在多頁面下的數據通信,是經過url實現的,好比file://assets/dist/SecondPage.js?params=0,而vuex和vue-router在跨頁面是沒法共用的;而 react native 在跨 Actvity 使用時,由於是同一個bundle文件,只要 manager 相同,那麼 router 和 store 時能夠照樣使用的,數據通訊方式也和當個 Actvity 沒區別。
項目模板:
weex 和 react native 模板代碼模式也不一樣。weex 的模板是從 cordova 模式修改過來的,根據platform需求,用命令添加固定模塊,而在 .gitignore 對 platforms 文件夾是忽略跟蹤。 react native 在項目建立時模版就存在了,特別是添加第三方插件原生端支持時,會直接修改模板代碼,git代碼中也會添加跟蹤修改。
咱們選擇框架的時候,不少時候會關注框架的成熟度和生命力不是麼(◐‿◑)。
「Airbnb 宣佈放棄使用 React Native,迴歸使用原生技術」 : Airbnb 做爲 react native 平臺上最大的支持者之一,其開源的lottie 一樣是支持原生和 react native。
Airbnb 在宣佈放棄的文中,也對 react native 的表示了很大量的確定,而使得他們放棄的理由,其實主要仍是集中於項目龐大以後的維護困難,第三方庫的參差不齊,兼容上須要耗費更多的精力致使放棄。
ps: Lottie庫Airbnb出的是一個可以幫助解析AE導出的包含動畫信息的json文件。這很好的解決了一個矛盾,設計師能夠更專一的設計出各類炫酷的動畫效果,而開發只須要將其加入支持便可。
Facebook 正在重構 React Native,將重寫大量底層。在經歷了開源協議風波後,能夠看出 Facebook 對於 react native 仍是很看重的。
這些底層重構優化的地方,主要集中於:
1)首先:改變線程模型。UI 更新再也不須要在三個不一樣的線程上執行,而是能夠在任意線程上同步調用 JavaScript 進行優先更新,同時將低優先級工做推出主線程,以便保持對 UI 的響應;
2)其次:將異步渲染功能引入 React Native 中,容許執行多個渲染並簡化異步數據處理;
3)最後:簡化橋接,讓它更快、更輕量。原生和 JavaScript 之間的直接調用效率更高,而且能夠更輕鬆地構建調試工具,如跨語言堆棧跟蹤。
其餘React Native相關文章:
從Android到React Native開發(2、通訊與模塊實現)
從Android到React Native開發(3、自定義原生控件支持)
從Android到React Native開發(4、打包流程和發佈爲Maven庫)
沒有死!阿里公開Weex技術架構,還開源了一大波組件。 2018年初的新聞能夠看出,weex 的遭遇有點相似曾經的 Duddo(Dubbo由於內部競爭被阿里一度放棄維護),這波詐屍後 weex 被託管到了Apache,而github的 weexteam 現在也還保持着更新,但願後續能有多好的發展,拭目以待吧。
Flutter 是 Google 跨平臺移動UI框架,Dart做爲谷歌的親兒子在 Flutter 中使用,而且谷歌新操做系統 Fuchsia 支持 Dart,使用 Flutter 做爲操做UI框架。這些集合到一塊兒不免讓你懷疑 Android 是否要被谷歌拋棄的想法。
或者現在先 Android 等平臺上推廣 Flutter 與 Dart,就是爲了之後跟好的過渡到新系統上,畢竟開發者是操做系統的生命源泉之一。而 Java 與 JVM 或者能夠被谷歌徹底拋開。固然,目前看起來 Flutter 貌似還缺乏一些語法糖,嵌套下來的代碼有點不忍直視,或者到正式版以後,咱們更能感覺出它的美麗吧。
《現代移動端網絡短鏈接的優化手段總結:請求速度、弱網適應、安全保障》
《QQ音樂團隊分享:Android中的圖片壓縮技術詳解(上篇)》
《QQ音樂團隊分享:Android中的圖片壓縮技術詳解(下篇)》
《騰訊原創分享(一):如何大幅提高移動網絡下手機QQ的圖片傳輸速度和成功率》
《騰訊原創分享(二):如何大幅壓縮移動網絡下APP的流量消耗(上篇)》
《騰訊原創分享(三):如何大幅壓縮移動網絡下APP的流量消耗(下篇)》
《基於社交網絡的Yelp是如何實現海量用戶圖片的無損壓縮的?》
《騰訊技術分享:騰訊是如何大幅下降帶寬和網絡流量的(圖片壓縮篇)》
《騰訊技術分享:騰訊是如何大幅下降帶寬和網絡流量的(音視頻技術篇)》
《字符編碼那點事:快速理解ASCII、Unicode、GBK和UTF-8》
《最火移動端跨平臺方案盤點:React Native、weex、Flutter》
>> 更多同類文章 ……
(本文同步發佈於:http://www.52im.net/thread-1870-1-1.html)