首先感謝@栖冰 @祖建國 一起對FFW的預研做的投入!
背景
Google在最新的Google I/O上推出了Flutter for Web,旨在進一步解決一次代碼,多端運作的問題。Flutter for Web還處于早期試驗版,官方不建議在生産環境上使用。那麼到底它的實際情況怎麼樣呢? 我們做了一次預研。期望這次預研的結果可以幫你決定是用,還是不用FFW。
Flutter for Web原理

Flutter for Web和Flutter在上層都是Dart環境,兩者不同的是,Flutter的Dart代碼運作在Dart虛拟機中,界面由Flutter引擎處理,通過Skia繪圖引擎經由GPU繪制到螢幕上。而Flutter for Web的Dart代碼編譯成JavaScript,界面上部分轉換成标準的html标簽,部分轉換成通過Canvas繪制的自定義标簽,最終構成一個dom樹。
這個原理上的差異非常重要,這直接可以讓我們通過原理得出下面的結論:
Flutter for Web的一緻性和體驗上存在沖突
如果Flutter for Web追求(和Flutter)完美的一緻性,勢必需要大量使用Canvas去繪制,而Canvas去繪制元件的性能(尤其在移動端)至少不會比html标簽好。如果FFW追求性能極限而使用大量标準的html标簽,這就會帶來和Weex、RN等一樣的一緻性問題:對于Flutter所有的控件都是一套代碼在繪圖引擎上繪制,對Flutter for Web如果要使用大量html标簽,那如何保證一緻性呢?隻能靠大量精細的打磨工作了。是以FFW必須要處理好這個平衡。
為啥使用canvas繪制性能不優于手寫html呢,定性的從幾個角度分析:
- FFW在canvas上繪制的元件帶有很多MD特色的視覺和動畫,比如陰影、Z軸變化等,這部分對性能的消耗要大于普通html标簽
- FFW是通過Dart的DSL轉成的dom樹結構,轉化後的dom樹十分複雜,不太可能比手寫的dom樹更簡潔
使用canvas的控件,其手勢事件的捕獲分發都是靠FFW架構自己實作的,emmmm
雖然不排除Google大力出奇迹的情況,但是不管怎樣,相同素質的開發人員,相同的界面,性能上也不可能優于html+css+js
另外一點,如果FFW在原理上涉及大量HTML标簽的轉化,那就勢必會涉及到碎片化的進行中,浏覽器的碎片化程度可一點都不比Android系統的碎片化小。像Flutter本身之是以被那麼多人看重,就是因為其通過繪圖引擎這一層,完美的避開了碎片化,保證一緻性。
是以最好的平衡就是,隻有有限的一部分标準的html标簽可以被FFW複用,其必須有幾點性質:
- 标簽本身的功能簡單又直覺
-
最好不要有直接圖形化的展示,或者隻負責簡單的圖形化展示(比如畫方形)
那幾個比較典型的标簽就是
、<p>
這種了<div>
Flutter官方就是這麼做的,是以我的結論是:
一緻性上大體不會存在問題,性能上,FFW應該不會優于純手寫html标簽界面。
官方現狀&建議
根據
官網和
Github repo上的說法,我們整理了一下:
- Flutter for Web和Flutter目前暫時是兩個倉庫,官方正在進行合并,沒有給出結論。這一點在工程上非常重要,它說明了幾個問題:
- 目前官方對FFW的成熟度沒有信心,同時FFW的疊代速度也很快。
- 目前FFW和Flutter最多保證API一樣,實作原理差異可能非常大,同時不保證所有控件都已經在FFW上實作。
- 官方不建議應用在生産環境
- 目前插件能力十分有限,和系統互動的一些能力缺失,比如拍照等。
- 性能無法保證,運作會慢,可能會有掉幀
- FFW中針對桌面的UI部分沒有完成(跟我無線有什麼關系?)
- 開發中隻能在Chrome中調試(又有什麼關系?),release版是可以運作在任意浏覽器中(除了IE,另支援的最低版本存疑)。
實踐
對于這麼新的東西,官網上的内容的确不多,而且簡單來看這些問題好像也沒什麼,是以對于到底能不能用,我們還是需要抱着吃螃蟹的心态具體進去預研一下,為了盡快弄清,我計劃找一個我們app已經做好的flutter頁面,把它遷移成FFW,對整個遷移過程做個評估,再看下頁面效果,基本上就能得出結論了。
具體的遷移細節就不提了,官網也有
遷移文檔,大體上就是這麼幾個步驟
- 安裝Flutter for Web的工具webdev
- 改SDK依賴,新增Web檔案夾(和之前存在的android、ios檔案夾同級),新增一些其他檔案(index.html, main.dart等)。
- 将所有flutter代碼中依賴的flutter包,改成flutter_web包
- 去掉所有不相容的代碼,比如多語言、路由、
等等Platform.isAndroid
- 編譯運作
實踐的主要目的,有以下幾個:
- 對整體坑的深度和廣度有個認知,友善推算出填坑成本
- 對FFW整體的性能和體驗有個把握,尤其是我們自己的頁面跑在FFW上是什麼體驗。
- 對FFW和JS互相調用有具體的了解,如果可行,那複用集團已有能力(比如mtop)的坑就會小很多
删了一萬行代碼跑成功之後,最終在工程、開發體驗、使用者體驗上得到一些結論,以下的結論中,體驗部分是我在我的mix2s上的感受:
工程
- 支援debug和release模式,後者比前者性能高(差異很明顯)。
- debug模式支援代碼修改後自動重新編譯,和其他的前端架構(我隻用過Django)一緻
- 支援hotreload,暫時沒有嘗試
- 支援
指令編譯出index.html+js,可以通過nginx做反向代理。webdev build
- 非常重要 編譯出來的代碼,gzip壓縮前,最簡單的helloworld的
大小約為500k左右,sample中的gallery大小約為2M,閹割版的純展示用的訂單清單大小約為1.3M。gzip對文本的壓縮率一般是80%,壓縮後也要動辄幾百k的大小。而且main.dart.js
不加載完,界面是不會展示的。main.dart.js
開發體驗
- 非常重要flutter for web使用flutter_web庫,且不支援其他許多插件,這會帶來幾個問題
- 工程上無法優雅的解決flutter和flutter_web共存的情況,最多搞個dart2的conditional import(這個特性可不在官方文檔中哦)
- 依賴flutter sdk的幾個庫,尤其是多語言庫無法應用在flutter_web上,并且Google肯定不會再單獨為flutter_web适配,而是在合并時做支援。這就意味着現階段所有依賴flutter sdk的庫不能被flutter_web使用。
- 調試困難
- 錯誤日志可以列印到浏覽器的console中,但是try catch部分的堆棧不好拿
- 浏覽器中的堆棧很複雜,但是基本上能找到出錯的dart代碼
- 目前沒有發現單步調試的能力
-
全部報錯,針對Android和iOS做差異化展示目前還不知道有沒有其他方法可以做到。Platform.isAndroid
- 目前沒有發現控件的api不一緻的地方,不過有些控件的行為十分異常,比如下拉重新整理,在Android手機上經常卡死、失效。猜測重互動的一些控件都有可能存在類似的問題,但是測一遍的成本太高。
- 圖檔控件
可以直接用,但是現在來看有些糊,不确定是官方控件的問題,還是我們做的cdn url政策有問題。NetworkImage
- dart代碼可以調用js,開發體驗和反射類似,并且需要處理JS類型和Dart類型的轉換。Dart和JS的互動速度未知。隻能說哪怕FFW有很多浏覽器的API不支援,也可以通過JS來擴充能力。
- Dart和Js語言本身的差異會帶來測試和相容成本的增加,雖然Dart可以編譯成Js,但是跑在DartVM上的Dart的表現,和其編譯成Js運作在浏覽器中的表現并不完全相同。比如對于如下代碼
在DartVM中該代碼可以執行,結果是Map<String, String> query = null; val b = query["abc"];
;但是編譯成Js以後運作時因為query為null,會報空指針。b=null
使用者體驗
使用了chrome、uc、小米自帶浏覽器分别試了一下訂單清單和官方的sample-gallery界面,體驗如下:
- 加載速度差不多,因為是區域網路環境,感覺不到差異。
- 非常重要打出的唯一的
和部分資源檔案(比如main.dart.js
)沒有加載出來的時候,界面不會展示。MaterialIcons
- 幀率或者流暢度上,chrome > uc >> 小米自帶浏覽器,其中chrome算是最流暢的體驗,但是sample中的一些動畫和頁面轉場也可以看到明顯的卡頓。
- 文字展示上,看flutter for web的界面,chrome對大部分文字處理的很清晰,小米自帶浏覽器看文字明顯模糊。對比下面兩張圖,點開放大之後檢視,FFW頁面的文字模糊的很明顯。
Flutter for Web | Flutter |
---|---|
| |
- 所有浏覽器都不能選中文字複制粘貼。flutter for web應該是以canvas處理展示文字的,這樣才能解釋為啥有的字型size會模糊,并且不能選中文字。
- iOS的safari和chrome通路demo頁,TextField整個不可用,包括以下問題:
- 軟鍵盤彈出邏輯詭異,大部分時候彈出自動縮回,小部分時候正常彈出(Android這部分表現正常);但焦點轉移的時候不會自動收回
- safari不能輸入文字,chrome可以輸入(Android這部分表現正常)
- 可以選中文字,複制和粘貼不生效(Android問題相同)
- 焦點到TextField的時候界面會自動放大,這個時候很難縮放回去(Android這部分表現正常)
總結
按照上面的,整體總結一下,Flutter for Web有幾個比較嚴重的問題,不解決的話估計是無法應用到生産環境上的:
- 包大小問題,這會帶來幾個問題:
- FFW的包遠大于正常h5的包,對流量和頁面加載速度都是很大的挑戰
- FFW打成一個JS包,多個FFW頁面無法對公共元件進行複用,進一步造成浪費。
- FFW的js包不加載完,頁面無法展示,使用者體驗極差;而H5可以漸進加載,js可以後入場。
- SDK分離的問題,這也會帶來幾個問題:
- 工程上,很難優雅的解決兩個SDK并存的問題
- 能力上,依賴Flutter SDK的官方庫,比如多語言庫,不支援FFW SDK。隻能自研一套多語言方案。
- 表單場景不要用FFW,上述說到的TextField的問題不知能否在應用層去解。
- 一些重互動的元件,比如下拉重新整理等,存在問題,幾乎無法使用。不确定整體元件的品質情況如何,挨個去看成本太大。
結論
- 沒有非常強的業務訴求或者技術推動,不要在目前嘗試在生産環境使用Flutter for Web。
- 如果有填坑的決心,并且舍得投入,并且對包大小不關心,對幀率等使用者體驗也不看重,可以考慮現階段進行嘗試。
- 我個人判斷填坑成本在100人日以上(上限未知),并且有些坑(包大小)可能根本填不了。
- 什麼時候可以再次跟進?我認為在FFW合并進Flutter SDK的時候,至于他們具體的規劃需要問下Google的人了。
填坑指北
本節的主要目的是列出假設要做FFW,我們需要做的技術項和對應方案。