當 Flutter 碰見 Web,會有怎樣的祕密?前端
在線教育團隊(簡稱:OED)已經將 Flutter 這樣技術在業務中落地了,作爲 IMWeb 前端團隊的咱們也要進行一些嘗試。本文從前端角度進行 Flutter 開發的概況描述。主要是爲了讓您瞭解和感覺一下:Flutter to Web 的實例、Flutter 爲何會出現、Flutter 設計實現原理、Flutter 技術特色和優點。react
前言
OED的客戶端團隊在 2019 年上半年 ,就已經把 Flutter 落地到企鵝輔導的業務中了。今年咱們又一塊兒去上海蔘加了 2019 年穀歌開發者大會,碰見了更多的 Flutter 開發者,此次體驗比第一次去的時候感受熟悉了不少。但願將來有機會把他們邀請來深圳,進行一些 Flutter 的技術分享。這次開發者大會又恰逢 Flutter to Web 也已經正式合入 Master,那麼,前端同窗是否能夠趁着這股東風一塊兒參與到 Flutter 的協同開發中呢,我想這問題會困擾着不少人?若是您有好的想法,能夠在留言區參與評論。linux
本文不是一篇 Flutter 詳細的學習教程,更像是一個概覽,用盡量平實的語言和對比的思路去描述它。本着依舊從前端同窗的角度出發,去理解一項新的技術,但又不限於前端技術自己。但願您能經過這篇文章相對全面的理解 Flutter 這項技術自己。特別感謝領導的鼓勵和支持,讓我有機會去學習和理解 Flutter 框架,由於相對我而言,OED 的客戶端團隊的同窗經驗會遠超於我,他們已經完整經歷了業務從 0 到 1 的過程,這是一種很是有意思的體驗。git
一、Flutter to Web 案例
下面轉換的工程案例 是咱們的 企鵝輔導APP 裏面的業務代碼(詳細的操做流程)。Flutter 官方文檔具備很好的說明,若是您單純轉一個徹底不依賴 APP 的項目,您安裝完環境而且切換到 Master 版本就能夠直接進行了,甚至不須要任何代碼修改。轉換業務產品中的代碼,還須要處理一些奇奇怪怪的問題,相信這對您來講,應該都不是問題。能夠在這裏安裝和環境配置 進行環境安裝。Flutter 官網提供了一個 案例能夠嘗試一下。官方給了一個開箱即用的 開發文檔 。github
視頻web
上面的視頻裏面展現咱們 企鵝輔導 的第三個 TAB 內的一個上課頁的 Flutter 業務實踐,以及轉換後的 Web 頁面。能夠明顯看到,確實有一局部有些失真,可是用戶徹底可用。打包壓縮以後,Web 端代碼只有 1.4M,Mac 桌面端體驗過程當中,沒有出現卡頓問題。這裏 Web 頁面內的渲染是經過 Canvas 渲染 和 DOM 進行的頁面填充。Dart 本來從天生就支持在 Chrome 中使用的,只是在 2015 年不幸夭折,可是,它長期支持轉換爲 JavaScript。所以,能夠碰見的將來,隨着 Flutter 的發展,Dart to Js 業務實踐的進化速度,可能會超過 WASM 的業務使用。算法
下面是一個處理無限列表的場景,不管是在 Mac,仍是在移動端時,依舊會有卡頓的現象,FPS 表現並不理想。npm
首先官方目前還不建議,在產品化中使用 。但既然已經合入 Master,相信這一天也不會遠了。編程
轉換這裏須要解決一些問題,整理了一下官方建議和實踐的體驗:windows
業務轉換使用的時候,須要把系統依賴解決掉,部分樣式問題跟 Flutter 排版組件有關,而系統相關的如本地存儲、網絡請求須要咱們自定義轉化方案。例如:客戶端使用的是 WNS 協議,而前端須要使用的是 HTTPS。目前看 Flutter to Web 做爲業務容災的策略仍是能夠的,老是優於 APP Crash 或者 影響範圍很是大的 BUG 致使用戶沒法使用的狀況出現。這裏 Skia 有個明顯的短板,就是 3D 動畫,若是您業務對 3D 動畫依賴比較強,一段時間內,就不要選擇 Flutter 做爲業務的技術選型了。
上面簡單的羅列了一些前端在 Flutter 的工做範圍,前端定位更可能是打輔助的!固然若是您是全能型開發,也能夠所有都作。技術本質上沒有邊界,侷限的只有本身。從前端角度看 Flutter 的開發成本相比於 H5 確實仍是多了一些成本,但學起來也不會太難,只是時間的問題。對於團隊有人力,而且但願嘗試新技術的,徹底放在業務中嘗試使用。
至於將來是否能肯定是 Flutter 這個框架成爲行業標杆,還不得而知。但若是想完全統一全端技術棧,Flutter 今天的設計思路是一個很是有突破性的存在!應該說,類 Flutter 的自繪引擎方案在將來會有機會大放異彩。
站在前端的角度上,咱們嘗試着在組件化和工程化的方向找到本身在 Flutter 生態中的定位。而且相比於 IOS 和 Android,Flutter 的 Dart 代碼是徹底開源的,參與感是會讓開發者提高很多的,就相似 linux 和 windows 的感受。
至於團隊是否要參與進去,不少時候是看綜合的成本和收益,作與不作,作到什麼程度,適合何時進行業務跟進,其實,都是要以團隊的價值最大化爲目標,沒有絕對的對與錯,結合團隊的實際狀況量身定製就好。錯誤的時機進入,也會付出不小的成本,您本身考量。
以下圖,橫向對比行業開源方案:
簡單對比來看,結合團隊的技術實踐和能力。站在第三方的角度上。RN 和 Flutter 相對是 2 個比較好的跨平臺方案。並且其它方案或多或少都有一些侷限。
那真的放到業務上,又當怎麼選擇呢?
回答這個問題確實有些艱難,仁者見仁智者見智。不管是相對成熟的 React Native,仍是新貴 Flutter,放眼到整個大前端技術史裏面看,都不如 Hybird 影響的深遠。但現在它們也都達到了商用的標準。我同時使用過 Hybird 和 RN 做爲業務接入方,算是會有一點點經驗之談。
從目前行業的產品,以及社區生態來講,React Native 總體仍是勝出 Flutter 一籌。畢竟早出來幾年,市場佔有率和行業積累仍是在的。可是長遠來看,技術發展也有它的必然規律,Flutter 的技術理念已經領先了 React Native,做爲大公司、或者大前端團隊的技術儲備和技術選型,科技公司要想在將來在行業有一席之地,使用 Flutter 這樣的技術,必然也會是一個趨勢。至於開發者對於技術自己,應該也會對 Flutter 保有濃厚的興趣吧,畢竟技術永遠都會向前發展,不管是誰,不進步同樣會被淘汰。所以,做爲技術開發人員首先保持的就是一顆好奇的心,進而持續成長。
固然,將來可能成功的框架不必定就是 Flutter,可是它設計理念和設計思路是一脈相承的,類 Flutter 框架同樣也會出現。 就像 React 出來了,Vue 也會跟着出來了。有的團隊使用 Vue,有的團隊使用 React。可是,您會發現它們愈來愈像了。使用 Vue 的也不必說使用 React 的同窗水平很差,不存在的!理念很重要,有一句話說的頗有意思,叫 ~ 思路決定出路!
二、Flutter 技術架構
1)擁有了 RN,爲何又會出現 Flutter
在談及 Flutter 以前,咱們仍是要先簡單回顧一下,客戶端的上一次技術革新 —— ReactNative(此後簡稱 RN)。相信很是多的團隊都有去落地實踐 RN 的機會,不少 APP 的首屏渲染方案都是用 RN 技術棧進行的。咱們本身的產品 企鵝輔導 和 騰訊課堂 內的應用也是同樣。
這裏簡單回顧一下,在有客戶端開發的場景下,爲何又出現了 RN ?
RN 的價值簡單來說就是—— 可接受的頁面性能 + 高效開發 + 熱更新。
更新:傳統的 APP 上架以後,出現了業務 BUG,用戶只能去更新 APP,進行 BUG 修復。客戶端實現熱更新修復 BUG,有多難,能夠問問 IOS 的開發同窗。大機率猜想,手 Q 和微信,應該仍是有方案能夠熱更新的。可是對不少小廠商這確實是很是艱難的事情。所以,得益於強大的動態化能力 RN 的價值也就完美的體現出來了。
高效:一個 APP 發佈上線,Android 和 IOS 同時須要開發兩個應用,而 RN 只須要一套代碼,就能夠運行在雙平臺上,節省很大的人力成本。而且不少業務線有很強的業務運營訴求,可能會存在很短期內的屢次改版和發佈的狀況出現,客戶端開發的人力瓶頸和發佈週期的限制,已經很難知足這樣的業務場景了。尤爲在一些有損發佈的狀況下,趕着時間點,帶着 BUG 上線的場景,在後續進行增量的修復,再這樣的狀況下,傳統客戶端的表現,簡直就是災難性的。
性能:RN 具備優於 H5 的性能體驗。畢竟是經過客戶端進行的頁面渲染,速度上比 WebView 渲染仍是要快很多的。這個在 Weex、Hippy、Plato 上都有所體現,雖然低於 Native 的性能,可是在可接受範圍。
PS:這裏的表達,不是描述客戶端開發很差。只是單純從業務角度上看待問題,而把合適的技術放在合適的位置是很是重要的,這也是架構師核心價值之一。
回顧了以上三點,咱們發現 RN 的出現,有它的必然性。那麼回到主題,RN 已經這麼優秀了,爲何還要有 Flutter 的存在,有一次向 Ab 哥請教技術成長的時候,Ab 哥提到了頗有意思的一個觀點,就是您對一項技術瞭解的深刻程度,取決因而否能認清這項技術的侷限。 就像人同樣,他(她)有多少優勢,就會存在多少缺點。沒發現,不等於不存在,由於必定存在。所以,順着這個思路,咱們簡單的看一下 RN 的問題。
首先,看維護成本,雖然 RN 是一套代碼多端運行。但仍是須要 IOS 和 Android 開發幫助咱們去一個一個的繪製組件,尤爲遇到特殊訴求的時候,還要 case by case 的處理,而且隨着 IOS 和 Android 系統自己的迭代和升級,以及框架自身發展的歷史包袱,咱們可能還須要處理不少與原生系統之間的平臺差別,修復各類奇奇怪怪的 BUG,這對業務來講是很大的負擔。
其次,對性能訴求,不管是產品仍是開發同窗,對於用戶體驗的追求,永遠都不會中止。RN 存在諸多性能的短板,所以,纔會有 Weex 這樣的產品出現,去定製化的解決業務場景下的問題。JS 和 Native 的通訊,頁面的事件監聽,複雜動畫的渲染和交換成本,都是很大的性能挑戰。
最後,在存在更強的業務訴求的時候,人們就不得不去尋找更好的方式去實現。很是存感激的看待谷歌這家公司,都是定位於商業公司,但實際上對世界的影響力上面,公司與公司之間差距仍是很是大的。這個課題範圍太大,之後有機會能夠深度討論一下。
您看到了上面的描述,爲了解決上面這些問題 —— 自繪引擎時代出現了,以 Flutter 爲表明的技術方案會應運而生,相信必定不會只有 Flutter 一項跨平臺技術出現的,歷史老是驚人的類似。其實想到自繪引擎,我最早想到的是那些遊戲引擎。那如今又爲何給出 自繪引擎 這樣的一個概念呢?H5 是依賴於瀏覽器渲染,RN 依賴於客戶端渲染,而 Flutter 基於 Skia 本身繪製的圖形界面。所以,Flutter 才能真正實現跨端!相信在不久的將來,在傳統客戶端上也能看到 Flutter 的身影,這樣才能真正達到多端統一。
最後,咱們再簡單總結一下有哪些問題:
一、Web 性能差,跟原生 App 存在肉眼可見的差距;
二、React Native 跟 Web 相比,支持的能力很是有限,特定長場景問題,須要三端團隊一個一個處理;
三、Web 瀏覽器的安卓碎片化嚴重(感謝 X5,騰訊的同窗過得相對輕鬆一些)。
爲了解決上面的問題,Flutter 出現了:
一套代碼能夠運行在兩端;自繪 UI,脫離平臺,也能夠簡單的把它理解爲一個瀏覽器的子集。
鋪墊了這麼多,就是爲了幫助您回憶起技術發展的脈絡和技術趨勢,能夠更好的理解下面即將要表達的文稿,下面咱們正式開始介紹 Flutter。
2)Flutter 實現原理
Flutter 能介紹的技術點其實很是多,這裏找了一些具備表明性的技術項,結合本身的理解跟你們分享一下。包括設計思路、渲染方式、UI 的生命週期。由於這幾個點,跟 React 技術棧風格很是類似,以這種思考結構去對比介紹,能夠幫助你們更好的理解這項技術自己。
Flutter 總體架構設計
Google 了一下關鍵詞,搜素獲得了這張圖片,從下向上進行一些描述:
Embedder:是操做系統適配層,實現了渲染 Surface 設置,線程設置,以及平臺插件等平臺相關特性的適配。從這裏咱們能夠看到,Flutter 平臺相關特性並很少,這就使得從框架層面保持跨端一致性的成本相對較低。
Flutter Engine:這是一個純 C++實現的 SDK,其中囊括了 Skia 引擎、Dart 運行時、文字排版引擎等。不過說白了,它就是 Dart 的一個運行時,它能夠以 JIT、JITSnapshot 或者 AOT 的模式運行 Dart 代碼。在代碼調用 dart:ui 庫時,提供 dart:ui 庫中 Native Binding 實現。 不過別忘了,這個運行時還控制着 VSync 信號的傳遞、GPU 數據的填充等,而且還負責把客戶端的事件傳遞到運行時中的代碼。具體的繪製方式,咱們放在後面描述。
Flutter Framework:這是一個純 Dart 實現的 SDK,相似於 React 在 JavaScript 中的做用。它實現了一套基礎庫, 用於處理動畫、繪圖和手勢。而且基於繪圖封裝了一套 UI 組件庫,而後根據 Material 和 Cupertino 兩種視覺風格區分開來。這個純 Dart 實現的 SDK 被封裝爲了一個叫做 dart:ui 的 Dart 庫。咱們在使用 Flutter 寫 App 的時候,直接導入這個庫便可使用組件等功能。
PS:雖然很早知道 Flutter,但實際寫 Flutter 時間也比較短暫。引擎源碼層面,目前也沒有深刻的涉獵。瞭解的方式能夠經過本身閱讀源碼,或者找谷歌、阿里、美團、以及我司的開發者幫忙。從技術角度來了解這些,在須要的階段,不會成爲你們的瓶頸。畢竟商業世界充滿了壁壘,而應用層面的技術自己是開放的。
3)Flutter 應用層語言 Dart
簡單描述一下 JIT 與 AOT:
- JIT
在運行時即時編譯,在開發週期中使用,能夠動態下發和執行代碼,開發測試效率高,但運行速度和執行性能則會由於運行時即時編譯受到影響。 - AOT
即提早編譯,能夠生成被直接執行的二進制代碼,運行速度快、執行性能表現好,但每次執行前都須要提早編譯,開發測試效率低。
Dart 是什麼
它的目標在於成爲下一代結構化 Web 開發語言。Dart 發佈於 2011 年 10 月 Google 的"GOTO 國際軟件開發大會"。是一種基於類編程語言(class-based programminglanguage),在全部瀏覽器都可以有高性能的運行效率。Chrome 瀏覽器內置了 DartVM,能夠直接高效的運行 dart 代碼(2015 年被移出)。支持 Dart 代碼轉成 Javascript,直接在 Javascript引擎上運行。dart2js
Dart 的特色
- 開發時 JIT,提高開發效率;發佈時 AOT,提高性能。
- 不會面對 JS 與 Native 之間交互的問題了。
- Dart 的內存策略,採用多生代算法(與 Node 有一些相似)。
- 線程模型依舊是單線程 Event Loop 模型,經過 isolate 進行隔離,能夠下降開發難度(與 Node 也很是相似)。
- Dart 的生態,這個跟 Node.js 差距十分明顯,npm 仍是行業中最活躍的。
- 而靜態語法與排版方式,純前端入門仍是有必定成本。
備註:
- 1)TS 能夠必定程度上幫助 JS 添加一些靜態檢測,但本質上依舊是沒法達成這樣的效果;
-
- 關於入門成本這個問題,若是您想深刻,我相信這都不會成爲問題。關鍵看是否能爲業務和團隊帶來價值。
Flutter 選擇 Dart 的緣由
- 健全的類型系統,同時支持靜態類型檢查和運行時類型檢查。
- 代碼體積優化(TreeShaking),編譯時只保留運行時須要調用的代碼(不容許反射這樣的隱式引用),因此龐大的 Widgets 庫不會形成發佈體積過大。
- 豐富的底層庫,Dart 自身提供了很是多的庫。多生代無鎖垃圾回收器,專門爲 UI 框架中常見的大量 Widgets 對象建立和銷燬優化。
- 跨平臺,iOS 和 Android 共用一套代碼。
- JIT & AOT 運行模式,支持開發時的快速迭代和正式發佈後最大程度發揮硬件性能。
- Native Binding。在 Android 上,v8 的 Native Binding 能夠很好地實現,可是 iOS 上的 JavaScriptCore 不能夠,因此若是使用 JavaScript,Flutter 基礎框架的代碼模式就很難統一了。而 Dart 的 Native Binding 能夠很好地經過 Dart Lib 實現。
4)Flutter 實現思路
看到了上面的介紹,這裏總結一下 Flutter 的實現思路。它開闢了新的設計理念,實現了真正的跨平臺的方案,自研 UI 框架,它的渲染引擎是 Skia 圖形庫來實現的,而開發語言選擇了同時支持 JIT 和 AOT 的 Dart。不只保證了開發效率,同時也提高了執行效率。因爲 Flutter 自繪 UI 的實現方式,所以也儘量的減小了不一樣平臺之間的差別。也保持和原生應用同樣的高性能。所以,Flutter 也是跨平臺開發方案中最靈活和完全的那個,它重寫了底層渲染邏輯和上層開發語言的一整套完整解決方案。
- 完全跨端:
- Flutter 構建了一整套包括底層渲染、頂層設計的全套開發套件。
- 這樣不只能夠保證視圖渲染在 Android 和 IOS 上面的高度一致,也能夠保證渲染和交互性能(媲美原生應用)。
- 與現有方案核心區別:
- 類 RN 方案,JS 開發,Native 渲染。數據通訊 bridge;
- Hybird 瀏覽器渲染 + 原生組件繪製;
- Flutter 設計自閉環,完成渲染和數據通訊。
三、Flutter 的 UI 渲染方案
渲染方案是 Flutter 目前獨特的設計形態,就是因爲渲染自閉環,才能真正跨平臺。談到 UI 渲染方案,做爲前端開發,咱們是繞不過如今如火如荼的三大框架的。爲何要談類 React 方案呢?由於 Flutter 的設計方案,與 React 設計具備同樣的思路。在渲染這裏咱們會談及控件、渲染原理、以及生命週期。
Flutter 是如何進行頁面渲染的呢?傳統 Web 是經過瀏覽器,而 Flutter 是自繪。所謂自繪就是用戶界面上 Flutter 本身繪製到界面,無需依賴 IOS 和 Android 原生能力,是經過一個叫作 Skia 引擎進行頁面繪圖。
1)介紹一下 Skia
Skia 是一個 2D 的繪圖引擎庫,其前身是一個向量繪圖軟件,Chrome 和 Android 均採用 Skia 做爲繪圖引擎。Skia 提供了很是友好的 API,而且在圖形轉換、文字渲染、位圖渲染方面都提供了友好、高效的表現。Skia 是跨平臺的,因此能夠被嵌入到 Flutter 的 iOS SDK 中,而不用去研究 iOS 閉源的 CoreGraphics / Core Animation。
Skia 是用 C++ 開發的、性能彪悍的 2D 圖像繪製引擎,其前身是一個向量繪圖軟件。Skia 在圖形轉換、文字渲染、位圖渲染方面都表現卓越,並提供了開發者友好的 API。Android 自帶了 Skia,因此 Flutter Android SDK 要比 iOS SDK 小不少。正是得益於 Skia 的存在:
- Flutter 底層的渲染能力獲得了統一,不在須要使用作雙端適配;
- 經過 OpenGL、GPU,不須要依賴原生的組件渲染框架。
- Flutter 能夠最大限度的抹平平臺差別,提高渲染效率和性能。
2)Flutter 的渲染流程
用戶能夠看到一張圖像展現,至少須要三類介質:CPU、GPU 和 顯示器。CPU 負責圖像的數據計算,GPU 負責圖像數據的渲染,而顯示器是最終圖片展現的載體。CPU 拿到須要上屏的數據作處理和加工,處理完成以後交給 GPU,GPU 在渲染以後將數據放入幀緩衝區,隨後隨着控制同步信號 (VSync)以週期性的頻率,從緩衝區內讀出數據,在顯示器上進行圖像呈現。並且操做系統就是一個無限循環的機制,不停的重複上面的操做,進行顯示器的更新.
Flutter 的渲染總體流程也是這樣的, Dart 進行視圖數據的合成,而後交給 Skia 引擎進行處理,處理以後再交給 GPU 進行數據合成,而後準備上屏。當一幀圖像繪製完畢後準備繪製下一幀時,顯示器會發出一個垂直同步信號(VSync),因此 60Hz 的屏幕就會一秒內發出 60 次這樣的信號。
3)Flutter 繪製流程
如上圖所示,Flutter 渲染流程分爲 7 個步驟:
首先是獲取到用戶的操做,而後你的應用會所以顯示一些動畫 ;
接着 Flutter 開始構建 Widget 對象。Widget 對象構建完成後進入渲染階段,這個階段主要包括三步:
- 佈局元素:決定頁面元素在屏幕上的位置和大小;
- 繪製階段:將頁面元素繪製成它們應有的樣式;
- 合成階段:按照繪製規則將以前兩個步驟的產物組合在一塊兒
最後的光柵化由 Engine 層來完成。
4)佈局
佈局時 Flutter 深度優先遍歷渲染對象樹。數據流的傳遞方式是從上到下傳遞約束,從下到上傳遞大小。也就是說,父節點會將本身的約束傳遞給子節點,子節點根據接收到的約束來計算本身的大小,而後將本身的尺寸返回給父節點。整個過程當中,位置信息由父節點來控制,子節點並不關心本身所在的位置,而父節點也不關心子節點具體長什麼樣子。
爲了防止因子節點發生變化而致使的整個控件樹重繪,Flutter 加入了一個機制——RelayoutBoundary,在一些特定的情形下 Relayout Boundary 會被自動建立,不須要開發者手動添加。
邊界:Flutter 使用邊界標記須要從新佈局和從新繪製的節點部分,這樣就能夠避免其餘節點被污染或者觸發重建。就是控件大小不會影響其餘控件時,就不必從新佈局整個控件樹。有了這個機制後,不管子樹發生什麼樣的變化,處理範圍都只在子樹上。
緩存:要提高性能表現,緩存也是少不了的。在 Flutter 中,幾乎全部的 Element 都會具備一個 key,這個 key 是惟一的。當子樹重建後,只會刷新 key 不一樣的部分,而節點數據的複用就是依靠 key 來從緩存中取得。
在肯定每一個空間的位置和大小以後,就進入繪製階段。繪製節點的時候也是深度遍歷繪製節點樹,而後把不一樣的 RenderObject 繪製到不一樣的圖層上。
5)繪製
在佈局完成以後,渲染對象樹中的每一個節點都有了明確的尺寸和位置。Flutter 會把全部的 Element 繪製到不一樣的圖層上。與佈局過程相似,繪製的過程也是深度優先遍歷,先繪製父節點,而後繪製子節點。如下圖爲例:節點 一、節點 二、節點 三、四、5,最好繪製節點 6。
如上圖能夠看到一種場景,就是好比視圖可能會合並,致使 節點 2 的子節點 5 與它的兄弟節點 6 處於同一個圖層,這樣會致使當 節點 2 須要重繪的時候,與其無關的節點 6 也會被重繪,帶來性能問題。
爲了解決上面的問題,Flutter 提出了佈局邊界的機制 ——重繪邊界(Repaint-Boundary)。在重繪邊界內,Flutter 會強制切換新的圖層,這樣能夠避免邊界內外的互相影響,避免無關內容雖然處於同一個層級致使的沒必要要的重繪。
重繪邊界的一個典型場景就是 ScrollView。ScorllView 滾動的時候會刷新視圖,從而觸發內容重繪,而當滾動內容重繪時,通常狀況下其它內容是不須要被重繪的。這個時候重繪邊界就很是有價值了。
這裏簡單理解,就是更精細化的對控件的更新,進行了小範圍的控制。在時間複雜度和空間複雜度中進行權衡。將來咱們優化業務,大機率也會優化這裏,找到自身業務的平衡點。
6)合成和渲染
最上面已經展現了 Flutter 的 7 層渲染流水線(Renderingpipline)的圖裏。這裏主要描述一下對合成和渲染的理解。渲染流水線是由垂直同步信號(Vsync)驅動的。這個概念很相似咱們平時說的 FPS 的概念,每秒 60 幀,太低的頻率會顯得頁面很卡。當每一次 Vsync 信號到來之後,Flutter 框架會按照圖裏的順序執行一系列動做:動畫(Animate)、構建(Build)、佈局(Layout)和繪製(Paint)
最終生成一個場景(Scene)以後送往底層,由 GPU 繪製到屏幕上。
Flutter App 只有在狀態發生變化的時候須要觸發渲染流水線。當你的 App 無任何狀態改變的時候,Flutter 是不須要從新渲染頁面的。因此,Vsync 信號須要 Flutter App 去調度。好比,咱們在 Widget 內使用了 setState 方法改變了控件的狀態。
整個渲染流水線是運行在 UI 線程裏的,以 Vsync 信號爲驅動,在框架渲染完成以後會輸出 Layer Tree。Layer Tree 被送入 Engine,Engine 會把 Layer Tree 調度到 GPU 線程,在 GPU 線程內合成(compsite)Layer Tree,而後由 Skia 2D 渲染引擎渲染後送入 GPU 顯示。這裏提到 Layer Tree 是由於咱們即將要分析的渲染流水線繪製階段最終輸出就是這樣的 LayerTree。因此繪製階段並非簡單的調用 Paint 函數這麼簡單了,而是不少地方都涉及到 Layer Tree 的管理。
Flutter 只關心向 GPU 提供視圖數據,GPU 的 VSync 信號同步到 UI 線程,UI 線程使用 Dart 來構建抽象的視圖結構,這份數據結構在 GPU 線程進行圖層合成,視圖數據提供給 Skia 引擎渲染爲 GPU 數據,這些數據經過 OpenGL 或者 Vulkan 提供給 GPU。
這裏描述一下合成的概念,所謂合成就是由於咱們繪製的頁面結構複雜,若是直接交付給繪圖引擎去進行圖層渲染,可能會出現大量的渲染內容重繪,所以,須要先進性一次圖層合成,就是說先把全部的圖層根據大小、層級等規則計算出最終的顯示效果,將相同的圖層合併,簡化渲染樹,提高渲染效率。
Flutter 會將合成以後的數據,交給 Skia 進行頁面二維圖層的渲染。
四、Widget 控件的更新策略
在這一個部分咱們對比着 React 的設計方式對比着看一下 Flutter 的實現,在 React 中您能夠看到三種很重要的名稱。JSX、Virtual Dom、真實 Dom,而在 Flutter 中咱們依然能夠看到對應的三類抽象的數據結構分別是 Widget、Element 和 RenderObject,他們的功能與 React 內三個數據抽象有殊途同歸之處。Flutter 繪製界面的基礎是 Widget,也就是描述頁面的最小模塊。
Flutter 的核心設計思想就是 "一切皆 Widget":
前端同窗能夠把 Widget 理解爲 Web Component 的 組件 便可。 一種結構化數據的抽象,包含了組件的佈局、渲染屬性、事件響應信息等。
1)Widget 相似 React VM 的 F(x) = Y 中的 x 存在
Flutter 中的 Widget 是徹底不可變的!只要當視圖發生變化,Flutter 就會從新建立一個新的 Widget 進行更新。便是 React 也是有必定的數據 Diff 的策略,而這裏變動即建立的方式,會帶來大量的銷燬和重建的過程,是否很是消耗性能?
Widget 對標的是 標識 React 的虛擬 DOM 節點的 數據描述 JSX,不是真實渲染的頁面 DOM。只是數據的抽象,不涉及視圖渲染。而且 Widget 具備不可變性,也提高了 Widget 自己的複用性。所以並沒大量的性能消耗,而 Dart 的做爲靜態語言的運行速度,也會有着超越 JS 的性能。
2)Element 是 Widget 的一個實例化對象
Element 承載了視圖構建的上下文數據,是鏈接結構化的配置信息到完成最終渲染的橋樑; Element 是一個可變的數據結構, 能夠大體理解爲 Virtual DOM。能夠進行 diff 更新; 能夠將真正須要修改的數據同步到 RenderObject 中。最大程度的下降渲染視圖的修改,提高渲染效率。
3)RenderObject 負責視圖渲染的對象
Flutter 的渲染分爲 4 個部分。佈局、繪製、合成、渲染,其中 佈局和繪製是在 RenderObject 中完成的。 Flutter 採用深度優的方式渲染對象樹,肯定樹中的各個對象的位置和尺寸,並把它繪製到不一樣圖層, 繪製完成以後交給
Skia 在 VSync 信號同步時從渲染樹合成位圖,而後交給 CPU 進而完成上屏。
4)Widget 一樣分爲有狀態 和 無狀態組件
無狀態控件 StatelessWidget 相似 React 的 PFC。 有狀態控件 StatefulWidget 就是 React 的 組件。 如同 react 組件同樣,使用有狀態組件是有成本的。正確的評估你的需求,避免使用無心義的有狀態組件。
這裏比較大的區別,是 Flutter 直接把 Widget 設計成爲了一個不可變的! 這也致使了技術方案的實現上存在了差別。
5)既然看到了 Widget,那必定會有生命週期的存在
因爲篇幅限制,這裏就不在詳細介紹了,簡單描述一下,生命週期,讓您有個影響。
建立:構造函數 --> initState --> didChangeDependencies --> build
更新: 主要由三個方法觸發:setState、didChangeDependencies 和 didUpdateWidget。
銷燬: 系統會調用 diactivate 和 dispose 這兩個方法,來移除或銷燬組件。
這裏多了 APP 生命週期的概念,在傳統 Web 咱們相對關注較少
從後臺切入前臺:paused -> inactive -> resumed;
從前臺退到後臺:resumed-> inactive-> paused;
五、學習路線
粗略了整理了一下我最近這 2 周體驗開發過程當中認知的學習範圍,這上面除了跟 APP 相關的部分,大部分場景已經經過代碼體驗和實踐過了。這裏因爲篇幅限制,就不在一一的介紹了。最重要的是關注 Flutter 的官方文檔。
其次,找了三個實例,跟您分享,您能夠 clone 下來,更細節的體驗一下:
佈局案例:https://github.com/yang7229693/flutter-study
代碼實例: https://github.com/nisrulz/flutter-examples
FlutterDemo: https://github.com/OpenFlutter/Flutter-Notebook
除了上面列出的這些,還有不少要作,好比 運維、調試、自動化測試、兼容性、客戶端 SDK 封裝、國際化等等。固然對於開發者來講,工程化、調試、組件化都是很是重要的實踐內容。
六、引文
做者:Alvin老師 連接:https://www.jianshu.com/p/287ff533fe7f 來源:簡書 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。