大前端公共知識雜談

大前端公共知識雜談首發於InfoQ-架構師特刊:大前端,是筆者對於泛前端知識圖譜(Web/iOS/Android/RN) 的文字版介紹,夾雜了 GUI 應用程序架構的十年變遷:MVC、MVP、MVVM、Unidirectional、Clean 的部份內容;更多參考資料能夠查看筆者的 Web 學習與實踐資料索引以及 React 學習與實踐資料索引前端

近年來,隨着移動化聯網浪潮的洶涌而來與瀏覽器性能的提高,iOS、Android、Web 等前端開發技術各領風騷,大前端的概念也日漸成爲某種共識。其中特別是 Web 開發的領域,以單頁應用爲表明的富客戶端應用迅速流行,各類框架理念爭妍鬥豔,百花競放。而 Web 技術的蓬勃發展也催生了一系列跨端混合開發技術,但願可以結合 Web 的開發便捷性與原生應用的高性能性;其中以 Cordova、PWA 爲表明的方向致力於爲 Web 應用盡量添加原生體驗,而以 NativeScript、ReactNative、Weex 爲表明的利用 Web 技術或者理念開發原生應用。平心而論,不管哪種開發領域或者技術,他們本質上都是進行圖形用戶界面(GUI)應用程序的開發,面對的問題、思考的方式、架構的設計很大程度上仍然能夠回溯到當年以 MFC、Swing、WPF 爲主導的桌面應用程序開發時代,其術不一樣而道類似。react

任何的前端開發學習中,咱們都須要掌握基本的編程語言語法與接口;譬如在 Android 開發中使用的 Java 或者 Kotlin,在 iOS 開發中使用的 Objective-C 或者 Swift,在 Web 開發中使用的 JavaScript、HTML 與 CSS 等。編程語言的學習中咱們每每關注於語法基礎、數據結構、功能調用、泛型編程、元編程等內容,譬如如何聲明表達式、如何理解做用域與閉包、如何進行基本的流程控制與異常處理、如何實踐面向對象編程、如何進行網絡請求通訊等等。接下來咱們就須要瞭解如何構建基礎的界面,譬如利用 HTML 與 CSS 繪製簡單 Web 頁面、利用代碼建立並使用簡單的 Activity、利用 StoryBoard 快速構建界面原型等等。而後咱們須要去學習使用常見的系統功能,譬如如何進行網絡交互,如何訪問遠端的 RESTful 接口以獲取須要的數據、如何讀取本地文件或者利用 SharedPreference、localStorage、CoreData 來存取數據、如何進行組件間或者應用間信息交互等內容。到這裏咱們已經可以進行基礎的界面開發,而且爲其增添必要的特性,不過在真實的項目中咱們每每還會用到不少的組件或者插件,iOS 或者 Android 中爲咱們提供了豐富的 SDK,譬如 UITableView 或者 RecycleView 能夠幫助咱們快速構建高性能列表組件,Android 5.0 以後默認的 Material Design 也是很是優秀的界面樣式設計指南;而 Web 開發中咱們每每須要引入第三方模式庫,譬如著名的 BootStrap、React Material UI、Vue element 都爲咱們提供了不少預置的樣式組件,react-virtualized 也爲咱們提供了高性能的相似於 ListView 這樣的部分項渲染機制。而後咱們須要將應用真實地發佈給用戶使用,咱們須要考慮不少工程實踐的問題,譬如如何進行測試與調試、如何進行性能優化而且在生產環境下完成應用狀態跟蹤、熱更新等操做、如何統一開發團隊的代碼風格與約定等等;這裏 Web 由於其特性而自帶了熱更新的功能,而在 Android 或者 iOS 咱們則能夠利用插件化技術或者 JSPatch 來實現熱更新。Java 與 Swift 都是強類型語言,其可以在編譯階段幫開發者排查問題減小潛在風險;而咱們也可使用 TypeScript 或者 Flow 爲 JavaScript 添加靜態類型檢測的特性,在 VSCode 等現代編輯器中一樣能夠達到相似於 Android Studio、XCode 中的即時檢查與提示的功能。最後,隨着應用功能的增長、代碼庫的擴展,咱們須要考慮總體的應用架構與工程化的問題;在應用架構中咱們每每須要考慮模塊化、組件化以及狀態管理等多個方面,選擇合適的 MVC、MVP、MVVM、Flux、VIPER 等不一樣的架構模式來引導應用中的代碼組織與職責分割;咱們也須要考慮選擇合適的構建與部署工具來簡化或者自動化應用發佈流程,在 Android 開發中咱們會選擇 Gradle 及其自帶的多模塊特性來管理依賴與分割代碼,而在 Web 中咱們可使用 Webpack、Rollup 等工具來自動處理依賴而且進行構建,iOS 中咱們也能夠選擇 CoocaPods。程序員

到這裏咱們會發現雖然具體的代碼實現、使用的技術不一樣,可是 Android、iOS 以及 Web 乃至於 React Native 等開發中,咱們須要解決的問題、可以用到的架構設計模式都是能夠相互借鑑的。在咱們從某個領域遷移到其餘領域時,咱們能很方便地知道應該學習些什麼,不一樣的技術、工具他們的職責是什麼,應該選擇怎樣的架構或者設計模式。古語云,欲窮千里目,更上一層樓,咱們想要真正掌握某種客戶端開發技術,最好是要了解咱們應該掌握哪些方面,本文便是對筆者日前總結出的泛前端知識圖譜(Web/iOS/Android/RN) 的簡要闡釋。正則表達式

編程語言

編程語言的學習是咱們進入軟件世界的基礎階梯,著名的 Code Complete 一書中提到:Program into Your Language, Not in it. 咱們不該該將本身的編程思惟侷限於掌握的語言提供的那些特性或者概念,而是可以理解這些語法特性背後能提供的抽象功能與原理,從而可以根據本身想要達到的目標選擇最合適的編程語言。而從另外一個角度來看,不管哪一門編程語言的學習也是具備極大的共性,從嚴謹而又被詬病過分冗餘的 Java 到須要用遊標卡尺的 Python,從掙扎着一路向前的 JavaScript 到含着金湯勺出生的 Swift、Rust,咱們都可以發現其中的相通與互相借鑑之處。算法

語法基礎

任何一門編程語言的學習都須要從基本的表達式(Expression)語法開始學習,咱們須要瞭解如何去聲明與使用變量、如何爲這些變量賦值、如何使用運算符進行簡單的變量操做等等。在不少語言之中都有所謂的傳值仍是傳引用的思量,譬如 Java 與 JavaScript 本質上就是 Pass-by-Value 的語言,只不過會將複雜對象的引用值傳遞給目標變量。這個特性又引起了所謂淺複製與深複製、如何進行復合類型深拷貝等等須要注意的技術點。除此以外,做用域與閉包也是不少語言學習中重點討論的內容,在 JavaScript 與 Python 的學習中咱們就會常常討論如何利用閉包來保存外部變量,或者在循環中避免閉包帶來的意外變量值。表達式是一門編程語言語法基礎的重要組成部分,接下來咱們就須要去學習流程控制與異常處理、函數定義與調用、類與對象、輸入輸出流、模塊等內容。流程控制的典型表明就是分支選擇與循環,譬如不一樣的語言都爲咱們提供了基礎 for 循環或者更方便地 for-in 循環,而在 JavaScript 中咱們還可使用 forEach 與 for-of 循環,Java 8 以後咱們也能夠基於 Stream API 中的 forEach 編寫聲明式地循環執行體,而 Python 中的列表推導也能夠看作便捷的循環實現方式。異常處理也是各個編程語言的重要組成部分,合理的異常處理有助於加強應用的魯棒性;不過不少時候會出現濫用異常的狀況,咱們只是一層一層地拋出而並未真正地去處理或者利用這些異常。Java 中將異常分爲了受控異常與不受控異常這兩類,雖然 JavaScript 等語言中並未在數據類型中有所區分,可是卻能夠引入這種分類方式來進行不一樣的異常處理;有時候 Let it Crash 也是不錯的設計模式。數據庫

Eric Elliott 曾在博文中說起,軟件開發實際就是 Function Composition 與 DataStructure Design;函數或者方法是軟件系統的重要基石與組成。咱們須要瞭解如何去定義函數,包括匿名函數以及 Lambda 表達式等;儘管 Java 中的 Lambda 表達式是對於 FunctionalInterface 的實現,可是鑑於其表現形式咱們也能夠將其劃歸到函數這個知識類別中。接下來咱們須要瞭解如何定義與傳入函數參數,在 C 這樣的語言中咱們會去關心指針傳遞的不一樣姿式;而在 JavaScript 中咱們經常會關心如何設置默認參數,不管是使用對象解構仍是可選參數,都各有利弊。Objective-C 與 Swift 中提供的外部參數就是不錯的函數自描述,Java 或 Python 中提供的不定參數也可以幫咱們更靈活地定義參數,在 JavaScript 中咱們則能夠經過擴展操做符實現相似的效果。而後咱們就須要去考慮如何調用函數,最典型就是就是 JavaScript 中函數調用的四種方式,咱們還須要去關心調用時函數內部的 this 指針指向。而裝飾器或者註解能幫咱們更好地組織代碼,以相似於高階函數的方式如洋蔥圈般一層一層地剝離與抽象業務邏輯。最後在函數這部分咱們還須要關心下迭代器與生成器,它們是不錯的異步實現模式或者流數據構建工具。編程

近幾年隨着前端富客戶端應用的迅猛發展與服務端併發編程的深刻應用,函數式編程以及 Haskell 這樣的函數式編程語言也是引領風騷。儘管面向對象編程也有着不少其餘被人詬病的地方,可是在大型複雜業務邏輯的應用開發中咱們仍是會傾向使用面向對象編程的範式;這就要求咱們對於類與對象的基本語法有所掌握。咱們首先要去了解如何定義類,定義類的屬性、方法以及使用訪問修飾符等方式進行訪問控制。其次咱們須要瞭解如何從類中實例化出對象,如何在具體的語言中實踐單例模式等。而後咱們就須要去了解面向對象的繼承與多態的特性,應該如何實現類繼承,子類與父類在靜態屬性、靜態方法、類屬性、構造函數上的調用順序是怎樣的;以及如何利用純虛函數、抽象類、接口、協議這些不一樣的關鍵字在具體語言中實現多態與約定。最後咱們還須要去關注下語言是否支持內部類,譬如 Java 就分爲了靜態內部類、成員內部類、局部內部類與匿名內部類這四種不一樣的分類。在整個語法基礎部分的最後,咱們還須要去了解下輸入輸出流與模塊化相關的知識,譬如 Java 9 中即將推出 JPMS 模塊化系統,而 JavaScript 的模塊化標準則歷經了 CommonJS、AMD、UMD、ES6 Modules 等多輪變遷。設計模式

數據結構與功能

語法基礎是咱們掌握某門編程語言的敲門磚,而學習內建的數據結構與功能語法則是可以用該語言進行實際應用開發的重要前提。在數據結構的學習中咱們首先要對該語言內建的數據類型有所概覽,咱們要了解如何進行常見的類型與值判斷以及類型間轉換;譬如如何進行引用與值的等價性判斷、如何進行動態類型檢查、如何對複合對象的經常使用屬性進行判斷等等。不少編程語言中會將數據類型劃分爲原始類型(Primitive)與複合類型(Composite),不過這裏爲了保證通用性仍是將學習複雜度較低的數據類型劃歸到基本類型中。常見的基本類型囊括了數值類型、空類型、布爾類型、可選類型(Optional)以及枚舉類型(Enum)等等。學習數值類型的時候咱們還須要瞭解如何進行隨機數生成、如何進行常見的科學計算,這也是基礎的數值理論算法的重要組成。JavaScript 中提供了 undefined 與 null 兩個關鍵字,兩者均可以認爲是空類型不過又有所區別;而可選類型則可以幫咱們更好地處理可能爲空地對象,避免不少的運行時錯誤。接下來咱們就要將目光投注於字符串類型上,咱們須要瞭解如何建立、刪除、複製、替換某個字符串或者其餘內容;不少語言也提供了模板字符串或者格式化字符串的方式來建立新字符串。咱們還須要知道如何對字符串進行索引遍歷,如何對字符串進行常見的類型編碼以及如何實踐模式匹配。模式匹配中最直接的方式就是使用正則表達式,這也是咱們應用開發中常常會使用到的技術點。除此以外咱們還須要關注字符串校驗、以及如何進行高效模糊搜索等等內容;咱們也能夠學習使用 KMP、Sunday 等常見的模式匹配算法來處理搜索問題。瀏覽器

而後咱們須要學習常見的時間與日期處理方式,瞭解如什麼時候間戳、時區、RFC282二、ISO8601 這些基礎的時間與日期相關的概念,瞭解如何從時間戳或者時間字符串中解析出當前編程語言支持的時間與日期對象。咱們還須要瞭解時區轉換、時間比較以及如何格式化地展現時間等內容,有時候咱們還須要利用日曆等對象進行事件的增減以及偏移計算。接下來就是很是重要的集合類型,不管哪一種編程語言都會提供相似於 Array、List、Set、Dictionary、Map 等相關的數據結構實現,而咱們也就須要去了解這些常見集合類型中的增刪復替以及索引遍歷這些基礎操做以及每一個集合的特色;譬如對於序列類型咱們要能熟練使用 map、reduce、filter、sort 這些常見的變換進行序列變換與生成。進階而言的話咱們能夠多瞭解下這些數據結構的底層算法實現,譬如 Java 8 中對於 HashMap 的鏈表/紅黑樹實現,或者 V8 中是如何利用 Hidden Class 進行快速索引的。接下來的話咱們能夠對於像 Java 中 SteamAPI 或者各類語言的 Immutable 對象的實現方式有所瞭解,還有就是常見的 JSON、XML、CSV 這些類型的序列化與反序列化操做庫也是實際開發中常常用到的。緩存

接下來咱們就須要對語言提供的經常使用外部功能相關的 API 或者語法有所掌握,主要也是分爲存儲、網絡與系統進程這三個部分。在存儲部分咱們須要掌握如何與 MySQL、Redis、Mongodb 等關係型或者非關係型數據庫進行數據交互,掌握如何對文件系統進行如文件尋址、文件監控等操做,而且還須要可以使用一些譬如 Java 堆外存儲這樣的應用內緩存來存放數據。而網絡部分咱們應該掌握如何利用 HTTP 客戶端進行網絡交互、如何使用相對底層的 Socket 套接字創建 TCP 鏈接、或者使用語言內置的一些遠程調用框架與遠端服務進行交互。最後咱們須要對如何利用語言進行系統進程操做有所瞭解,本部分筆者認爲最重要的當屬併發編程相關知識。在而今服務器性能不斷提高、處理的數據量愈來愈多的狀況下,咱們不可避免地須要使用併發操做來提升應用吞吐量。併發編程領域咱們應該去學習如何使用線程、線程池或者協程來實現併發,如何利用鎖、事務等方式進行併發控制並保持數據一致性,如何使用回調、Promise、Generator、Async/Await 等異步編程模式。除此以外,咱們還須要對切面編程、系統調用以及本地跨語言調用有所瞭解。

工程實踐與進階

編程語言初學階段的最後咱們須要瞭解下工程實踐以及一些偏原理與底層實現的進階內容。首先開發者應當對具體編程語言中如何實現 S.O.L.I.D 編程原則與數十種設計模式有所瞭解,固然也不能邯鄲學步只求形似,而是可以根據業務功能需求靈活地選擇適用的範式。而在團隊開發中咱們每每還須要統一團隊內的樣式指南,包括代碼風格約定中常見的命名約定、文檔與註釋約定、項目與模塊的目錄架構以及語法檢查規範等。接下來咱們還須要對語言或者經常使用開發工具的調試方式有所瞭解,掌握基本的單步調試等技巧,而且可以爲代碼編寫合適的單元測試用例。工程實踐方面的最後則是要求咱們對代碼性能優化全部瞭解,儘可能避免反模式。
進階內容的話則相對更加地抽象或者須要花費更多的精力去學習,其中包括泛型編程、元編程、函數式編程、響應式編程、內存管理、數據結構與算法等幾個部分。泛型編程與元編程中的反射、代碼生成、依賴注入等仍是屬於語言自己提供的語法特性之一,而函數式編程與響應式編程則偏向於實際應用開發中有所偏心的開發範式。即便 Java 這樣純粹的面向對象的語言,當咱們借鑑純函數、不可變對象、高階函數、Monad 等函數式編程中常見的名詞時,也能爲代碼優化開闢新的思路。響應式編程是很是不錯的異步編程範式,這裏咱們還須要注意下併發編程與異步編程之間的差別。而內存管理則有助於咱們理解編程語言運行地底層機制,譬如對於 JVM 或者 V8 的內存結構、內存分配、垃圾回收機制有所瞭解的話可以反過來有助於咱們編寫高性能地應用程序,而且對於線上應用錯誤的調試也能更加駕輕就熟。

界面基礎

用戶界面是前端應用程序的核心組成部分,而咱們涉足前端開發的第一步每每也就是從簡單的界面搭建開始。咱們多是在 Android 中編寫簡單的基於 XML 佈局的 Activity,在 iOS 中利用 StoryBoard 快速構建導航界面,或者在 Web 中使用某個框架實現 TODOList。而界面開發最基礎的部分就是佈局與定位,不管在何端開發中咱們每每都會使用相對佈局、絕對佈局、彈性佈局、網格佈局等佈局方式;而且面向多尺寸的屏幕咱們每每也須要進行響應式佈局的考慮,從橫豎屏響應式切換到不一樣分辨率下的佈局與尺寸的調整,都是爲了給予用戶較好的使用體驗。而瞭解了佈局與定位以後,咱們每每就須要來學習如何使用基本的界面容器,譬如常見的滾動視圖、導航視圖、頁卡視圖與伸縮視圖。Android 與 iOS 每每也爲咱們對這些基本容器進行了較好地封裝,而 Web 中則每每須要咱們本身動手去實現相應功能。譬如在滾動視圖中,咱們須要去提供常見的滾動事件控制,典型的有如何在不一樣環境下保證平滑滾動體驗、如何設置優美的滾動條、如何設置滾動監聽等等。除此以外,咱們每每還須要針對列表或者長閱讀界面封裝一些高級事件響應,譬如上拉加載、下拉刷新以及無限滾動時須要的滾動觸發規則實現。做爲最多見的用戶交互方式之一,不管是在移動端仍是桌面端,咱們也都須要實現一些優美的動畫;譬如視差滾動就能夠給用戶帶來不同的視覺感覺,而像 Swiper 這樣的整頁滾動則是很好地產品展現或者講演頁的交互方式。
在基礎的界面容器使用中咱們已經接觸了一些用戶交互的監聽與響應的實現,接下來咱們則是須要深刻全面地瞭解用戶交互相關內容。最基礎的咱們須要瞭解經常使用事件與手勢操做,瞭解如何進行事件監聽與綁定、如何捕獲事件而且進行分發、如何進行縮放、拖拽、搖晃等複雜手勢動做地監聽與識別、如何響應鍵盤事件而且進行響應處理。除此以外,筆者將音視頻錄製與播放,指紋、計步器等傳感器的使用,本地通知與遠程推送等內容也都概括於了用戶交互這個分類下。在 Android 與 iOS 開發中相信對於這些 API 的使用並不會陌生,而隨着 HTML5 的流行以及現代瀏覽器的發展,相信將來 Web 應用也會愈來愈多地添加這些與系統層面進行交互地功能。咱們在本部分還須要瞭解下動畫與變換、繪圖及數據可視化等相關內容。常見的動畫引擎包含了屬性控制與幀動畫兩種方式,前者更趨向於命令式編程然後者則適用於聲明式編程;除了瞭解這些基礎的語法,咱們還須要對經常使用的動畫進行收集與彙總,以便在項目開發中可以靈活應用。而隨着大數據時代的到來,數據可視化相關應用也成了前端開發常見的任務之一。在這個部分,咱們須要對 SVG、Canvas、WebGL 等相關繪畫基礎有所瞭解,可以運用 D3.js 或者其餘相似的庫進行簡單圖形繪製。而且咱們要可以利用 ECharts 等優秀的外部繪圖庫進行散點圖、折線圖、流程圖等常見類型圖表進行繪製。最後,地圖以及相關技術也是咱們須要去了解的,做爲開發者咱們要可以基於百度地圖等第三方 API 或者 SDK 開發導航、地理位置信息可視化等相關的功能。

系統功能

與界面基礎相對的就是常見的系統功能以及 API 的使用語法,其主要分爲系統與進程、數據存儲以及網絡交互這三個部分。

進程與存儲

在開發多界面應用程序或者利用 Service、ServiceWorker 等方式啓動後臺線程時,咱們就須要考慮如何進行組件間通訊;譬如在 Android 開發中咱們能夠利用 Otto 等庫以消息總線的方式在 Activity、Fragment、Service 等組件之間傳遞消息。而在 Android 或者 iOS 開發中咱們也經常須要考慮併發編程,可能會涉及到如何利用 Thread、GCD 等方式實現多線程並行、如何利用 RxJava 等響應式擴展優化異步編程模型、如何利用鎖等同步方式進行併發控制等等內容。有時候咱們也須要去更多地瞭解系統服務相關的內容,特別是在 Android 或者桌面應用程序開發時,咱們須要考慮如何實現守護進程以協調而且保障各個組件的正常運行。在系統與進程部分的最後,咱們還須要去接觸些系統輔助相關的功能實現,譬如如何進行運行環境檢測、如何利用 DeepLink 進行 APP 之間跳轉、如何進行應用的權限管理等等。接下來咱們討論下數據存儲部分應該掌握哪些內容,最簡單的就是相似於 SharedPreference、NSUserDefaults、localStorage 這樣的鍵值類存儲;複雜一點的狀況咱們可能會利用到 SQLite 或者 IndexedDB 這樣的簡化關係型或者文檔型數據庫,有時候 Realm 這樣的第三方解決方案也是不錯的選擇。不少時候咱們還須要瞭解如何控制緩存或者剪貼板中的內容,以及如何對文件系統進行基本的操做,譬如讀寫配置文件與資源文件、瀏覽列舉文件系統中的文件而且根據不一樣的文件類型選用不一樣的處理方式。

網絡交互

而網絡交互部分更多地關注如何與服務端或者第三方系統進行交互,實際上對於如何在需求動態變化的狀況下較好地協調服務端與客戶端對於接口的定義是不少項目開發的痛點。不過從基礎使用的角度,咱們首先須要瞭解如何利用網絡客戶端進行基於 HTTP 或 HTTPS 的網絡請求。這部分咱們須要瞭解如何構造、分析、編碼 URI,如何管理請求頭、設置請求方法與請求參數,如何同步、異步或者併發地執行請求,如何進行響應解析,如何進行復雜的請求管理等等內容。除了這些,咱們還要可以利用基礎的 Socket 進行通訊,這樣有助於咱們理解通訊網絡與 TCP/IP 實現原理;咱們每每還須要關心如何利用 WebSocket 等技術實現推送與長鏈接功能,如何進行遠程與本地方法調用等等。除了這三個偏功能實現的知識點,咱們還能夠嘗試去了解下系統的底層設計原理。譬如在 Android 開發中咱們能夠嘗試去了解 Dalvik 虛擬機的工做原理,使用 Xposed 或相似工具進行系統層面的一些操做;對於 Web 開發而言咱們能夠去更多地關注瀏覽器工做原理,瞭解現代瀏覽器的運行機制等等內容。

界面插件

在掌握瞭如何構建基本的界面而且爲應用添加必須的功能以後,咱們就須要去嘗試進行應用項目開發。每一個應用能夠按照用戶交互地邏輯切分爲多個獨立界面,而每一個界面的開發中咱們每每又須要編寫導航、菜單、列表、表單等等可重複使用的界面插件。實際上前端開發中最核心的工做之一就是界面插件的開發,好的開發者可以在項目開發中沉澱出可複用的界面插件庫;這類可複用的界面插件每每會獨立於具體的業務邏輯,其分類天然也應按照顯示或者交互邏輯自己,而不該該受制於不一樣的業務場景。筆者習慣地會將界面插件區分爲指示器(Indicator)、輸入器(Picker)、列表與表單(TableGrid)、對話框(Dialog)、畫廊(Galley)、WebView 等幾個部分。

指示器與輸入器

指示器與輸入器算是兩個寬泛的界面插件分類,最多見的指示器當屬文本顯示類別的插件,譬如標籤。標籤多用於表單中的輸入域描述、用戶引導等場景,而除了文字標籤以外咱們也會使用圖標或者所謂的 Tags。除此以外咱們還會關注於 MarkDown 等富文本的展現、如何針對不一樣屏幕對頁面進行排版與字體設置、如何針對不一樣地區的用戶進行國際化切換、如何爲文本添加合適的動畫等等方面。在應用開發中咱們也會添加專門的介紹或引導頁,一方面引導用戶使用,另外一方面也能夠進行後臺資源請求與處理;譬如咱們每每會在應用啓動時設置閃屏頁(Splash),記得最先在 Uber 見到以短視頻爲背景的閃屏頁頗有耳目一新的感受。除此以外,咱們常見的指示器還包括了進度指示與時間指示這兩種。在進行數據請求或者數據處理等須要用戶等待的場景中,咱們每每會給用戶以進度條方式地友好反饋,這種進度條就是典型地進度指示。經常使用的進度條設計有線性進度條、圓形進度條或者固定在頁首或者頁尾的進度條,有些設計中咱們也會以背景投射地方式反饋當前進度,這種方式可能更具備視覺衝擊力。而除了進度條以外,無限循環的加載效果、分頁器或者步驟跟蹤顯示器也是常見的進度指示的表現形式之一。而所謂的時間指示即譬如界面上放置的擬物時鐘或者電子時鐘、常見於社交媒體上的時間軸或者日曆效果以及倒計數效果等。

而輸入器的典型表明則爲按鈕與文本輸入,譬如咱們除了常見的 Primary、Secondary 按鈕以外,咱們可能還會用到懸浮按鈕、可擴展的按鈕或者在喜歡與點贊時用到的具備必定動畫效果的按鈕。而文本輸入系列的插件中,除了常見的文本框或者富文本編輯器,有時咱們也須要去編寫具備自動補全或者相似於密碼、勾選之類的特殊格式的輸入框。選擇器也是咱們經常使用到的輸入器之一,譬如開關、單選按鈕、勾選按鈕、分段輸入以及經常使用於兩個列表互選的左右穿梭器等等。除了這些,搜索、菜單、解鎖界面也是歸屬於輸入器這個類別中。

列表、畫廊與對話框

在這兩個大類以外算得上最經常使用的插件的當屬列表、網格與表單這個系列的控件;基本上每一個應用都會包含列表或者網格佈局,對於海量數據的列表渲染也是前端常見的挑戰之一。Android 中內置的 RecycleView 與 iOS 中內置的 UITableView 都爲咱們提供了不錯的懶加載、局部渲染的功能,而 Web 中咱們每每須要本身定製或者尋求第三方庫的幫助。對於列表的交互也是常見問題之一,除了容許用戶正常的點擊,咱們還須要添加左滑右滑時的反饋、可伸縮或者容許排序、拖拽的方式進行交互,有時候還須要爲了列表項添加進出時的轉場動畫,以這種微互動增長整個界面的友好性。最後咱們來聊聊畫廊與對話框,畫廊最典型的插件就是提供圖片或者視頻預覽的走馬燈效果的輪播插件,筆者也是將圖片加載、呈現、處理相關的插件劃分到了畫廊這一系列插件中。而在端開發中咱們經常須要對相冊或者緩存中的圖片進行瀏覽,或者將圖片以瀑布流的方式呈現給用戶,這種性質的插件也應歸屬到畫廊這一類中。對話框的分類則稍顯的有些生硬,譬如 ActionSheet、HUD 是系統提供的消息提示性質的插件,這種彈出與顯示層天然會劃歸到對話框這個系列的組件中。而在 Web 中咱們經常須要自定義的模態對話框、覆蓋層也屬於對話框系列,有時候咱們還須要考慮如何爲對話框提供拖拽支持,或者在對話框顯示和消失之際添加轉場動畫。

工程化與應用架構

前面咱們討論了開發某個前端應用所須要的必備技能,而在須要持續交付的團隊項目開發中,咱們還須要考慮不少工程實踐相關的方法與技巧。命令式編程到聲明式編程的變化,將更多地功能性工做交於框架處理,而開發人員更加地專一於業務邏輯的實現。

工程實踐

代碼調試是每一個程序員都掌握的技能,不過如何較好地調試代碼以快速定位錯誤所在卻並非那麼容易。在開發中咱們經常須要熱加載、增量編譯等相關技術來避免過長的等待,而單步調試則可以幫助咱們梳理代碼邏輯、按部就班地發現問題所在。可能 iOS、Android 的開發人員更習慣使用單步調試,而在 Web 或者 Node.js 開發中咱們也應適當地多使用 Chrome 等工具進行代碼的單步調試;有時候單步調試也是不錯的瀏覽分析第三方源代碼庫的方式。另外一方面,日誌不管在開發環境仍是生產環境中都可以幫咱們記錄應用運行狀態等信息。接下來咱們還要了解應用開發週期中不一樣階段使用的單元測試、集成測試以及端到端測試的具體的實現方式,在團隊協同開發中統一代碼風格與約定,可以利用多種方式對應用進行性能優化,以及在發佈到生產環境以後可以混淆加密、進行應用更新以及應用狀態跟蹤。

應用架構

所謂架構二字,核心便是對於對於富客戶端的代碼組織/職責劃分,從具體的代碼分割的角度,便是功能的模塊化、界面的組件化、應用狀態管理這三個方面。縱覽這十年內的架構模式變遷,大概能夠分爲 MV 與 Unidirectional 兩大類,而 Clean Architecture 則是以嚴格的層次劃分獨闢蹊徑。從筆者的認知來看,從 MVC 到 MVP 的變遷完成了對於 View 與 Model 的解耦合,改進了職責分配與可測試性。而從 MVP 到 MVVM,添加了 View 與 ViewModel 之間的數據綁定,使得 View 徹底的無狀態化。最後,整個從 MV 到 Unidirectional 的變遷便是採用了消息隊列式的數據流驅動的架構,而且以 Redux 爲表明的方案將本來 MV* 中碎片化的狀態管理變爲了統一的狀態管理,保證了狀態的有序性與可回溯性。 實際上從 MVC、MVP 到 MVVM,一直圍繞的核心問題就是如何分割 ViewLogic 與 View,即如何將負責界面展現的代碼與負責業務邏輯的代碼進行分割。所謂分久必合,合久必分,從筆者自我審視的角度,發現頗有趣的一點。Android 與iOS中都是從早期的用代碼進行組件添加與佈局到專門的 XML/Nib/StoryBoard 文件進行佈局,Android 中的 Annotation/DataBinding、iOS 中的 IBOutlet 更加地保證了 View 與 ViewLogic 的分割(這一點也是從元素操做到以數據流驅動的變遷,咱們不須要再去編寫大量的 findViewById。而Web的趨勢正好有點相反,不管是 WebComponent 仍是 ReactiveComponent 都是將 ViewLogic 與 View 置於一塊兒,特別是 JSX 的語法將 JavaScript 與 HTML 混搭,很有幾分當年 PHP/JSP 與 HTML 混搭的風味。

從代碼組織的角度來看,項目的構建工具與依賴管理工具會深入地影響到代碼組織,這一點在功能的模塊化中尤爲顯著。譬如筆者對於 Android/Java 構建工具的使用變遷經歷了從 Eclipse 到 Maven 再到 Gradle,筆者會將不一樣功能邏輯的代碼封裝到不一樣的相對獨立的子項目中,這樣就保證了子項目與主項目之間的必定隔離,方便了測試與代碼維護。一樣的,在 Web 開發中從 AMD/CMD 規範到標準的 ES6 模塊與 Webpack 編譯打包,也使得代碼可以按照功能儘量地解耦分割與避免冗餘編碼。而另外一方面,依賴管理工具也極大地方便咱們使用第三方的代碼與發佈自定義的依賴項,譬如 Web 中的 NPM 與 Bower,iOS 中的 CocoaPods 都是十分優秀的依賴發佈與管理工具,使咱們不須要去關心第三方依賴的具體實現細節即可以透明地引入使用。所以選擇合適的項目構建工具與依賴管理工具也是好的GUI架構模式的重要因素之一。不過從應用程序架構的角度看,不管咱們使用怎樣的構建工具,均可以實現或者遵循某種架構模式,筆者認爲兩者之間也並無必然的因果關係。而組件便是應用中用戶交互界面的部分組成,組件能夠經過組合封裝成更高級的組件。組件能夠被放入層次化的結構中,便可以是其餘組件的父組件也能夠是其餘組件的子組件。根據上述的組件定義,筆者認爲像 Activity 或者UIViewController 都不能算是組件,而像 ListView 或者 UITableView 能夠看作典型的組件。 咱們強調的是界面組件的Composable&Reusable,便可組合性與可重用性。當咱們一開始接觸到 Android 或者 iOS 時,由於自己 SDK 的完善度與規範度較高,咱們可以不少使用封裝程度較高的組件;凡事都有雙面性,這種較高程度的封裝與規範統一的 API 方便了咱們的開發,可是也限制了咱們自定義的能力。一樣的,由於 SDK 的限制,真正意義上可複用/組合的組件也是很少,譬如你不能將兩個 ListView 再組合成一個新的ListView。在 React 中有所謂的 controller-view 的概念,即意味着某個 React 組件同時擔負起 MVC 中 Controller 與 View 的責任,也就是 JSX 這種將負責 ViewLogic 的 JavaScript 代碼與負責模板的 HTML 混編的方式。 界面的組件化還包括一個重要的點就是路由,譬如 Android 中的 AndRouter、iOS中的 JLRoutes 都是集中式路由的解決方案,不過集中式路由在 Android 或者 iOS 中並無大規模推廣。iOS 中的 StoryBoard 卻是相似於一種集中式路由的方案,不過更偏向於以 UI 設計爲核心。筆者認爲這一點多是由於 Android 或者 iOS 自己全部的代碼都是存放於客戶端自己,而 Web 中較傳統的多頁應用方式還須要用戶跳轉頁面從新加載,然後在單頁流行以後即不存在頁面級別的跳轉,所以在 Web 單頁應用中集中式路由較爲流行而 Android、iOS 中反而不流行。所謂可變的與不可預測的狀態時軟件開發中的萬惡之源,咱們儘量地但願組件的無狀態性,那麼整個應用中的狀態管理應該儘可能地放置在所謂 High-Order Component 或者 Smart Component 中。在 React 以及 Flux 的概念流行以後,Stateless Component 的概念深刻人心,不過其實對於 MVVM 中的 View,也是無狀態的 View。經過雙向數據綁定將界面上的某個元素與 ViewModel 中的變量相關聯,筆者認爲很相似於 HOC 模式中的 Container 與 Component 之間的關聯。隨着應用的界面與功能的擴展,狀態管理會變得愈發混亂。

相關文章
相關標籤/搜索