注:本文爲轉載文章,部份內容參考移動端跨平臺開發的深度解析,並作了精簡和加工。前端
移動跨平臺開發一直是移動開發者和前端開發者追求的的話題,從早期的cordova、ionic,到現在的react native、weex、kotlin native和flutter等,能夠說現在的跨平臺框架可謂百花齊放,很有一股推倒原生開發者的勢頭。vue
若是要對目前的跨平臺的方案進行一個總結,大體能夠分爲如下幾個流派: JavaScript流派:這一流派中,最明顯的特徵是使用JavaScript做爲編程語言,react native、weex均屬於這一流派。和其餘跨平臺方案相比,JavaScript在跨平臺開發中,使用者最多,大有「一統天下」的趨勢。 VM虛擬機:與其餘方案不一樣,kotlin提供的kotlin-native技術擁有本身的VM,能夠同時支持Android、iOS 和 Web 開發。 Flutter:Futter是Google開源的移動跨平臺UI框架,使用的是Google本身的Dart編程語言,因爲是Google推出的產品,於是也受到不少開發者的喜好。node
不過,綜合對比開發現,目前最火的跨平臺開發方案,特別是已經商用的跨平臺開發框架中,react native無疑是老大,其次是Weex(畢竟是阿里的產品),最後多是最近才火起來的Flutter。react
曾經,React Native的口號是「Learn once, write anywhere」,這句話表明了FaceBook對React Native設計的初衷:學習 react ,同時掌握 web 與 app 兩種開發技能。android
藉助FaceBook旗下的React的設計模式 , React Native使用的UI渲染、動畫效果、網絡請求等會轉換成原生端的實現。也就是說,開發者編寫的js代碼,經過 react native 的中間層(JavaScriptCore)轉化爲原生控件和操做,這就最大程度的接近原生應用的用戶體驗,並提升了開發的效率。webpack
React Native的跨平臺是實現主要由三層構成,其中 C++ 實現的動態連結庫(.so),做爲中間適配層橋接,實現了js端與原生端的雙向通訊交互。ios
這裏最主要是封裝了 JavaScriptCore 執行js的解析,而 react native 運行在JavaScriptCore中,因此不存在瀏覽器兼容的問題。其結構如以下圖:web
React Native實現的原理其實就是利用JS 調用Native 端的組件,並使用Native的組件來繪製界面,從而達到媲美原生應用的效果。apache
和前端開發不一樣,React Native 所使用的標籤並非真實的控件,React Native提供的組件會Dom 轉換爲Native的控件進行渲染。例如<Text>
標籤會被轉換爲Android 中對應 TextView 控件。編程
須要說明的是,在React Native 中,JS端是運行在獨立的線程中(稱爲JS Thread ),JS Thread 做爲單線程邏輯,不可能處理耗時的操做。那麼如 fetch 、圖片加載 、 數據持久化等操做,在 Android 中實際對應的是 okhttp 、Fresco 、SharedPreferences等。而跨線程通訊,也意味着 Js Thread 和原生之間交互與通信是異步的。
由此能夠看出,跨平臺的關鍵在於C++層,開發人員大部分時候,只專一於JS 端的代碼實現便可,無線瞭解底層的實現細節。 而若是要實現和原生模塊的交互,只須要在原生端提供的各類 Native Module 模塊(如網絡請求,ViewGroup控件)便可,而後經過 JS 端提供的各類 JS Module(如JS EventEmiter模塊)來實現調用。而且這些調用都會在C++實現的so中保存起來,雙方的通信經過C++中的保存的映射,最終實現兩端的交互,通訊的數據和指令,在中間層會被轉爲String字符串傳輸,雙向的調用流程以下圖。
在React Native混合項目中,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。
Weex是阿里巴巴開源的一套移動跨平臺開發框架,可以完美兼顧性能與動態性,讓移動開發者經過簡捷的前端語法寫出Native級別的性能體驗,並支持iOS、安卓、YunOS及Web等多端部署。目前,使用Weex框架的多半是阿里系的產品和一些創業的公司。
Weex的口號是「Write once, run everywhere」,Weex使用的耳熟能詳的Vue,阿里的思惟是:寫個 vue 前端,順便完成一個apk 和 ipa,但其實仍是有差距的。Weex支持 web、android、ios 三端,原生端一樣經過中間層轉化,將控件和操做轉化爲原生邏輯來提升用戶體驗。。
在 Weex的架構層次中,主要包括三大部分:JS Bridge、Render、Dom,分別對應WXBridgeManager、WXRenderManager、WXDomManager,三部分經過WXSDKManager統一管理。其中 JS Bridge 和 Dom 都運行在獨立的 HandlerThread 中,而 Render 運行在 UI 線程。關於Weex架構分層的內容讀者能夠自行查看官方資料的介紹。
其中,JS Bridge 主要用來和 JS 端實現進行雙向通訊,好比把 JS 端的 dom 結構傳遞給 Dom 線程。Dom 主要是用於負責 dom 的解析、映射、添加等等的操做,最後通知UI線程更新。而 Render 負責在UI線程中對 dom 實現渲染。
和 React Native同樣,Weex 全部的標籤也不是真實控件,Weex的標籤只不過是JS 代碼中所生成存的 dom,最後都是由 Native 端解析,再獲得對應的Native控件渲染。如 Android 中 <text>
標籤對應 WXTextView 控件。
Weex 中文件默認爲 .vue ,而 vue 文件是被沒法直接運行的,因此 vue 會被編譯成 .js 格式的文件,Weex SDK會負責加載渲染這個js文件。Weex能夠作到跨三端的原理在於:在開發過程當中,代碼模式、編譯過程、模板組件、數據綁定、生命週期等上層語法是一致的。
不一樣的是,在 JS Framework 層的最後,web 平臺和 Native 平臺,對 Virtual DOM 執行的解析方法是有區別的,在渲染真實 UI 的時候調用的接口也不一樣的。
Weex 表面上是一個客戶端技術,但實際上它串聯起了從本地開發、雲端部署到分發的整個鏈路。開發者首先可在本地像編寫 web 頁面同樣編寫一個 app 的界面,而後經過命令行工具將之編譯成一段 JavaScript 代碼,生成一個 Weex 的 JS bundle;同時,開發者能夠將生成的 JS bundle 部署至雲端,而後經過網絡請求或預下發的方式加載至用戶的移動應用客戶端;在移動應用客戶端裏,Weex SDK 會準備好一個 JavaScript 執行環境,而且在用戶打開一個 Weex 頁面時在這個執行環境中執行相應的 JS bundle,並將執行過程當中產生的各類命令發送到 native 端進行界面渲染、數據存儲、網絡通訊、調用設備功能及用戶交互響應等功能;同時,若是用戶但願使用瀏覽器訪問這個界面,那麼他能夠在瀏覽器裏打開一個相同的 web 頁面,這個頁面和移動應用使用相同的頁面源代碼,但被編譯成適合Web展現的JS Bundle,經過瀏覽器裏的 JavaScript 引擎及 Weex SDK 運行起來的。 其中, Native 加載bundle 文件大體經歷瞭如下階段:相比React Native,Weex主要是在JS V8的引擎上多了 JS Framework 承當了重要的職責,使得上層具有統一性,能夠支持跨三個平臺。總的來講它主要負責是:管理Weex的生命週期,解析JS Bundle,轉爲Virtual DOM,再經過所在平臺不一樣的API方法,構建頁面;進行雙向的數據交互和響應。
在打包方案上,Weex和React Native都經過 Webpack 來打包bundle 文件的。不過,React Native打包若是不作拆分,打出的包是很大的,於是會本身制定一些拆包的規則。而Weex 做爲React Native以後出現的跨平臺實現方案,天然能夠站在前人的肩膀上優化問題,好比:Bundle文件過大問題。 Weex 選擇使用JS Framework 集成到 WeexSDK的方式,必定程度減小了JS Bundle的體積,使得 bundle 裏面只保留業務代碼。
打包時,weex 是經過 webpack 打包出 bundle 文件的。bundle 文件的打包和 entry.js 文件的配置數量有關,默認狀況下以後一個 entry 文件,天然也就只有一個bundle文件。 在 weex 項目的 webpack.common.conf.js 中能夠看到,其實打包也是區分了 webConfig 和 weexConfig 的不一樣打包方式。
Flutter是Google用以幫助開發者在Ios和Android兩個平臺開發高質量原生應用的全新移動UI框架。與 React Native 和 Weex 框架使用的Javascript 技術不一樣,Flutter 使用的是全新的編程語言Drat,因此執行時並不須要 Javascript 引擎,但實際效果最終也經過原生渲染。
Flutter框架主要分爲 Framework 和 Engine兩層,咱們基於Framework 開發App主要運行在 Engine 上。Engine 是 Flutter 的獨立虛擬機,由它適配和提供跨平臺支持,目前猜想 Flutter 應用程序在 Android 上,是直接運行 Engine 上 因此在是不須要Dalvik虛擬機。其架構圖以下圖所示:
得益於 Engine 層,Flutter 甚至不使用移動平臺的原生控件, 而是使用本身 Engine 來繪製 Widget (Flutter的顯示單元),而 Dart 代碼都是經過 AOT 編譯爲平臺的原生代碼,因此 Flutter 能夠 直接與平臺通訊,不須要JS引擎的橋接。
而Flutter惟一要求系統提供的是canvas,用以實現UI的繪製。不過,Flutter 上 Android 自帶了 Skia,Skia是一個 2D的繪圖引擎庫,跨平臺,因此能夠被嵌入到 Flutter的 iOS SDK中,也使得 Flutter Android SDK要比 iOS SDK小不少。
下面對上面介紹的幾大框架進行一個簡單的對比,以方便讀者進行對比學習。
對比類型 | React Native | Weex | Flutter |
---|---|---|---|
實現技術 | JavaScript | JavaScript | 原生編碼,無橋接 |
引擎 | JS V8 | JSCore | Flutter engine |
使用語言 | React | Vue | Dart |
bundle文件大小 | 默認單1、較大 | 較小、多頁面可多文件 | 不須要 |
上手難度 | 稍高大 | 容易 | 簡單 |
框架難度 | 較重 | 較輕 | 重 |
支持對象 | Android、IOS | Android、IOS、Web | Android、IOS |
上面Apk大小是經過 react-native init、weex create 和 flutter 建立出的工程後,直接不添加任何代碼,打包出來的 release 簽名 apk 大小。從下圖能夠看出,其中大比例都是在so庫。