岳奎
(四川理工學(xué)院,四川自貢643000)
最早的跨端跨平臺(tái)解決方案要追溯到PhoneGap 的時(shí)代,也叫作Hybird 時(shí)代,代表作Cordova,同時(shí)期后來也有了基于AngularJS 和Cordova 的增強(qiáng)版Ionic,這些方案都是混合開發(fā)方案。接著迎來了借助原生渲染的方案,先后出現(xiàn)的有React Na‐tive、Weex、uni-app等。之后出現(xiàn)了放棄原生渲染,自建渲染方案的自渲染技術(shù)Flutter。本文主要研究跨端方案的演進(jìn)過程以及各種跨端方案的各自特性。
混合(hybird)開發(fā)時(shí)代的代表作有Apache Cordova[1],其前身就是PhoneGap。它允許開發(fā)者用標(biāo)準(zhǔn)的Web 技術(shù)也就是HTTML5、CSS3和JavaScript來做跨平臺(tái)開發(fā)。應(yīng)用在每個(gè)平臺(tái)的具體執(zhí)行細(xì)節(jié)都被封裝了,并借助封裝過原生標(biāo)準(zhǔn)的API去訪問每個(gè)設(shè)備的功能,比如相機(jī)拍照、地理位置定位、傳感器等。
1.1.1 實(shí)現(xiàn)原理
因?yàn)槭腔赪eb技術(shù),所以必然依賴于WebView才能進(jìn)行頁面渲染展示。那么對(duì)于Android 來講就是利用WebView 技術(shù),對(duì)于iOS 來說就是利用的UIWebView 或者WKWebView。所以Web 和客戶端Native 的通信方式是依賴于WebView 提供的相關(guān)接口實(shí)現(xiàn)的,對(duì)于Android 來講一般常用的相互通信方式是WebView 實(shí)例加載Web 的URL 以及Native 注入全局變量(使用addJavascriptInterface[2])到WebView 中,這樣HTML5 就可以全局可見地調(diào)用該方法。對(duì)于iOS 來說是利用WKWebview里的evaluateJavaScript 方法直接執(zhí)行即可,那么H5 調(diào)用Native的能力是依靠NA 自身的JavaScriptCore 提供接口出去供H5 來調(diào)用。本質(zhì)上兩端通信還有一個(gè)共同的方式就是NA攔截Web的URL(也叫作標(biāo)準(zhǔn)協(xié)議規(guī)范schema)做對(duì)應(yīng)的處理,這是一個(gè)比較常用,而且容易自定義的協(xié)議規(guī)范,實(shí)現(xiàn)也相對(duì)簡單靈活。
1.1.2 混合開發(fā)特性
開發(fā)效率方面,對(duì)前端開發(fā)者友好,前端眾多生態(tài)都可以拿來使用,比傳統(tǒng)的Android 和iOS 開發(fā)效率快很多,但涉及Native 的部分,依然需要去熟悉掌握原理。其次是能力受限于橋接層,擴(kuò)展性低。調(diào)試和錯(cuò)誤日志不友好。熱更能力方面,因?yàn)槭褂肑S,所以是支持熱更。多端一致性方面,因完全依賴于Native的webview層的內(nèi)核渲染,無法媲美原生體驗(yàn),涉及宿主問題必然需要開發(fā)者自己處理。性能方面,因渲染依賴于WebView,所以性能不如Native 直接渲染,特別對(duì)于長列表渲染[4],性能很差。
綜上所述其實(shí)Hybird 方案是一個(gè)矛盾結(jié)合體,既要快,又要性能還要完善的生態(tài)。當(dāng)然開發(fā)者們都看到了此缺陷所以才有了后來大家耳熟能詳?shù)腞eact-Native。
常見的通過Native 渲染的技術(shù)目前有React Native、Weex、uni-app,接下來逐個(gè)分析相關(guān)方案的原理和特性。
架構(gòu)理念是Learn once,write anywhere也就是說掌握了Re‐act 開發(fā),可以同時(shí)掌握Web 和App 兩種開發(fā)技能。React Na‐tive[3]用了react 的設(shè)計(jì)模式,但UI 渲染、動(dòng)畫效果、網(wǎng)絡(luò)請(qǐng)求等功能均由原生實(shí)現(xiàn)。開發(fā)者編寫的js 代碼,通過React Native的中間層轉(zhuǎn)化為原生控件來渲染和響應(yīng)事件,相比ionic等跨平臺(tái)應(yīng)用,極大地提高了用戶體驗(yàn)。
2.1.1 React Native的框架組成、渲染原理、平臺(tái)通信
首先React Native 框架內(nèi)部已提供了很多內(nèi)置組件,如View、Text 等基礎(chǔ)布局組件,一些功能組件如Button、Picker 等,用于列表展示的List 組件和對(duì)應(yīng)Android 平臺(tái)和iOS 平臺(tái)特有的組件、API 等。同時(shí)也提供了編寫與原生平臺(tái)交互的接口。渲染這塊主要還是把React寫好的JSX代碼通過ReactElements轉(zhuǎn)換,然后經(jīng)歷React的部分生命周期再由AppRegistry.register‐Component[3]進(jìn)行注冊(cè),接著通過遍歷dom進(jìn)行diff算法,但這里是要經(jīng)過消息隊(duì)列異步傳遞的,最終把增量的變化傳遞到Na‐tive 側(cè),然后進(jìn)行渲染真正的Native 組件。平臺(tái)通信這塊主要是通過Bridge 來進(jìn)行通信,所有JS 代碼都是要經(jīng)過JavaScript‐Core編譯,然后給到由C/C++編寫的Bridge橋,當(dāng)作中間人進(jìn)行交換通信,再分發(fā)給對(duì)應(yīng)的Android或者iOS宿主。所以這個(gè)過程中最消耗性能的其實(shí)就是渲染時(shí)的消息隊(duì)列通信相關(guān)的增量變化。大致的架構(gòu)圖如圖1所示。
圖1 架構(gòu)圖
2.1.2 React Native特點(diǎn)總結(jié)
開發(fā)效率方面,在Web基礎(chǔ)上引進(jìn)了React能力,符合前端大趨勢(shì),但涉及與Native 交互的地方就需要開發(fā)者自己處理,這點(diǎn)還是需要對(duì)NA比較熟悉,因本地支持熱加載,也有對(duì)應(yīng)配套的調(diào)試dom節(jié)點(diǎn)元素的工具提供,開發(fā)體驗(yàn)是友好的。動(dòng)態(tài)化方面,因?yàn)橛玫氖莚eact,故可以做線上熱更操作,不過需要結(jié)合三方npm 包或者自研NA 相關(guān)的JS Bundle 文件下載、上傳、替換等功能。多端一致性方面,因?yàn)槭卿秩镜腘A組件,所以可以保證和Native 擁有一樣的用戶體驗(yàn),但三方組件依賴性較強(qiáng),比如版本更新不及時(shí),社區(qū)活躍度不高。性能方面,稍差于Native,但遠(yuǎn)好于Hybird,但因?yàn)殇秩緯r(shí)需要JavaScript 和原生之間不停地進(jìn)行數(shù)據(jù)通信,在有些場景如拖動(dòng)可能會(huì)因?yàn)橥ㄐ蓬l繁導(dǎo)致卡頓。其次是因?yàn)镴avaScript 為腳本語言,執(zhí)行時(shí)需要JIT,執(zhí)行效率相比AOT代碼[5]仍有一定的差距。
Weex 也是借助NA 來做渲染的一個(gè)移動(dòng)端跨平臺(tái)方案。它和React Native 很多方面都很相似,但還是有不同點(diǎn),比如它使用的是Vuejs 和Rax 這兩個(gè)任意一個(gè)框架來編寫代碼,同時(shí)Weex 的渲染引擎和DSL 語法層是分開的,這點(diǎn)和React Native理念完全不同。因?yàn)楹芏喽己蚏eact Native 相似,所以這里就簡單闡述。
2.2.1 Weex整體工作流程
首先會(huì)把用vue 寫的項(xiàng)目打包成一份JS Bundle,然后部署到服務(wù)器,客戶端打開某一個(gè)網(wǎng)頁,先下載服務(wù)端的JS Bundle,然后在客戶端本地執(zhí)行該JS Bundle。一般會(huì)用到JavaScript‐Core 或者V8 引擎去執(zhí)行JS Bundle,JS 執(zhí)行引擎和瀏覽器執(zhí)行過程類似,JS Bundle 的代碼被執(zhí)行,生成VNode 樹進(jìn)行patch,找到最小DOM 節(jié)點(diǎn)的操作變化,把DOM 節(jié)點(diǎn)的操作轉(zhuǎn)變?yōu)镹ative DOM API 的操作,然后調(diào)用WXBridge[6]進(jìn)行通信,WX‐Bridge 將渲染指令分發(fā)到Native 渲染引擎,由Native 渲染引擎完成最終的頁面渲染。
2.2.2 Weex的特性
開發(fā)效率方面,和React Native 幾乎沒啥差別,不過本地開發(fā)調(diào)試方面沒有React Native 體驗(yàn)好。動(dòng)態(tài)化方面,天然支持動(dòng)態(tài)化而且還支持拆bundle,React Native當(dāng)然也支持拆包但需要改造。
盡管借助NA 渲染方式相比混合開發(fā)性能高很多,而且方案也很完美,但還有很多不足的地方比如框架自身維護(hù)成本高,主要是因?yàn)樘峁┑慕M件大多依賴原生控件,那么這些An‐droid 和iOS 控件也不是一成不變的,系統(tǒng)廠商也會(huì)做一些迭代,那么就必然要適配,適配成本很高。所以就會(huì)存在天然抹不平雙端的差異,所以才有了后來的自渲染技術(shù)。
含義是通過框架自身的自繪引擎,來實(shí)現(xiàn)布局和繪制到終端,可以認(rèn)為是從另一個(gè)角度的創(chuàng)新,為人們打開的新的方向和思考,自渲染的代表作就是Flutter,接下來我們就一起了解下Flutter的核心架構(gòu)思想以及特性。
Flutter 是以Dart 語言編寫[6],基于Dart 編寫提供了很多組件也叫Widget,有兩套風(fēng)格的Widget,分別Material 和cuperti‐no。兩套風(fēng)格有很多widget,比如Text、Input等,也提供了動(dòng)畫、手勢(shì)等通用的Widget。因?yàn)闆]有采用WebView,也就沒有JS和Native 的通信了,而是自行實(shí)現(xiàn)一套UI 框架,在引擎底層通過Skia[6]渲染到屏幕。對(duì)于UI之外所需要使用的移動(dòng)設(shè)備自身提供的服務(wù),比如相機(jī)拍照、定位等,則采用Platform Channels[6]接口跟原生系統(tǒng)通信的方式來實(shí)現(xiàn)。
效率方面,因?yàn)椴捎胐art 語言編寫,需要先熟悉dart 語言,但熟練后效率有明顯提升,同樣也支持熱Hot Reload 能夠很好地在本地進(jìn)行快速的開發(fā)和調(diào)試。動(dòng)態(tài)化方面,目前iOS是不允許進(jìn)行熱更的,因?yàn)樘O果官方禁止JS Path,當(dāng)然Android是可以熱更,但大環(huán)境目前是不太友好的。平臺(tái)一致性方面,可以做到像素級(jí)UI控制,能做到最大限度的還原,保證最大限度在不同平臺(tái)的體驗(yàn)一致性,目前還支持PC、嵌入式等。性能方面,這塊優(yōu)于以上介紹的所有跨平臺(tái)框架方案,也是最能媲美原生性能的跨平臺(tái)技術(shù)方案,因?yàn)镈art代碼執(zhí)行效率比JS高很多,而且是通過AOT 編譯成平臺(tái)原生代碼[7],渲染采用Skia 方案,既不需要JS Bridge也不涉及虛擬機(jī)[8]。
通過從最早的Apache Cordova 混合開發(fā)技術(shù)的背景開始,分析了當(dāng)時(shí)混合開發(fā)時(shí)代的優(yōu)劣勢(shì),當(dāng)時(shí)的開發(fā)者為此做了很多努力和改進(jìn),讓我們知道當(dāng)時(shí)的技術(shù)方案是可以降本增效的,但有諸多弊端,于是就迎來了原生渲染技術(shù)的時(shí)代,這個(gè)時(shí)代放大了前端生態(tài)的繁榮,很多方案都是借助前端技術(shù)棧作為入口,撬動(dòng)了整個(gè)跨端方案,無論是從體驗(yàn)、性能、一致性上都完勝混合開發(fā),但還是有諸多不完美,比如依賴三方組件,依賴廠商適配,同步迭代更新成本高等問題。最后出現(xiàn)了自渲染技術(shù),這對(duì)于整個(gè)跨平臺(tái)方案來說立意高,創(chuàng)新強(qiáng),一出來就在用戶體驗(yàn)層面和平臺(tái)一致性上完勝依賴原生Native渲染方案,但不支持熱更,有一定的學(xué)習(xí)成本。所以綜上所述,目前性能最佳、一致性最好的方案是自渲染。但如果想支持熱更最佳選擇就是React Native。