前言
每當我們評估新技術時要問的第一個問題就是“它會給我們的業務和客戶帶來哪些價值?”,工程師們很容易對閃閃發光的新事物着迷,卻經常會忽略這些新事物其實可能對我們的客戶沒有任何好處,反而只會讓現有的工作流程更加復雜。
flutter最近比較熱鬧,畢竟是Google出品。
但我們不是炒作熱點的媒體,也不是忽悠你交學費的培訓機構,我們作為實際的跨平台開發者,冷靜的分析下這個東東。
flutter是Google為Fuchsia操作系統設計的應用開發方式。
Fuchsia OS要兼容廉價物聯網設備,要求對硬件的消耗降低,並且為了避免與oracle的java打官司,Fuchsia 使用了dart語言+flutter界面庫的方式。
從設計上來看,這套方案的性能確實夠高。dart雖然屬於大前端范疇,但dart是和java一樣的強類型語言,這讓dart虛擬機可以做很多優化,性能方面超出了js。
dart曾經與typescript競爭,誰才是更好的js?但不幸輸給了typescript,chrome也放棄了內置dart虛擬機的計划。
不過dart團隊沒有解散,幾年后,他們借助flutter,再次出現在公眾面前。
性能分析和寫法的對比
flutter作為界面庫(注意它只是界面庫,dart語言是另一個項目),它唯一要干的事情就是渲染界面。不像HTML5,flutter界面庫連視頻、定位等都沒有,就是一個純排版引擎,繪制文字、按鈕、圖片等常用界面控件。
這個排版引擎的特點是簡單、高性能。
在3大主流渲染引擎里,webview、react native/weex、flutter,復雜度依次降低,渲染性能依次上升。(uni-app是雙渲染引擎,webview和weex都內置了,隨便開發者使用切換)
所以我們要清楚,提升性能是有代價的,你究竟想要靈活豐富的css3,還是想要固定flex模式排版,抑或是最簡單但高性能的flutter排版?開發便利性和運行性能不可兼得。
同時我們要明白,性能的差別,並不是因為Google的chrome團隊、Android團隊的技術比同公司的flutter團隊差。而是flutter提供的布局寫法是被限制過的,解析快,所以渲染快。別忘了webview的排版引擎也是世界級工程師用c寫的。
但通過這種方式提升性能的代價,就是布局復雜的界面時,flutter的代碼嵌套的讓人崩潰。
我們先舉個例子,同樣的界面,用HTML和flutter如何實現:
復制代碼<div class="greybox"> <div class=redbox> smaple text </div> </div> .greybox { display: flex; align-items: center; justify-content: center; background-color: #e0e0e0; /* grey 300 */ width: 320px; height: 240px; font: 18px } .redbox { background-color: #ef5350; /* red 400 */ padding: 16px; color: #ffffff }
復制代碼var container = new Container( // grey box child: new Center( child: new Container( // red box child: new Text( "smaple text", style: new TextStyle( color: Colors.white, fontSize: 18.0, ), ), decoration: new BoxDecoration( color: Colors.red[400], ), padding: new EdgeInsets.all(16.0), ), ), width: 320.0, height: 240.0, color: Colors.grey[300], );
可以看出,從代碼的寫法來說,flutter沒有tag和樣式的說法,更沒有選擇器,從頭到尾只有dart語言,它的界面控件是用dart代碼new出來的,每個控件的樣式,是在new的時候設置的json參數。
如果我們要嵌套布局,就要不停的在dart里寫child,同時在dart里給child們設樣式參數。上面的代碼,只是嵌套了1層,實際開發中,dom要嵌套好多層,想象那樣的代碼。。。所以大家都詬病dart是“嵌套地獄”。
或者,你可以這么理解,這是一個只有js,沒有html和css的瀏覽器。你需要用js createElement來創建元素,用js的style方法給每個element設style,反正就是不能寫html和css代碼。前端都已經發展到各種mvc等視圖邏輯分離的架構了,也有了vue組件這種組件化模式方便用各種輪子快速完成界面。你是否能適應dart這種低效的界面開發模式?從開發模式來講,這確實是一種倒退。
瀏覽器的html提供了tag和樣式分離的寫法,還有各種各樣的選擇器,但其實這也是有代價的。它導致webview初始化時要同時先啟動webkit排版引擎來解析這些編寫隨性的html、css,同時還要啟動一個js引擎比如v8或jscore來解析里面的js。
而dart就很簡單,只啟動一個dart引擎,解析嚴格的dart語法,它不會去操心有些標簽未閉合要如何容錯,不會判斷寬度320后面是px還是rem或者是動態計算百分比。
對比這2個引擎初始化時要干的事,差別簡直太大了。
所以從解析效率上,flutter肯定比webview要高。但從編碼靈活性上,flutter寫的代碼,嗯,難看而低效!
flutter使用的也是flex布局思想,這是一個強嵌套布局模型,比web常規排版引擎的嵌套更多。當界面復雜時,flutter的代碼要嵌套幾十層,每層的元素的json樣式都和元素一起混寫在dart代碼里,讓人崩潰。
有人提出是否可以通過一種預編譯的dsl來簡化寫法,讓flutter的開發不這么痛苦。
但這個難度太大了,從嚴格轉換為松散是簡單的,從松散轉換為嚴格幾乎是不可能的。
什么意思呢?比如flutter代碼轉換web代碼,是很簡單的,flutter已經自帶了這個功能。但是想反過來,那可難了。
類似的還有,把typescript轉為js是容易的,反之,不是絕對不可行,但會復雜到你寧願去重寫一套typescript代碼。
flutter的性能高,除了簡單嚴格,還有一個特點,就是邏輯層與視圖層統一,運行在同一套dart虛擬機下。
我們知道rn和weex,也是原生渲染的,它們的性能高於webview。但同為原生渲染的,怎么會慢於flutter呢?其實不是原生渲染慢,而是js和原生通信慢。
rn和weex都采用了獨立的js引擎(iOS是jscore,Android是v8,最新版rn開始在Android上搞自己的js引擎),從js與dart的比較上,性能稍遜一籌。但這不是主要問題,主要問題是,rn、weex的js引擎和原生渲染層是兩個運行環境。
當js引擎聯網獲取到數據后,通知原生視圖層更新界面時,有一個跨環境的通信折損。同樣,當用戶在屏幕上操作原生視圖層時,要給js引擎發送通知,也會產生這個通信折損。
不過這種性能差別,在大多數場景中,用戶是感受不到的。比較影響的場景,是跟手式的js響應操作繪制幀動畫。
這方面,weex有個值得稱贊的技術是BindingX,它可以預定義規則,讓用戶界面在原生層交互時通過預定義規則直接響應,而無需傳遞給js層。在需要短時間內來回通信的場景時,可以使用BindingX這類解決方案。它的性能和靈活性比rn更強了一些。
說回來flutter,它只有一個dart引擎,沒有來回通信產生的性能問題。不過任何事情都是有利有弊的,flutter在普通的界面繪制上效率雖然高,但一旦涉及原生的界面,反而會遇到更多問題。
前面已經說過,flutter只是一個基礎排版引擎,缺少很多能力,當我們需要在flutter界面上內嵌一個原生的視頻播放擴展控件時(flutter沒有視頻播放能力),或者原生的高德地圖sdk,那么在拖動視頻進度時、拖動地圖時,flutter一樣會產生原生和dart之間的通信,造成性能損耗。
事實上,由於flutter是在一個類canvas環境繪制的,想把一個原生控件嵌入flutter的布局里某些元素之間去排版,還不是一件容易做到的事情,坑很多。
每個人都想要一個像css3那樣靈活寫法的布局引擎,他們給react native和weex提需求,給flutter提需求。殊不知,讓這些產品團隊實現了css3時,他們的性能優勢已經不再了,他們相當於又實現了一遍webview。這種無意義的需求,他們是不會受理了。
性能好,有個度,客觀地講,rn/weex調用原生渲染的性能,和flutter的渲染性能,在用戶體驗上並沒有明顯區別,甚至在很多場景下,和webview渲染的小程序也沒有明顯區別。
也簡單說說webview渲染小程序,為什么性能高,核心是預載。點擊一個新頁面時,webview是提前創建好的,不會走復雜的webkit、v8的初始化流程,連開發者的js代碼,也是預載好的。所以點擊新頁面時,它的渲染速度和原生應用沒什么差別。當然也有個壞處,就是啟動慢。微信里啟動小程序速度看着還行,其實是微信在啟動小程序之前,就已經提前初始化了小程序運行環境。
即便是排版引擎,ui庫好用嗎?
不管是rn還是flutter,有一個設計,很不中國化。它們在iOS和Android平台上,使用2套ui庫。
比如flutter,在iOS上寫一個button,要用CupertinoButton,是iOS風格的控件,在Android上則要用RaisedButton,是Material風格的控件。
rn也是如此,它的官方說法是:learn once,write anywhere。它都不敢說:write once,run anywhere。因為它確實要求開發者寫2套代碼。
中國的開發者可沒有這種習慣,中國的每個開發者,為了避免用戶換手機后不會用自己的app,都會使用中性的設計。
就連微信Android版,底部的tab也是仿iOS而不是Material風格(Material風格是把底tab放在頂部的,並且左右滑動,微信曾經有這樣一個臨時版本,因為被用戶吐槽,很快就下掉了)。
這種中外差異怎么造成的?
國外Android手機,其手機主界面就是強烈的Material風格。用戶在Android主界面習慣的風格和使用方式,如果啟動一個App后不是這樣,會導致用戶不會用了。
Google也一再給Android開發者強調,App必須使用Material風格。這其實也是一個防止用戶切換脫離Android的策略設計。
所以國外開發者的App,Android上都會遵循Material風格,當然,這種Material風格的App是上不了Apple的Appstore的。
這就導致他們默認就是要寫2套ui的,所以rn和flutter都是iOS、Android各自1套ui控件。
但在中國,我們的國產Android Rom,根本不是Material風格,很多rom以仿iOS體驗為賣點。
所以中國的App,全都是貼近iOS的中性風格,中國的用戶換了手機,不管是手機os本身,還是App的使用,都不會造成切換障礙。
rn和flutter這種“跨平台”排版引擎,其跨平台性,對於中國開發者而言,又打了折扣。
其實類似小程序那樣的ui風格,是能夠良好的跨iOS和Android的體驗的,不管用什么手機,打開小程序都不會覺得有問題。
uni-app默認也是這種通用ui風格。uni-app的開發者只需要寫一套界面ui,就可以適應不同手機的用戶,真正的 write once,run anywhere。
動態性
webview、rn/weex,都有一個特點,可以遠程動態載入js代碼,可以更新本地的js代碼。前端開發者認為動態性是天經地義的,但其實flutter並不支持。
flutter是有編譯優化概念的,如果它提供動態性支持,會影響它的性能。
業內有些開發者,改造了flutter,用一個獨立的v8/jscore來加載動態js代碼,去操作flutter布局引擎的渲染。好像還有些人在追捧這樣的方案,簡直是閑得蛋疼。
flutter本來沒有跨環境通信的問題,結果又弄了一個js引擎進來搞出了通信問題,造成性能下降,還把包體積增加了很大,還不如直接用rn/weex。
除了flutter,rn/weex/uni-app都可以動態熱更新。
跨平台排版引擎和跨平台應用開發引擎的區別
有些人說他們的App用rn/weex、flutter。但是具體用它們做了什么呢?
是整個App用了它們,還是某個頁面用了它們?
一個頁面跨平台,和一個應用跨平台,是完全不同的2個概念。
webview、rn/weex、flutter全部是渲染引擎,webview因為HTML5的發展,還算是多了一些能力比如位置服務、多媒體等。而rn/weex、flutter真的只是一個純粹的排版引擎,沒有任何原生能力。
如果一個原生應用里,某個不涉及原生能力的界面想跨平台,那么這幾個引擎都可以,並且flutter的性能最高。所以能看到一些公司嘗試把App中的個別原生交互較少頁面使用flutter實現。
但如果一個完整的應用,想用跨平台工具開發,那就不是排版引擎的范疇了,它需要應用開發引擎。
什么是跨平台應用開發引擎?不但排版部分要跨平台,開發API也要跨平台。
應用開發離不開os或三方sdk的能力調用,如果是單純的排版引擎,一旦涉及os能力和sdk調用,就必須iOS、Android的工程師配合,編寫不同的原生代碼整合在一起。這就不跨平台了。
Airbnb曾是React Native 框架的倡導者和開發者代表。但他們於2019年正式發公告,棄用了react native。
原因是什么?
很簡單,react native並不能提升Airbnb的開發效率,反而降低了他們的效率。
“本來我們可以只維護Android和iOS兩套代碼,但現在我們要維護三套(指多了一套react native的js代碼),這讓我們很疲憊” -- aribnb
開發者選用跨平台開發引擎,本來是為了提高效率、降低成本。Airbnb正是在實踐了幾年后,發現rn根本無法實現他選用跨平台引擎的初衷時,無奈放棄了rn,用原生開發重寫。
要想真的提升開發效率,降低開發成本,那么跨平台開發引擎,需要提供一個完整的應用開發平台,包含所有常用的應用開發能力的跨平台。在不常用的部分,提供插件市場以及免原生介入的插件使用方式。
在react native、flutter的社區,也有不少三方提供的原生插件,但是連Airbnb這樣的國外開發者對此都不滿意。更何況對於很多中國開發者常用的場景,其對應的插件的質量、跨端性都難以商用。
更麻煩的是如果你不會原生開發,就沒法把這些插件與你的前端代碼集成起來。
uni-app,它的設計目標不是跨平台排版引擎,而是跨平台應用開發引擎。
所以uni-app的排版部分,可以選擇小程序強化webview引擎和weex引擎,可根據自己的需求切換。而能力層面,uni-app提供了htmlplus API、Native.js、插件市場,解決了原生能力js化的問題。
uni-app讓開發者真的不用懂原生開發就能做出完整的跨平台應用。遇到極個別的需求,開發者也可以去插件市場找人訂做一個原生插件,自己仍然使用js來集成,仍然可以雲端直接打包。
技術學習成本和難度
rn,要求開發者學習react,要求精通flex布局,要求原生開發協作。
flutter,要求開發者學習dart,了解dart和flutter的API、要求精通flex布局,要求原生開發協作。
weex已經內嵌到uni-app中,就不單獨提了。
uni-app,要求開發者學習vue,了解小程序。
很明顯uni-app的學習成本太低了,它沒有附加專有技術,全部使用公共技術。
學習成本和難度,直接意味着:開發成本、招聘成本、上線速度、上線風險。
另外,dart究竟值不值得學,是一個大問題。
Google的天才工程師也發明了go語言,它確實有很多理論優勢,但實際上市場的主流,仍然是c和c++。
生態
任何開發引擎,都離不開生態。
對於國外的開發者,rn、flutter的生態肯定比uni-app好,比如facebook登陸分享、Google地圖等。
但對於國內的開發者,那是反過來的,中國開發者需要的全端推送(UniPush集成了iOS、華為、小米、OPPO等眾多原廠推送)、各種國內登陸、支付、分享SDK、各種國內地圖、各種ui庫、以及Echart圖表等,都是在uni-app體系里,這方面生態可比rn、flutter豐富多了。
其他端的跨端性
flutter和rn,都是支持web技術的。但都是僅限於普通界面排版,涉及定位、攝像頭、相冊什么的,是要單獨寫代碼的。
另外flutter的H5版,嗯,作為中國開發者,你不會想要一個如此濃郁的Material風格的H5版的。。。
更何況這個Material ui庫大的很,編譯出來的H5版要十幾M的體積。
uni-app的H5端是包含完善的能力引擎的,豐富能力都可以直接跨端使用,風格也是跨端風格。uni-app的H5引擎體積只有1百K,gzip后只剩下30k(不含vue、vue rooter),比其他工具的引擎體積要小的多。
另外,中國離不開小程序,rn、flutter官方都不會支持小程序,由於架構差異太大,國內三方也做不到把rn代碼良好的編譯為小程序代碼。uni-app則可以一套代碼,同時編譯為iOS、Android、H5、微信小程序、支付寶小程序、百度小程序、頭條小程序、QQ小程序。
結論
每種技術的誕生,有其背后公司的目的。
但凡沒有明確公司戰略的技術,除非是特別簡單的技術,否則很難商用,因為為了商用要投入公司非常多資源。
flutter誕生的目的,是為了Fuchsia OS,是為了在下一個互聯網大潮,即萬物互聯的物聯網年代,提供一個類似Android在移動互聯網位置的壟斷性操作系統。
因為Google已經很明確不會在下一個時代使用Android+java的路線了。
至於在Android上去java化,那是Kotlin的使命,與flutter無關。
跨iOS和Android平台開發,這不是Google的戰略目標。
但萬物互聯何時到來?Fuchsia OS何時流行?這在現實中是一個問號,在Google內部,也只是戰略儲備項目。
一個語言的流行,不是一件簡單的事情,不是有優點,就會流行,它需要天時地利人和。
6年前我們就知道dart比js更好,dart不應該消亡,但想成為主流技術,太難太難了。
同樣我們也知道go比c++更好,但go還是起不來。
想靠flutter驅動dart流行是不現實的,甚至是反過來的。跨iOS、Android開發在國外不是主流市場,這點價值造就不出一個這么難建的生態。
所以dart能否流行,是要打一個大大的問號的,它可能會像go語言一樣,叫好不叫座。
寫了這么多,最后總結下flutter與uni-app的比較:
- flutter的相對優勢:
- 性能好一丟丟。比rn有優勢,但比擁有bindingx的weex/uni-app,在實際開發中沒有很明顯的差距。
- flutter的相對劣勢:
- 需要原生協作,維護3套代碼,無法有效降低開發成本,提升開發效率
- 嵌套地獄,代碼難看難維護
- 不支持熱更新
- 目前質量和成熟度很低,github上的issue有5k+,很容易掉坑里
- 原生可視控件融合不好,比如webview、video、map
- ui庫不適合國情
- 學習成本高
- dart未來撲朔迷離
再總結下rn和uni-app的比較
- rn的相對優勢:
- rn的坑雖然比weex的少,但uni-app已經填了weex的很多坑。這方面差別不大。
- rn的生態雖然比weex豐富。但uni-app是反過來的,uni-app的國內應用生態豐富度超過了rn。
- rn的app冷啟動比uni-app快。這個問題uni-app已經內部改進完畢,下個版本發布就能解決。
- rn是純單頁的,嵌入原生App比較靈活。而uni-app是應用整體的概念,如果要內嵌入其他原生應用的話,要求原生應用內嵌uni-app應用整體進來
- rn的相對劣勢:
- 需要原生協作,維護3套代碼,無法有效降低開發成本,提升開發效率
- 不支持小程序,發布到h5也無法直接發
- ui庫不適合國情,learn once,write anywhere
- 學習成本高,用人成本高,不利於開發商降低開發成本
- rn是純單頁應用,如果一個應用的頁面很多,用rn寫會很崩潰,變量污染和干擾嚴重。而weex/uni-app支持多頁面,頁面之間上下文隔離,寫頁面較多的大型應用更合適
另外react在中國的市場占有率遠不如vue。這也是中國與國外不同的特色情況。
貼個vue、react、react native的百度指數對比,無論總體量的差距,還是發展趨勢的下滑程度,可以明顯看出react系在中國確實不行了。

中國的開發者,過去總會想:
- 小程序那套webview優化的技術,我能不能用到我的App里?現在uni-app已經為你解決了這個問題。
- weex能不能坑少點,API和插件多點?現在uni-app已經為你解決了這個問題。
如果你是一個資源充沛的大公司,原生App中部分不要求動態性、也沒有太多原生交互的頁面,可以嘗試使用flutter實現。但如果大范圍使用,你也會遇到和aribnb一樣的問題,維護3套代碼還不如維護2套代碼。
如果你開發uni-app選用了weex原生渲染,那App的性能足夠好,且你得到了切實的開發效率的提升、成本的下降、快速和低風險的上線。
選擇跨平台工具而不是原生開發,本質目的不就是為了成本和效率嗎?能真正解決你本質需求的,就是uni-app。