跨平臺一直是老生常談的話題,cordova、ionic、react-native、weex、kotlin-native、flutter等跨平臺框架的百花齊放,頗有一股推倒原生開發(fā)者的勢頭(事實上更多是共存發(fā)展)看完本篇,相信你會對于當下跨平臺移動開發(fā)的現(xiàn)狀、實現(xiàn)原理、框架的選擇等有更深入的理解。
一、前言
為什么我們需要跨平臺開發(fā)?
本質上,跨平臺開發(fā)是為了增加代碼復用,減少開發(fā)者對多個平臺差異適配的工作量,降低開發(fā)成本,提高業(yè)務專注的同時,提供比web更好的體驗。嗯~通俗了說就是:省錢、偷懶。
本篇主要以react-native、weex、flutter,結合資訊展望,深入聊聊當前跨平臺移動開發(fā)的實現(xiàn)原理、現(xiàn)狀與未來。至于為什么只講它們,因為對比ionic、phoneGap,它們更于
“naive” (? ̄? ??  ̄??)。
二、原理與特性
目前移動端跨平臺開發(fā)中,大致歸納為以下幾種情況:
react native、weex均使用JavaScript作為編程語言,目前JavaScript在跨平臺開發(fā)中,可謂占據(jù)半壁江山,大有“一統(tǒng)天下”的趨勢。
kotlin-native開始支持 iOS 和 Web 開發(fā),(kotlin已經(jīng)成為android的一級語言)也想嘗試“一統(tǒng)天下”。
flutter是Google跨平臺移動UI框架,Dart作為谷歌的親兒子,毫無疑問Dart成為flutter的編程語言,如下圖,作為巨頭新生兒,在flutter官網(wǎng)也可以看出,flutter同樣“心懷天下”。
1、React Native
Facebook 出品,JavaScript語言,JSCore引擎,React設計模式,原生渲染
1.1、理念架構
“Learn once, write anywhere” ,代表著 Facebook對 react native 的定義:學習 react ,同時掌握
web 與 app 兩種開發(fā)技能。 react native ?用了 react
的設計模式,但UI渲染、動畫效果、網(wǎng)絡請求等均由原生端實現(xiàn)。開發(fā)者編寫的js代碼,通過 react native
的中間層轉化為原生控件和操作,比ionic等跨平臺應用,大大提高了的用戶體驗。
總結起來其實就是利用 JS 來調用 Native 端的組件,從而實現(xiàn)相應的功能。
如下圖所示,react native 的跨平臺是實現(xiàn)主要由三層構成,其中 C++
實現(xiàn)的動態(tài)連結庫(.so),作為中間適配層橋接,實現(xiàn)了js端與原生端的雙向通信交互。這里最主要是封裝了 JavaScriptCore 執(zhí)行js的解析,而
react native 運行在JavaScriptCore中,所以不存在瀏覽器兼容的問題。
其中在IOS上直接使用內置的javascriptcore, 在Android 則使用webkit.org官方開源的jsc.so。
1.2、實現(xiàn)原理
和前端開發(fā)不同,react native 所有的標簽都不是真實控件,JS代碼中所寫控件的作用,類似 Map 中的 key 值。JS端通過這個 key 組合的
Dom ,最后Native端會解析這個 Dom ,得到對應的Native控件渲染,如 Android 中<view> 標簽對應 ViewGroup 控件。
在 react native ?中,JS端是運行在獨立的線程中(稱為JS Thread )。JS Thread 作為單線程邏輯,不可能處理耗時的操作。那么如
fetch 、圖片加載 、 數(shù)據(jù)持久化 等操作,在 Android 中實際對應的是 okhttp 、Fresco
、SharedPreferences等。而跨線程通信,也意味著 Js Thread 和原生之間交互與通訊是異步的。
可以看出,跨平臺的關鍵在于C++層,開發(fā)人員大部分時候,只專注于JS 端的代碼實現(xiàn)。 在原生端提供的各種 Native Module
模塊(如網(wǎng)絡請求,ViewGroup控件),和 JS 端提供的各種 JS Module(如JS
EventEmiter模塊),都會在C++實現(xiàn)的so中保存起來,雙方的通訊通過C++中的保存的映射,最終實現(xiàn)兩端的交互。通信的數(shù)據(jù)和指令,在中間層會被轉為String字符串傳輸,雙向的調用流程如下圖。
1.3、打包加載
最終,JS代碼會被打包成一個 bundle 文件,自動添加到 App 的資源目錄下。react native
的打包腳本目錄為/node_modules/react-native/local-cli,打包最后會通過 metro 模塊壓縮 bundle
文件。而bundle文件只會打包js代碼,自然不會包含圖片等靜態(tài)資源,所以打包后的靜態(tài)資源,其實是被拷貝到對應的平臺資源文件夾中。
其中圖片等存在資源的映射規(guī)則,比如放在 react native 項目根目錄下的 ?img/pic/logo.png 的資源,編譯時,會被重命名后,根據(jù)大小
merged 到對應的是drawable目錄下,修改名稱為img_pic_logo.png。
打包Android和IOS,肯定需要相應的平臺項目存在,在 react-native init 時創(chuàng)建的項目,就已經(jīng)包含了 android 和 ios
的模版工程,打包完的工程會加載bundle文件,然后啟動項目。這里就不展(tou)開(lan)了,有興趣的可以看:React Native For
Android 架構初探。
圖片來源QQ空間移動開發(fā)團隊,完成啟動流程圖
2、WEEX
Alibaba 出品,JavaScript語言,JS V8引擎,Vue設計模式,原生渲染
2.1、理念架構
“Write once, run everywhere”, weex的定義就像是:寫個 vue 前端,順便幫你編譯成性能還不錯的 apk 和
ipa(當然,現(xiàn)實有時很骨感)?;?Vue 設計模式,支持 web、android、ios
三端,原生端同樣通過中間層轉化,將控件和操作轉化為原生邏輯來提高用戶體驗。
在 weex 中,主要包括三大部分:JS
Bridge、Render、Dom,分別對應WXBridgeManager、WXRenderManager、WXDomManager,三部分通過WXSDKManager統(tǒng)一管理。其中
JS Bridge 和 Dom 都運行在獨立的 HandlerThread 中,而 Render 運行在 UI 線程。
JS Bridge 主要用來和 JS 端實現(xiàn)進行雙向通信,比如把 JS 端的 dom 結構傳遞給 Dom 線程。Dom 主要是用于負責 dom
的解析、映射、添加等等的操作,最后通知UI線程更新。而 Render 負責在UI線程中對 dom 實現(xiàn)渲染。
2.2、實現(xiàn)原理
和 react native一樣,weex 所有的標簽也不是真實控件,JS 代碼中所生成存的 dom,最后都是由 Native
端解析,再得到對應的Native控件渲染,如 Android 中 <text> 標簽對應 WXTextView 控件。
weex 中文件默認為 .vue ,而 vue ?文件是被無法直接運行的,所以 vue 會被編譯成 .js 格式的文件,Weex
SDK會負責加載渲染這個js文件。Weex可以做到跨三端的原理在于:在開發(fā)過程中,代碼模式、編譯過程、模板組件、數(shù)據(jù)綁定、生命周期等上層語法是一致的。不同的是在
JS Framework 層的最后,web 平臺和 Native 平臺,對 Virtual DOM 執(zhí)行的解析方法是有區(qū)別的。
實際上,在 Native 中對 bundle 文件的加載大致經(jīng)歷以下階段:
weex 接收到 js 文件以后,JS Framework 根據(jù)文件為 Vue 模式,會調用weex-vue-framework 中提供的
createInstance方法創(chuàng)建實例。(也可能是Rax模式)
createInstance 中會執(zhí)行 Js Entry 代碼里 new Vue() 創(chuàng)建一個組件,通過其 render 函數(shù)創(chuàng)建出 Virtual DOM
節(jié)點。
由JS ?V8 引擎上解析 Virtual DOM ,得到 Json 數(shù)據(jù)發(fā)送至 Dom 線,這里輸出 Json 也是方便跨端的數(shù)據(jù)傳輸。
Dom 線程解析 Json 數(shù)據(jù),得到對應的 WxDomObject,然后創(chuàng)建對應的WxComponent 提交 Render 。
Render在原生端最終處理處理渲染任務,并回調里JS方法。
得益于上層的統(tǒng)一性,只是通過 weex-vue-framework 判斷是由Vue.js 生成真實的 Dom ,還是通過 Native Api
渲染組件,weex 一定程度上上,用JS 實現(xiàn)了 ?vue ?一統(tǒng)天下的效果。
weex 在原生渲染 Render 時,在接收到渲染指令后,會逐步將數(shù)據(jù)渲染成原生組件。Render 通過解析渲染數(shù)據(jù)的描述,然后分發(fā)給不同的模塊。
比如 控件渲染屬于 dom 模塊中,頁面跳轉屬于navigator模塊等。模塊的渲染過程并非一個執(zhí)行完,再執(zhí)行另一個的流程,而是類似流式的過程。如上一個
<text> 的組件還沒渲染好,下一個 <div> 的渲染又發(fā)了過來。這樣當一個組件的嵌套組件很多時,或者可以看到這個大組件內的UI,一個一個渲染出來的過程
weex 比起react native,主要是在JS V8的引擎上,多了 JS Framework
承當了重要的職責,使得上層具備統(tǒng)一性,可以支持跨三個平臺??偟膩碚f它主要負責是:管理Weex的生命周期;解析JS Bundle,轉為Virtual
DOM,再通過所在平臺不同的API方法,構建頁面;進行雙向的數(shù)據(jù)交互和響應。
2.3、打包
weex 作為 react-native 之后出現(xiàn)的跨平臺實現(xiàn)方案,自然可以站在前人的肩膀上優(yōu)化問題,比如:Bundle文件過大問題。
Bundle文件的大小,很大程度上影響了框架的性能,而 weex 選擇將 JS Framework 集成到 WeexSDK 中,一定程度減少了JS
Bundle的體積,使得 bundle 里面只保留業(yè)務代碼。
打包時,weex 是通過 webpack 打包出 bundle 文件的。bundle 文件的打包和 entry.js 文件的配置數(shù)量有關,默認情況下之后一個
entry 文件,自然也就只有一個bundle文件。
在 weex 項目的 webpack.common.conf.js 中可以看到,其實打包也是區(qū)分了 webConfig 和 weexConfig
的不同打包方式。如下圖,其中weexEntry 就是 weex 打包配置的地方,可以看到本來已經(jīng)有 index 和 entry.js
存在了。如果有需要,可配置上你需要的打包頁面,具體這里就不詳細展開了。有興趣可看:Weex原理之帶你去蹲坑 。
3、Flutter
Google 出品,Dart語言,F(xiàn)lutter Engine引擎,響應式設計模式,原生渲染
Flutter 是谷歌2018年發(fā)布的跨平臺移動UI框架。相較于本人已經(jīng)在項目中使用過 react native 和
Weex,F(xiàn)lutter目前僅僅是簡單運行過Demo,畢竟還是beta 階段,這里更多的聊一下它的實現(xiàn)機制和效果。
與 react native 和 weex 的通過 Javascript 開發(fā)不同,F(xiàn)lutter 的編程語言是Drat,(谷歌親兒子,據(jù)說是因為 Drat
項目組就在 Flutter 隔壁而被選上(???))所以執(zhí)行時并不需要 Javascript 引擎,但實際效果最終也通過原生渲染。
如上圖,F(xiàn)lutter 主要分為 Framework ?和 Engine,我們基于Framework 開發(fā)App,運行在 Engine 上。Engine 是
Flutter 的獨立虛擬機,由它適配和提供跨平臺支持,目前猜測 Flutter 應用程序在 Android 上,是直接運行 Engine 上
所以在是不需要Dalvik虛擬機。(這是比kotlin更徹底,拋棄JVM的糾纏?)
如下圖,得益于 Engine 層,F(xiàn)lutter 甚至不使用移動平臺的原生控件, 而是使用自己 ?Engine 來繪制 Widget
(Flutter的顯示單元),而 Dart 代碼都是通過 AOT 編譯為平臺的原生代碼,所以 Flutter 可以 直接與平臺通信,不需要JS引擎的橋接。同時
Flutter 唯一要求系統(tǒng)提供的是 canvas,以實現(xiàn)UI的繪制。咦?這么想來,支持web端也沒問題吧!
在Flutter中,大多數(shù)東西都是widget,而widget是不可變的,僅支持一幀,并且在每一幀上不會直接更新,要更新而必須使用Widget的狀態(tài)。無狀態(tài)和有狀態(tài)
widget 的核心特性是相同的,每一幀它們都會重新構建,有一個State對象,它可以跨幀存儲狀態(tài)數(shù)據(jù)并恢復它。
Flutter 上 Android 自帶了 Skia,Skia是一個 2D的繪圖引擎庫,跨平臺,所以可以被嵌入到 Flutter的 iOS SDK中,也使得
Flutter Android SDK要比 iOS SDK小很多。
三、對比
這算是互相傷害的環(huán)節(jié)了吧。(///▽///)
1、大小
上面Apk大小是通過?react-native init、weex create?和 flutter 創(chuàng)建出的工程后,直接不添加任何代碼,打包出來的
release 簽名 apk 大小。從下圖可以看出,其中大比例都是在so庫。
2、社群
react native 作為 Facebook 主力開源項目之一,至今已有各類豐富的第三方庫,甚至如 realm、lottie ?等開源項目也有
react native 相關的版本,社群實際無需質疑。當然,因為并完全正統(tǒng)開發(fā)平臺,第三庫的健壯性和兼容性有時候總是良莠不齊。
weex 其實有種生錯在國內的感覺。其實 weex 的設計和理念都很優(yōu)秀,性能也不錯,但是對比 react native
的第三方支持,就顯得有點后媽養(yǎng)的。2016年開源至今,社區(qū)和各類文檔都顯得有點疲弱,作為跨平臺開發(fā)人員,大多時候肯定不會希望,需要頻繁的自己增加原生功能支持,因為這樣的工作一多,反而會與跨平臺開發(fā)的理念背道而馳,帶來開發(fā)成本被維護難度增加。
Flutter目前還處理beta階段,但是谷歌的號召力一直很可觀,這一點無需質疑。
3、性能
理論上 flutter 的性能應該是最好的,但是目前實際體驗中,卻并沒有感受出來太大的差距,和 react native(0.5.0之后)、weex
在性能上個人體驗差異不是很大。當然,這里并沒有實測渲染的毫秒時間和幀率數(shù)據(jù)。
四、未來趨勢
我們選擇框架的時候,很多時候會關注框架的成熟度和生命力不是么(???)。
1、React Native
“Airbnb 宣布放棄使用 React Native,回歸使用原生技術” ?: Airbnb 作為 react native
平臺上最大的支持者之一,其開源的lottie 同樣是支持原生和 react native。
Airbnb 在宣布放棄的文中,也對 react native
的表示了很大量的肯定,而使得他們放棄的理由,其實主要還是集中于項目龐大之后的維護困難,第三方庫的良莠不齊,兼容上需要耗費更多的精力導致放棄。
ps:(
Lottie庫Airbnb出的是一個能夠幫助解析AE導出的包含動畫信息的json文件。這很好的解決了一個矛盾,設計師可以更專注的設計出各種炫酷的動畫效果,而開發(fā)只需要將其加入支持即可。)
Facebook 正在重構 React Native,將重寫大量底層。在經(jīng)歷了開源協(xié)議風波后,可以看出 Facebook 對于 react native
還是很看重的, 這些底層重構優(yōu)化的地方,主要集中于:
首先,改變線程模型。UI 更新不再需要在三個不同的線程上執(zhí)行,而是可以在任意線程上同步調用 JavaScript
進行優(yōu)先更新,同時將低優(yōu)先級工作推出主線程,以便保持對 UI 的響應。
其次,將異步渲染功能引入 React Native 中,允許執(zhí)行多個渲染并簡化異步數(shù)據(jù)處理。
最后,簡化橋接,讓它更快、更輕量。原生和 JavaScript 之間的直接調用效率更高,并且可以更輕松地構建調試工具,如跨語言堆棧跟蹤。
2、Weex
沒有死!阿里公開Weex技術架構,還開源了一大波組件。 2018年初的新聞可以看出,weex 的遭遇有點類似曾經(jīng)的
Duddo(Dubbo因為內部競爭被阿里一度放棄維護),這波詐尸后 weex 被托管到了Apache,而github的 weexteam
如今也還保持著更新,希望后續(xù)能有多好的發(fā)展,拭目以待吧。
3、Flutter
Flutter 是 Google 跨平臺移動UI框架,Dart作為谷歌的親兒子在 Flutter 中使用,并且谷歌新操作系統(tǒng) Fuchsia 支持
Dart,使用 Flutter 作為操作UI框架。這些集合到一起難免讓你懷疑 ?Android 是否要被谷歌拋棄的想法。
或者如今先 Android 等平臺上推廣 Flutter 與 Dart,就是為了以后跟好的過渡到新系統(tǒng)上,畢竟開發(fā)者是操作系統(tǒng)的生命源泉之一。而 Java
與 JVM 或者可以被谷歌完全拋開。當然,目前看起來 ?Flutter
貌似還缺少一些語法糖,嵌套下來的代碼有點不忍直視,或者到正式版之后,我們更能感受出它的美麗吧。
五、最后
內容有點長,其實很多點并沒有細致的展開說明,但是通過本文,對于移動端跨平臺的現(xiàn)狀與未來,希望可能給你帶來一點幫助。
原文發(fā)布時間為:2018-11-17
本文作者:戀貓月亮
本文來自云棲社區(qū)合作伙伴“前端大學
<https://yq.aliyun.com/go/articleRenderRedirect?url=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzIzNTU2ODM4Mw%3D%3D%26amp%3Bmid%3D2247487468%26amp%3Bidx%3D1%26amp%3Bsn%3D2b7b3966fac32a95a0e95d9ee423c666%26amp%3Bchksm%3De8e4627cdf93eb6aa26a13c5aad86f1f519e524ef94d31789b4bc0130cdbd75b45062ba1a729%26amp%3Bscene%3D0%26amp%3Bxtrack%3D1%23rd>
”,了解相關信息可以關注“前端大學
<https://yq.aliyun.com/go/articleRenderRedirect?url=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzIzNTU2ODM4Mw%3D%3D%26amp%3Bmid%3D2247487468%26amp%3Bidx%3D1%26amp%3Bsn%3D2b7b3966fac32a95a0e95d9ee423c666%26amp%3Bchksm%3De8e4627cdf93eb6aa26a13c5aad86f1f519e524ef94d31789b4bc0130cdbd75b45062ba1a729%26amp%3Bscene%3D0%26amp%3Bxtrack%3D1%23rd>
”。
熱門工具 換一換