移動端跨平臺開發的深度解析

跨平臺一直是老生常談的話題,cordova、ionic、react-native、weex、kotlin-native、flutter等跨平臺框架的百花齊放,很有一股推倒原生開發者的勢頭。(事實上更可能是共存發展)看完本篇,相信你會對於當下跨平臺移動開發的現狀、實現原理、框架的選擇等有更深刻的理解javascript

全篇內容較多,需耐心食用! (///▽///)html

1、前言

爲何咱們須要跨平臺開發?本質上,跨平臺開發是爲了增長代碼複用,減小開發者對多個平臺差別適配的工做量,下降開發成本,提升業務專一的同時,提供比web更好的體驗。嗯~通俗了說就是:省錢、偷懶前端

 本篇主要以react-native、weex、flutter,結合資訊展望,深刻聊聊當前跨平臺移動開發的實現原理、現狀與將來。至於爲何只講它們,由於對比ionic、phoneGap,它們更於 「naive」 (˶‾᷄ ⁻̫ ‾᷅˵)。vue

超完整跨平臺開源項目java

類型 連接
react-native https://github.com/CarGuo/GSYGithubApp
weex https://github.com/CarGuo/GSYGithubAppWeex
Flutter https://github.com/CarGuo/GSYGithubAppFlutter
 
百花齊放

2、原理與特性

目前移動端跨平臺開發中,大體概括爲如下幾種狀況:node

  • react nativeweex均使用JavaScript做爲編程語言,目前JavaScript在跨平臺開發中,可謂佔據半壁江山,大有「一統天下」的趨勢。react

  • kotlin-native開始支持 iOS 和 Web 開發,(kotlin已經成爲android的一級語言)也想嘗試「一統天下」。android

  • flutter是Google跨平臺移動UI框架,Dart做爲谷歌的親兒子,毫無疑問Dart成爲flutter的編程語言,以下圖,做爲巨頭新生兒,在flutter官網也能夠看出,flutter一樣「心懷天下」。webpack

 
flutter 官網野心勃勃

一、React Native

Facebook 出品,JavaScript語言,JSCore引擎,React設計模式,原生渲染ios

1.一、理念架構

「Learn once, write anywhere」,表明着 Facebook對 react native 的定義:學習 react ,同時掌握 web與 app兩種開發技能。 react native 用了 react 的設計模式,但UI渲染、動畫效果、網絡請求等均由原生端實現。開發者編寫的js代碼,經過 react native 的中間層轉化爲原生控件和操做,比ionic等跨平臺應用,大大提升了的用戶體驗。

總結起來其實就是利用 JS 來調用 Native 端的組件,從而實現相應的功能。

 以下圖所示,react native 的跨平臺是實現主要由三層構成,其中 C++ 實現的動態連結庫(.so),做爲中間適配層橋接,實現了js端與原生端的雙向通訊交互。這裏最主要是封裝了 JavaScriptCore執行js的解析,而 react native 運行在JavaScriptCore中,因此不存在瀏覽器兼容的問題。

 其中在IOS上直接使用內置的javascriptcore, 在Android 則使用webkit.org官方開源的jsc.so

 
圖片來源網絡

1.二、實現原理

 和前端開發不一樣,react native 全部的標籤都不是真實控件,JS代碼中所寫控件的做用,相似 Map 中的 key 值。JS端經過這個 key 組合的 Dom ,最後Native端會解析這個 Dom ,獲得對應的Native控件渲染,如 Android 中<view>標籤對應 ViewGroup控件。

 
圖片來源網絡

 在 react native 中,JS端是運行在獨立的線程中(稱爲JS Thread )。JS Thread 做爲單線程邏輯,不可能處理耗時的操做。那麼如 fetch圖片加載、 數據持久化等操做,在 Android 中實際對應的是 okhttpFrescoSharedPreferences等。而跨線程通訊,也意味着 Js Thread 和原生之間交互與通信是異步的。

 能夠看出,跨平臺的關鍵在於C++層,開發人員大部分時候,只專一於JS 端的代碼實現。 在原生端提供的各類 Native Module 模塊(如網絡請求,ViewGroup控件),和 JS 端提供的各類 JS Module(如JS EventEmiter模塊),都會在C++實現的so中保存起來,雙方的通信經過C++中的保存的映射,最終實現兩端的交互。通訊的數據和指令,在中間層會被轉爲String字符串傳輸,雙向的調用流程以下圖。

 
圖片來源網絡,js調用native
 
圖片來源網絡,native調用js

1.三、打包加載

 最終,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文件,而後啓動項目,以下圖。這裏就不展(tou)開(lan)了,有興趣的能夠看:React Native For Android 架構初探

 
圖片來源QQ空間移動開發團隊,完成啓動流程圖


二、WEEX

Alibaba 出品,JavaScript語言,JS V8引擎,Vue設計模式,原生渲染

2.一、理念架構

「Write once, run everywhere」, weex的定義就像是:寫個 vue 前端,順便幫你編譯成性能還不錯的 apk 和 ipa(固然,現實有時很骨感)。基於 Vue 設計模式,支持 web、android、ios 三端,原生端一樣經過中間層轉化,將控件和操做轉化爲原生邏輯來提升用戶體驗。

 在 weex 中,主要包括三大部分:JS BridgeRenderDom,分別對應WXBridgeManagerWXRenderManagerWXDomManager,三部分經過WXSDKManager統一管理。其中 JS Bridge和 Dom都運行在獨立的 HandlerThread 中,而 Render 運行在 UI 線程。

JS Bridge主要用來和 JS 端實現進行雙向通訊,好比把 JS 端的 dom 結構傳遞給 Dom線程。Dom主要是用於負責 dom 的解析、映射、添加等等的操做,最後通知UI線程更新。而 Render負責在UI線程中對 dom 實現渲染。

 
圖片來自網絡

2.二、實現原理

 和 react native同樣,weex 全部的標籤也不是真實控件,JS 代碼中所生成存的 dom,最後都是由 Native 端解析,再獲得對應的Native控件渲染,如 Android 中 <text>標籤對應 WXTextView控件。

 weex 中文件默認爲 .vue ,而 vue 文件是被沒法直接運行的,因此 vue 會被編譯成 .js 格式的文件,Weex SDK會負責加載渲染這個js文件。Weex能夠作到跨三端的原理在於:在開發過程當中,代碼模式、編譯過程、模板組件、數據綁定、生命週期等上層語法是一致的。不一樣的是在 JS Framework層的最後,web 平臺和 Native 平臺,對 Virtual DOM 執行的解析方法是有區別的。

 
 

 實際上,在 Native 中對 bundle 文件的加載大體經歷如下階段:

  • weex 接收到 js 文件之後,JS Framework 根據文件爲 Vue 模式,會調用weex-vue-framework中提供的 createInstance方法建立實例。(也多是Rax模式)

  • createInstance中會執行 Js Entry 代碼裏 new Vue()建立一個組件,經過其 render 函數建立出 Virtual DOM 節點。

  • 由JS V8 引擎上解析 Virtual DOM ,獲得 Json 數據發送至 Dom 線,這裏輸出 Json 也是方便跨端的數據傳輸。

  • Dom 線程解析 Json 數據,獲得對應的 WxDomObject,而後建立對應的WxComponent提交 Render 。

  • Render在原生端最終處理處理渲染任務,並回調裏JS方法。

 得益於上層的統一性,只是經過 weex-vue-framework判斷是由Vue.js生成真實的 Dom ,仍是經過 Native Api 渲染組件,weex 必定程度上上,用JS 實現了 vue一統天下的效果。

 
圖片來源網絡

  weex 在原生渲染 Render 時,在接收到渲染指令後,會逐步將數據渲染成原生組件。Render 經過解析渲染數據的描述,而後分發給不一樣的模塊。

  好比 控件渲染屬於 dom模塊中,頁面跳轉屬於navigator模塊等。模塊的渲染過程並不是一個執行完,再執行另外一個的流程,而是相似流式的過程。如上一個 <text>的組件還沒渲染好,下一個 <div>的渲染又發了過來。這樣當一個組件的嵌套組件不少時,或者能夠看到這個大組件內的UI,一個一個渲染出來的過程。

 weex 比起react native,主要是在JS V8的引擎上,多了 JS Framework承當了重要的職責,使得上層具有統一性,能夠支持跨三個平臺。總的來講它主要負責是:管理Weex的生命週期解析JS Bundle,轉爲Virtual DOM,再經過所在平臺不一樣的API方法,構建頁面進行雙向的數據交互和響應

 
 

2.三、打包

 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

Google 出品,Dart語言,Flutter Engine引擎,響應式設計模式,原生渲染

 Flutter 是谷歌2018年發佈的跨平臺移動UI框架。相較於本人已經在項目中使用過 react native 和 Weex,Flutter目前僅僅是簡單運行過Demo,畢竟仍是beta 階段,這裏更多的聊一下它的實現機制和效果。

 與 react native 和 weex 的經過 Javascript 開發不一樣,Flutter 的編程語言是Drat,(谷歌親兒子,聽說是由於 Drat 項目組就在 Flutter 隔壁而被選上(◐‿◑))因此執行時並不須要 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小不少。

3、對比

 這算是互相傷害的環節了吧。(///▽///)

類型 React Native Weex Flutter
平臺實現 JavaScript JavaScript 無橋接,原生編碼
引擎 JSCore JS V8 Flutter engine
核心語言 React Vue Dart
Apk大小 (Release) 7.6M 10.6M 8.1M
bundle文件大小 默認單1、較大 較小、多頁面可多文件 不須要
上手難度 稍高? 容易 通常
框架程度 較重 較輕
特色(不侷限) 適合開發總體App 適合單頁面 適合開發總體App
社區 豐富,Facebook重點維護 有點殘念,託管apache 剛剛出道小鮮肉,擁護者衆多
支持 Android、IOS Android、IOS、Web Android、IOS(並不止?)

一、大小

 上面Apk大小是經過 react-native initweex create和 flutter 建立出的工程後,直接不添加任何代碼,打包出來的 release 簽名 apk 大小。從下圖能夠看出,其中大比例都是在so庫。

 
 

二、社羣

 react native 做爲 Facebook 主力開源項目之一,至今已有各種豐富的第三方庫,甚至如 realmlottie等開源項目也有 react native 相關的版本,社羣實際無需質疑。固然,由於並徹底正統開發平臺,第三庫的健壯性和兼容性有時候老是參差不齊。

 weex 其實有種生錯在國內的感受。其實 weex 的設計和理念都很優秀,性能也不錯,可是對比 react native 的第三方支持,就顯得有點後媽養的。2016年開源至今,社區和各種文檔都顯得有點疲弱,做爲跨平臺開發人員,大多時候確定不會但願,須要頻繁的本身增長原生功能支持,由於這樣的工做一多,反而會與跨平臺開發的理念背道而馳,帶來開發成本被維護難度增長。

 Flutter目前還處理beta階段,可是谷歌的號召力一直很可觀,這一點無需質疑。

三、性能

 理論上 flutter 的性能應該是最好的,可是目前實際體驗中,卻並無感覺出來太大的差距,和 react native(0.5.0以後)、weex 在性能上我的體驗差別不是很大。固然,這裏並無實測渲染的毫秒時間和幀率數據。

四、其餘區別

  • Weex的多頁面實現問題

 weex 在 native 端是不支持 <keep-alive>的,這一點和 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代碼中也會添加跟蹤修改。

4、將來趨勢

  咱們選擇框架的時候,不少時候會關注框架的成熟度和生命力不是麼(◐‿◑)。

一、React Native

  「Airbnb 宣佈放棄使用 React Native,迴歸使用原生技術」: Airbnb 做爲 react native 平臺上最大的支持者之一,其開源的lottie一樣是支持原生和 react native。

 Airbnb 在宣佈放棄的文中,也對 react native 的表示了很大量的確定,而使得他們放棄的理由,其實主要仍是集中於項目龐大以後的維護困難,第三方庫的參差不齊,兼容上須要耗費更多的精力致使放棄。

ps:( Lottie庫Airbnb出的是一個可以幫助解析AE導出的包含動畫信息的json文件。這很好的解決了一個矛盾,設計師能夠更專一的設計出各類炫酷的動畫效果,而開發只須要將其加入支持便可。)

  Facebook 正在重構 React Native,將重寫大量底層。在經歷了開源協議風波後,能夠看出 Facebook 對於 react native 仍是很看重的, 這些底層重構優化的地方,主要集中於:

 首先,改變線程模型。UI 更新再也不須要在三個不一樣的線程上執行,而是能夠在任意線程上同步調用 JavaScript 進行優先更新,同時將低優先級工做推出主線程,以便保持對 UI 的響應。
 其次,將異步渲染功能引入 React Native 中,容許執行多個渲染並簡化異步數據處理。
 最後,簡化橋接,讓它更快、更輕量。原生和 JavaScript 之間的直接調用效率更高,而且能夠更輕鬆地構建調試工具,如跨語言堆棧跟蹤。

二、Weex

  沒有死!阿里公開Weex技術架構,還開源了一大波組件。2018年初的新聞能夠看出,weex 的遭遇有點相似曾經的 Duddo(Dubbo由於內部競爭被阿里一度放棄維護),這波詐屍後 weex被託管到了Apache,而github的 weexteam現在也還保持着更新,但願後續能有多好的發展,拭目以待吧。

三、Flutter

 Flutter 是 Google 跨平臺移動UI框架,Dart做爲谷歌的親兒子在 Flutter 中使用,而且谷歌新操做系統 Fuchsia 支持 Dart,使用 Flutter 做爲操做UI框架。這些集合到一塊兒不免讓你懷疑 Android 是否要被谷歌拋棄的想法。

 或者現在先 Android 等平臺上推廣 Flutter 與 Dart,就是爲了之後跟好的過渡到新系統上,畢竟開發者是操做系統的生命源泉之一。而 Java 與 JVM 或者能夠被谷歌徹底拋開。固然,目前看起來 Flutter 貌似還缺乏一些語法糖,嵌套下來的代碼有點不忍直視,或者到正式版以後,咱們更能感覺出它的美麗吧。

最後

 內容有點長,其實不少點並無細緻的展開說明,可是經過本文,對於移動端跨平臺的現狀與將來,但願可能給你帶來一點幫助。

超完整跨平臺開源項目

類型 連接
react-native https://github.com/CarGuo/GSYGithubApp
weex https://github.com/CarGuo/GSYGithubAppWeex
Flutter https://github.com/CarGuo/GSYGithubAppFlutter
做者:戀貓月亮 連接:https://www.jianshu.com/p/7e0bd4708ba7 來源:簡書 簡書著做權歸做者全部,任何形式的轉載都請聯繫做者得到受權並註明出處。
相關文章
相關標籤/搜索