相信每個見到 SwiftUI 的開發者,都會馬上將這門船新的 UI 框架和 Flutter 聯繫到一塊兒。是的,它們身上有太多太多類似的地方,類似的聲明語法、實時熱更新、跨平臺(SwiftUI 僅僅跨 Apple 平臺)等等,讓羨慕了前端技術爆發的移動開發圈子也熱鬧了一回。那麼 SwiftUI 和 Flutter 到底有什麼類似和不一樣?它們又各有什麼優缺點?以及最後,單就技術方向而言,誰纔是將來跨平臺方案的贏家呢?前端
現代計算機語言愈加趨於類似是一個不爭的事實,由於每一個語言的基本目標都至關的一致:簡潔、靈活、安全、高性能。同時,各類語言的優異特性也都在被相互借鑑,更近一步減小了各個語言之間的鴻溝。git
Swift 和 Dart 分別做爲這兩個 UI 框架的惟一官方語言,在語法層面的差異其實很是小,網上也有大量的文章來對比這兩種語言的優劣性。個人使用體驗和大部分人類似,就目前而言,Swift 和 Dart 各有優劣。程序員
Swift 比 Dart 更加簡潔。Swift 自己在語法層面已經比 Dart 要簡潔不少,好比無需在句末添加;
分號等。這一點在直接編寫 SwiftUI 和 Flutter 上會顯得尤其明顯。不過這個問題並不全是語言層面帶來的問題,也跟這兩個 UI 框架的設計有關。github
ForEach(userData.landmarks) { landmark in
NavigationButton( destination: LandmarkDetail(landmark: landmark)) {
LandmarkRow(landmark: landmark)
}
}
複製代碼
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
final landmark = landmarks[index];
return LandmarkCell(
landmark: landmark,
onTap: () {
Navigator.push(
context,
CupertinoPageRoute(
builder: (context) => LandmarkDetail(
landmark: landmark,
),
),
);
},
);
},
childCount: landmarks.length,
),
),
複製代碼
二者都實現了一個能夠點擊的列表頁,都調用了一個自定義 Cell 。swift
Swift 比 Dart 更加嚴格,從某種角度上講會更加安全,固然安全這種事情追根揭底仍是得靠人。後端
Swift 在發佈的第5個年頭居然尚未 await/async ,雖然早有提案但一直沒有落實下來。不過 Dart 這邊也沒有可選型。只能說二者互相吸取的還不夠。安全
Dart 的黑科技:同時支持 AOT 和 JIT,這也賦予了 Flutter Web 級的熱重載特性,是 Flutter 對決 SwiftUI 的有力武器。框架
Swift 和 Dart 都是開源語言。Apple 對 Swift 的開放是前所未有的,目前 Swift 已經能夠脫離 macOS 運行,同時在社區中也出現了諸如後端框架 Vapor
等一系列有意思的東西,更是成爲了 Tensorflow
的官方語言之一。這一切大大拓展了 Swift 的使用場景。不過,Dart 在可玩性上明顯走的要比 Swift 更快。 用其開發的後端框架已經被一些公司投入到了生產環境,Google 的 Web 框架 AngularDart
(非 Flutter Web)也早已在一些服務上穩定運行了許久。更不用說藉助 Flutter 跑遍 iOS Android macOS Windows Web,簡直能夠用所向睥睨來形容。less
一個語言的設計好壞的確會影響一個框架的受歡迎程度,我已經聽過無數 Flutter 開發人員吐槽 Dark 無止境的嵌套問題。雖然 Dark 的可玩度更高,但就單 UI 框架而言, 我我的認爲 Swift 的使用感覺要明顯好於 Dart。async
SwiftUI 和 Flutter 都無一例外的使用了聲明式語法和各自的 DSL 來描述 UI ,它的好處是,你能一眼就從代碼的結構中看出實際 UI 的結構,而且避免反覆的邏輯代碼。SwiftUI 用數據綁定和狀態管理替代了原有的複雜控制邏輯,而 DSL 又使得代碼在結構上和 UI 的層級結構高度一致。
VStack {
MapView()
.edgesIgnoringSafeArea(.top)
.frame(height: 300)
CircleImage()
.offset(y: -130)
.padding(.bottom, -130)
VStack(alignment: .leading) {
Text("Turtle Rock")
.font(.title)
HStack(alignment: .top) {
Text("Joshua Tree National Park")
.font(.subheadline)
Spacer()
Text("California")
.font(.subheadline)
}
}
.padding()
Spacer()
}
複製代碼
上面是 WWDC 上出現的一個 SwiftUI Demo LandMark。即使你沒有學習過 SwiftUI 的語法,稍稍閱讀一下上面的代碼,你也能發現,它和圖片中的 UI 是一一對應的。這就是聲明式語法的優勢。不過在具體寫代碼的時候,Flutter 和 Swift 的感覺就差太多了,最明顯的就是:Flutter 要比 SwiftUI 複雜太多了。上面的效果用 Flutter 實現要寫多少代碼呢?多到我都無法粘貼過來,不然個人文章有一半都會是 Dart 代碼。不過這裏有一我的仿照着 Demo 寫了一份 Flutter 版的 LandMarks ,你們能夠參考一下。
爲何一樣是聲明式語法,Fluter 要比 SwiftUI 複雜這麼多呢。這其實體現了 Apple 和 Google 在設計這兩個 UI 框架時的不一樣思路,甚至表明了這兩家公司的不一樣文化。對 iOS 和 Android 稍有了解的開發者都知道,iOS 開發要比 Android 開發輕鬆不少。單就配置開發環境來講,iOS 就能夠比 Android 提前1天開始寫代碼。更不容說複雜的框架、混亂的平臺兼容性、生澀難懂的文檔。
Apple 對待開發者,就像是在對待新手同樣,極盡所能的去簡化開發過程,減小開發中的工做量,甚至還要讓開發變成一件優雅的事情,這從 Xcode 的外觀功能設計到系統 API 再到 Swift 語言都是如此。而 Google 在對待開發者時,更像是對待極客同樣,將大量的工做和創做交給開發人員,讓它們自由的創造有意思的東西。但相應的,它的門檻也很高,並且自由帶來的碎片化和不可控性也會致使開發效率的下降。
在設計 SwiftUI 的 API 時,Apple 隱藏了大量的內部細節,讓開發者調用盡量少的代碼就能實現相應的效果。開發者不須要重寫初始化方法,不須要使用 return
,不須要要關心 context
上下文,也不須要關心它應該是一個 StatefulWidget
仍是 StatelessWidget
, 更不須要關心用的是 Material Design 仍是 iOS Style。能作的 Apple 都幫你在內部作好了,你須要作的,只是告訴編譯器,你須要什麼控件,它們是什麼樣的,它們應該在什麼地方,僅此而已。若是說 Flutter 給移動開發帶來的聲明式語法讓全部人眼前一亮的話,那麼 SwiftUI 的出現則是告訴全部人(包括 Flutter ),什麼纔是真正的聲明式語法。
SwiftUI 最爲重磅的功能,就是萬衆期待的熱加載,也就是實時更新。Web 端超高的開發效率一直是移動端求之不得的技能,RN 的出現讓全部移動開發者第一次看到了這種可能,雖然它現在也有着本身的問題。Flutter 做爲 Web 技術的一種延伸,繼承了大量的 Web 技術特性,熱加載彷佛也就成爲了手到擒來的技能。而 SwiftUI 要想實現近似 Web 那樣的實時刷新,所要面對的困難要複雜的多。從個人實際體驗下來,也充分說明了這一點。
SwiftUI 的實時預覽分爲靜態預覽和動態預覽。默認狀況下展現的是靜態預覽,它的速度快,並且支持代碼和可視化兩種編寫方式。不過它沒有任何響應事件,沒法滾動和跳轉頁面。若是須要動態調試,則須要切換到動態預覽。動態預覽須要通過一段編譯時間,而後能夠以徹底動態的形式實時響應 UI 的變化,並且能夠在真機上實時調試。但動態預覽的限制還比較多,沒法作到 Flutter 那種只需編譯一次,以後整個 App 都能實現動態更新。
爲何會有靜態預覽和動態預覽呢?我相信若是不是出於一些問題的考慮(性能問題、難以處理的 Bug),Apple 是斷然不會將這個問題複雜化的。只能說,目前殘疾版的熱加載是 Apple 能給咱們的最好效果。正所以,目前 SwiftUI 所謂的實時刷新,距離 Web 以及 Flutter,仍是小兒科。靜態 UI 調試其實徹底能夠經過 StoryBoard 來實現,而真正實用的動態預覽又有必定的侷限性。不過 SwiftUI 尚處在測試階段,暫時還沒法對其性能做出最終的定論。
Google 宣稱 Flutter 的目標之一就是流暢(60Hz),目前來看,它確實在大部分場景作到了。不過根據個人測試(此時尚處於 beta 版),像頁面轉場、控件動畫這樣的場景,跑到 60 Hz 仍是有必定壓力。我一直都有在關注 Flutter 項目,咱們內部也在嘗試將部分模塊使用 Flutter 重寫。但我至今體驗下來的狀況,目前的 Flutter, 只能說很接近原平生臺的流暢度。不過即使 Flutter 可以達到系統原生的流暢度,它也始終沒法迴避性能損耗的問題。iOS 的原生 UI 框架(包括 SwiftUI)都是搭建在 Metal 之上的,Metal 對於 GPU 的使用效率遠要高於 OpenGL 。其帶來的結果在個人測試中也代表了,一樣的頁面渲染和動畫,Flutter 對於 CPU 和 GPU 的利用率要高於 iOS 原生 UI 框架。而高性能消耗,對設備就意味着高發熱和低續航。這在移動設備上尤其嚴重。
反觀 SwiftIU,Apple 彷佛照搬了系統全部的原生控件,原則上不會有性能上的改觀,但千萬不要被你的眼睛騙了,Apple 在你看不見的地方,下了一盤大棋。
上面兩張圖分別是 SwiftUI 和 iOS原生渲染的 Label,它們都運行在 iOS 平臺,都長得一摸同樣,但它們背後是兩個徹底不同的東西。咱們發現,SwiftUI 中的文字已經不是 iOS 系統的原生控件了,而是一個新的類型,名爲 Text
。它實際上是一個繼承自 UIView 的新控件,全名是 DisplayList.ViewUpdater.Platform.CGDrawingView 。觀察右側的屬性窗口,發現兩個控件有着徹底不一樣的屬性。UILabel
有着複雜的屬性,用於控制樣式、交互等。而 Text
則要精簡的多的多。
有傳言說,SwiftUI 的部分控件已經拋棄了系統原生 UI 框架,轉而直接使用了更加底層,同時也在 Apple 生態系統中更加通用的 Core Animation、Core Graphics、Core Text 進行渲染。是否直接使用了底層框架進行高效渲染我暫時還無得而知,但目前能夠確定的是,Apple 正在將 SwiftUI 剝離出原有的 UI 框架,丟掉沉重的歷史包袱。這勢必會帶來諸多益處,好比更高的自由度、更好的性能,以及真正意義上的:跨平臺。
可能不少人都會說,SwiftUI 和 Flutter 根本無法一塊兒比較,SwiftUI 不是真正的跨平臺。沒錯,SwiftUI 目前所跨的僅僅是 Apple 自家的生態圈,包含了 iOS(iPadOS)macOS watchOS tvOS,而隔壁 Flutter 早已玩膩了移動端,開始向 Windows macOS 以及 Web進發。不過,你不能否認,SwiftUI 確實作到了在徹底不一樣的系統平臺上實現了 Write once,use anywhere。並且,隱藏在這背後的思路還徹底不同。
SwiftUI 從根本上不是在構建 UI,而是在描述 UI 。這二者有什麼區別?構建 UI 時,你會明確每個控件的類型,甚至精確到平臺。好比在原生 UI 框架中,你須要一個輸入框 ,你就要根據不一樣的平臺,選擇具體是使用 UITextFiled(iOS) 仍是 NSTextFiled(macOS),而這兩個輸入框 有着不一樣的屬性、外觀和特性。在 Flutter 中,你不須要考慮不一樣平臺的控件類型,但你須要考慮控件的風格,官方提供了符合 Material Design 的 Android 控件和 iOS Style 的 iOS 控件,它們的許多特性也不一致,這一切給 UI 構建帶來了更加複雜的邏輯和工做量。
而 SwiftUI 更像是 Web 的模版,它只描述 UI ,而控件具體長什麼樣子,有什麼特性,會根據編譯的平臺因地制宜的實現,並且是自動的。這就像是給 Web 套上了一個模版,一個主題。
左側的代碼描述了一個表單,其中包含了一些簡單的控件,包括Picker
、Toggle
、Stepper
、Button
。若是咱們把代碼原封不動的放到 macOS 項目中,它會變成下面這個樣子。
你會發現,一樣的代碼,展現出來的 UI 卻徹底不同,但 UI 表達的內容確實徹底一致的。更使人細思極恐的是,Picker
這個"單項列表選擇" 控件,在iOS上被翻譯成了一個選擇頁面,點擊 cell 會 push 出全部可選擇的選項,點擊選項會返回上一頁面並將選中的內容展現到對應的 cell 上。而在 macOS 上,Picker
則會被翻譯成咱們桌面操做系統上常見的下拉列表。這種轉變是難以想象的,我只能用 Wow、Awesome、Amazing 來形容它。由於它徹底符合平臺設計語言和用戶使用習慣的,同時又極大的下降了開發和適配難度。然而 SwiftUI 能作的還遠遠不止這些。
沒錯,不只僅是 iOS 和 macOS,在 tvOS 和 watchOS 上,SwiftUI 也將以最符合平臺設計語言和交互的方式去展現每個控件。並且,你還將自動得到大量的系統特性,好比 Dynamic Type、Dark Model、閱讀方向自適應等。
從這一點上來看,SwiftUI 和 React Native 有着更爲類似的思路和技術。只不過 Facebook 不管對 iOS 仍是 Android 都幾乎沒有任何話語權,所以在兼容性、一致性上都有必定問題。而爲了達到這種兼容性,不只 React Native 自身須要作大量的工做,開發人員也須要疲於應對各類問題,能夠說是一種比較尷尬的中間技術。
而 SwiftUI 和 React Native 與 Flutter 的跨 UI 平臺思想有着本質的不一樣。Flutter 徹底拋棄了運行平臺的原生 UI 框架,相似於在一張畫布上一個像素一個像素"畫"出每個控件,相似於一個 2D 遊戲引擎。它的目的也很明確:確保相同的代碼在不一樣的操做系統、硬件設備、屏幕尺寸下展現徹底相同的 UI 。這彷佛是開發者想要的,由於咱們受夠了 RN 在不一樣平臺上表現出的不可控差別。但即使拋去了不一樣操做系統設計語言的差別,不一樣尺寸屏幕下的 UI 至少應該是很不同的。桌面平臺 UI 和移動平臺 UI,甚至手錶上的 UI,應該根據用戶的使用習慣和操控方式,制定徹底不一樣的 UI 和交互。若是要在 Flutter 上實現多平臺的適配,結果對任何一個程序員而言均可能是一場噩夢。你須要作大量的判斷,而且可能須要使用多種控件來實現單一功能,最終還要面對大量的測試和 Bug。
不過有利就有弊。SwiftUI 近乎完美的跨平臺方案是創建在較低自由度下的。經過上面的例子你也發現了,Demo 中使用的都是系統原生的控件樣式。這並非說咱們沒法自定義,只不過:
好比咱們想要修改列表背景的顏色,那麼咱們將會失去部分系統自動爲咱們添加的 Dark Model 特性,你須要作一些額外的工做來告訴 App 在黑/白模式下背景應該展現什麼顏色。
但好在 Apple 已經開始嘗試脫離系統原有的 UI 框架,這反而讓咱們更加方便自定義部分 UI 。以往咱們要實現下面這個 Button 的樣式,須要對 UIButton 的參數進行大量調整,由於默認的 UIButton 是文字在左,圖片在右。現在,SwiftUI 脫離了系統 UI 框架組建後,咱們能夠經過極其簡單的代碼實現複雜的 UI 樣式。
不過這和 Flutter 引覺得傲的 "精確到像素" 還有必定差距,咱們可能沒法像 Flutter 那樣徹底掌控 UI 控件的全部細節。但就像畫畫同樣,你失去了必定的自由,換來的是更快更高效的開發,這偏偏體現了 Apple 的企業風格。
然而 SwiftUI 跨平臺的故事講完了嗎?我認爲這偏偏只是個開始。SwiftUI 這個徹底與平臺、設備無關的,純描述的 UI 框架,偏偏才是跨平臺方案的正確方向。SwiftUI 可以用來描述 iOS macOS tvOS 甚至 watchOS 的 UI,爲何不能用來描述 Android,或者 Web?SwiftUI 已經擁有了 Flex 佈局、combine 數據綁定、熱加載等一系列特性,咱們只須要照着這個思路,將 Android 或者 Web 的模版套用在 SwiftUI 上,就能一樣獲得符合平臺設計語言和規範的 UI 。固然,這遠沒有我說的那麼簡單,可是不要忘了,Swift 是開源的,跨平臺運行也不是問題。只要條件存在,一切皆有實現的可能。
然而到那時,Flutter 又會變成什麼樣子呢?至少在目前,Flutter 仍是會成爲大部分公司跨平臺的首要方案之一,而 SwiftUI 嘛,大面積使用至少也是2~3年以後的事咯。