大前端公共知識雜談
大前端公共知識雜談首發於InfoQ-架構師特刊:大前端,是筆者對於泛前端知識圖譜(Web/iOS/Android/RN) 的文字版介紹,夾雜了 GUI 應用程序架構的十年變遷:MVC、MVP、MVVM、Unidirectional、Clean 的部分內容;更多參考資料可以查看筆者的 Web 學習與實踐資料索引以及 React 學習與實踐資料索引。
n
近年來,隨著移動化聯網浪潮的洶湧而來與瀏覽器性能的提升,iOS、Android、Web n等前端開發技術各領風騷,大前端的概念也日漸成為某種共識。其中特別是 Web n開發的領域,以單頁應用為代表的富客戶端應用迅速流行,各種框架理念爭妍鬥豔,百花競放。而 Web n技術的蓬勃發展也催生了一系列跨端混合開發技術,希望能夠結合 Web 的開發便捷性與原生應用的高性能性;其中以 Cordova、PWA n為代表的方向致力於為 Web 應用儘可能添加原生體驗,而以 NativeScript、ReactNative、Weex 為代表的利用 Web n技術或者理念開發原生應用。平心而論,無論哪一種開發領域或者技術,他們本質上都是進行圖形用戶界面(GUI)應用程序的開發,面對的問題、思考的方式、架構的設計很大程度上仍然可以回溯到當年以n MFC、Swing、WPF 為主導的桌面應用程序開發時代,其術不同而道相似。
n任何的前端開發學習中,我們都需要掌握基本的編程語言語法與介面;譬如在 Android 開發中使用的 Java 或者 Kotlin,在 niOS 開發中使用的 Objective-C 或者 Swift,在 Web 開發中使用的 JavaScript、HTML 與 CSS n等。編程語言的學習中我們往往關注於語法基礎、數據結構、功能調用、泛型編程、元編程等內容,譬如如何聲明表達式、如何理解作用域與閉包、如何進行基本的流程式控制制與異常處理、如何實踐面向對象編程、如何進行網路請求通信等等。接下來我們就需要了解如何構建基礎的界面,譬如利用n HTML 與 CSS 繪製簡單 Web 頁面、利用代碼創建並使用簡單的 Activity、利用 StoryBoard n快速構建界面原型等等。然後我們需要去學習使用常見的系統功能,譬如如何進行網路交互,如何訪問遠端的 RESTful n介面以獲取需要的數據、如何讀取本地文件或者利用 SharedPreference、localStorage、CoreData n來存取數據、如何進行組件間或者應用間信息交互等內容。到這裡我們已經能夠進行基礎的界面開發,並且為其增添必要的特性,不過在真實的項目中我們往往還會用到很多的組件或者插件,iOSn 或者 Android 中為我們提供了豐富的 SDK,譬如 UITableView 或者 RecycleView n可以幫助我們快速構建高性能列表組件,Android 5.0 之後默認的 Material Design 也是非常優秀的界面樣式設計指南;而 nWeb 開發中我們往往需要引入第三方模式庫,譬如著名的 BootStrap、React Material UI、Vue element n都為我們提供了很多預置的樣式組件,react-virtualized 也為我們提供了高性能的類似於 ListView n這樣的部分項渲染機制。然後我們需要將應用真實地發布給用戶使用,我們需要考慮很多工程實踐的問題,譬如如何進行測試與調試、如何進行性能優化並且在生產環境下完成應用狀態跟蹤、熱更新等操作、如何統一開發團隊的代碼風格與約定等等;這裡n Web 因為其特性而自帶了熱更新的功能,而在 Android 或者 iOS 我們則可以利用插件化技術或者 JSPatch n來實現熱更新。Java 與 Swift 都是強類型語言,其能夠在編譯階段幫開發者排查問題減少潛在風險;而我們也可以使用 TypeScript n或者 Flow 為 JavaScript 添加靜態類型檢測的特性,在 VSCode 等現代編輯器中同樣可以達到類似於 Android nStudio、XCode n中的即時檢查與提示的功能。最後,隨著應用功能的增加、代碼庫的擴展,我們需要考慮整體的應用架構與工程化的問題;在應用架構中我們往往需要考慮模塊化、組件化以及狀態管理等多個方面,選擇合適的n MVC、MVP、MVVM、Flux、VIPER n等不同的架構模式來引導應用中的代碼組織與職責分割;我們也需要考慮選擇合適的構建與部署工具來簡化或者自動化應用發布流程,在 Android n開發中我們會選擇 Gradle 及其自帶的多模塊特性來管理依賴與分割代碼,而在 Web 中我們可以使用 Webpack、Rollup n等工具來自動處理依賴並且進行構建,iOS 中我們也可以選擇 CoocaPods。
n
到這裡我們會發現雖然具體的代碼實現、使用的技術不同,但是 Android、iOS 以及 Web 乃至於 React Native n等開發中,我們需要解決的問題、能夠用到的架構設計模式都是可以相互借鑒的。在我們從某個領域遷移到其他領域時,我們能很方便地知道應該學習些什麼,不同的技術、工具他們的職責是什麼,應該選擇怎樣的架構或者設計模式。古語云,欲窮千里目,更上一層樓,我們想要真正掌握某種客戶端開發技術,最好是要了解我們應該掌握哪些方面,本文即是對筆者日前總結出的泛前端知識圖譜(Web/iOS/Android/RN) 的簡要闡釋。
n編程語言
n
編程語言的學習是我們進入軟體世界的基礎階梯,著名的 Code Completen 一書中提到:Program into Your Language, Not in it. n我們不應該將自己的編程思維局限於掌握的語言提供的那些特性或者概念,而是能夠理解這些語法特性背後能提供的抽象功能與原理,從而能夠根據自己想要達到的目標選擇最合適的編程語言。而從另一個角度來看,無論哪一門編程語言的學習也是具有極大的共性,從嚴謹而又被詬病過度冗餘的n Java 到需要用遊標卡尺的 Python,從掙扎著一路向前的 JavaScript 到含著金湯匙出生的 nSwift、Rust,我們都能夠發現其中的相通與互相借鑒之處。
n語法基礎
n
任何一門編程語言的學習都需要從基本的表達式(Expression)語法開始學習,我們需要了解如何去聲明與使用變數、如何為這些變數賦值、如何使用運算符進行簡單的變數操作等等。在很多語言之中都有所謂的傳值還是傳引用的思量,譬如n Java 與 JavaScript 本質上就是 Pass-by-Value n的語言,只不過會將複雜對象的引用值傳遞給目標變數。這個特性又引發了所謂淺複製與深複製、如何進行複合類型深拷貝等等需要注意的技術點。除此之外,作用域與閉包也是很多語言學習中重點討論的內容,在n JavaScript 與 Python n的學習中我們就會經常討論如何利用閉包來保存外部變數,或者在循環中避免閉包帶來的意外變數值。表達式是一門編程語言語法基礎的重要組成部分,接下來我們就需要去學習流程式控制制與異常處理、函數定義與調用、類與對象、輸入輸出流、模塊等內容。流程式控制制的典型代表就是分支選擇與循環,譬如不同的語言都為我們提供了基礎n for 循環或者更方便地 for-in 循環,而在 JavaScript 中我們還可以使用 forEach 與 for-of 循環,Java 8n 之後我們也可以基於 Stream API 中的 forEach 編寫聲明式地循環執行體,而 Python n中的列表推導也可以看做便捷的循環實現方式。異常處理也是各個編程語言的重要組成部分,合理的異常處理有助於增強應用的魯棒性;不過很多時候會出現濫用異常的情況,我們只是一層一層地拋出而並未真正地去處理或者利用這些異常。Javan 中將異常分為了受控異常與不受控異常這兩類,雖然 JavaScript n等語言中並未在數據類型中有所區分,但是卻可以引入這種分類方式來進行不同的異常處理;有時候 Let it Crash 也是不錯的設計模式。
nEric Elliott 曾在博文中提及,軟體開發實際就是 Function Composition 與 DataStructure nDesign;函數或者方法是軟體系統的重要基石與組成。我們需要了解如何去定義函數,包括匿名函數以及 Lambda 表達式等;儘管 Java 中的n Lambda 表達式是對於 FunctionalInterface n的實現,但是鑒於其表現形式我們也可以將其劃歸到函數這個知識類別中。接下來我們需要了解如何定義與傳入函數參數,在 C n這樣的語言中我們會去關心指針傳遞的不同姿勢;而在 JavaScript n中我們常常會關心如何設置默認參數,無論是使用對象解構還是可選參數,都各有利弊。Objective-C 與 Swift n中提供的外部參數就是不錯的函數自描述,Java 或 Python 中提供的不定參數也能夠幫我們更靈活地定義參數,在 JavaScript n中我們則可以通過擴展操作符實現類似的效果。然後我們就需要去考慮如何調用函數,最典型就是就是 JavaScript n中函數調用的四種方式,我們還需要去關心調用時函數內部的 this n指針指向。而裝飾器或者註解能幫我們更好地組織代碼,以類似於高階函數的方式如洋蔥圈般一層一層地剝離與抽象業務邏輯。最後在函數這部分我們還需要關心下迭代器與生成器,它們是不錯的非同步實現模式或者流數據構建工具。
n
近幾年隨著前端富客戶端應用的迅猛發展與服務端並發編程的深入應用,函數式編程以及 Haskell n這樣的函數式編程語言也是引領風騷。儘管面向對象編程也有著很多其他被人詬病的地方,但是在大型複雜業務邏輯的應用開發中我們還是會傾向使用面向對象編程的範式;這就要求我們對於類與對象的基本語法有所掌握。我們首先要去了解如何定義類,定義類的屬性、方法以及使用訪問修飾符等方式進行訪問控制。其次我們需要了解如何從類中實例化出對象,如何在具體的語言中實踐單例模式等。然後我們就需要去了解面向對象的繼承與多態的特性,應該如何實現類繼承,子類與父類在靜態屬性、靜態方法、類屬性、構造函數上的調用順序是怎樣的;以及如何利用純虛函數、抽象類、介面、協議這些不同的關鍵字在具體語言中實現多態與約定。最後我們還需要去關注下語言是否支持內部類,譬如n Java n就分為了靜態內部類、成員內部類、局部內部類與匿名內部類這四種不同的分類。在整個語法基礎部分的最後,我們還需要去了解下輸入輸出流與模塊化相關的知識,譬如n Java 9 中即將推出 JPMS 模塊化系統,而 JavaScript 的模塊化標準則歷經了 CommonJS、AMD、UMD、ES6 nModules 等多輪變遷。
n數據結構與功能
n
語法基礎是我們掌握某門編程語言的敲門磚,而學習內建的數據結構與功能語法則是能夠用該語言進行實際應用開發的重要前提。在數據結構的學習中我們首先要對該語言內建的數據類型有所概覽,我們要了解如何進行常見的類型與值判斷以及類型間轉換;譬如如何進行引用與值的等價性判斷、如何進行動態類型檢查、如何對複合對象的常用屬性進行判斷等等。很多編程語言中會將數據類型劃分為原始類型(Primitive)與複合類型(Composite),不過這裡為了保證通用性還是將學習複雜度較低的數據類型劃歸到基本類型中。常見的基本類型囊括了數值類型、空類型、布爾類型、可選類型(Optional)以及枚舉類型(Enum)等等。學習數值類型的時候我們還需要了解如何進行隨機數生成、如何進行常見的科學計算,這也是基礎的數值理論演算法的重要組成。JavaScriptn 中提供了 undefined 與 null n兩個關鍵字,二者都可以認為是空類型不過又有所區別;而可選類型則能夠幫我們更好地處理可能為空地對象,避免很多的運行時錯誤。接下來我們就要將目光投注於字元串類型上,我們需要了解如何創建、刪除、複製、替換某個字元串或者其他內容;很多語言也提供了模板字元串或者格式化字元串的方式來創建新字元串。我們還需要知道如何對字元串進行索引遍歷,如何對字元串進行常見的類型編碼以及如何實踐模式匹配。模式匹配中最直接的方式就是使用正則表達式,這也是我們應用開發中經常會使用到的技術點。除此之外我們還需要關注字元串校驗、以及如何進行高效模糊搜索等等內容;我們也可以學習使用n KMP、Sunday 等常見的模式匹配演算法來處理搜索問題。
n然後我們需要學習常見的時間與日期處理方式,了解如何時間戳、時區、RFC2822、ISO8601 n這些基礎的時間與日期相關的概念,了解如何從時間戳或者時間字元串中解析出當前編程語言支持的時間與日期對象。我們還需要了解時區轉換、時間比較以及如何格式化地展示時間等內容,有時候我們還需要利用日曆等對象進行事件的增減以及偏移計算。接下來就是非常重要的集合類型,無論哪種編程語言都會提供類似於n Array、List、Set、Dictionary、Map n等相關的數據結構實現,而我們也就需要去了解這些常見集合類型中的增刪復替以及索引遍歷這些基礎操作以及每個集合的特點;譬如對於序列類型我們要能熟練使用n map、reduce、filter、sort 這些常見的變換進行序列變換與生成。進階而言的話我們可以多了解下這些數據結構的底層演算法實現,譬如 nJava 8 中對於 HashMap 的鏈表/紅黑樹實現,或者 V8 中是如何利用 Hidden Class n進行快速索引的。接下來的話我們可以對於像 Java 中 SteamAPI 或者各種語言的 Immutable n對象的實現方式有所了解,還有就是常見的 JSON、XML、CSV 這些類型的序列化與反序列化操作庫也是實際開發中經常用到的。
n接下來我們就需要對語言提供的常用外部功能相關的 API n或者語法有所掌握,主要也是分為存儲、網路與系統進程這三個部分。在存儲部分我們需要掌握如何與 MySQL、Redis、Mongodb n等關係型或者非關係型資料庫進行數據交互,掌握如何對文件系統進行如文件定址、文件監控等操作,並且還需要能夠使用一些譬如 Java n堆外存儲這樣的應用內緩存來存放數據。而網路部分我們應該掌握如何利用 HTTP 客戶端進行網路交互、如何使用相對底層的 Socket 套接字建立 nTCP n連接、或者使用語言內置的一些遠程調用框架與遠端服務進行交互。最後我們需要對如何利用語言進行系統進程操作有所了解,本部分筆者認為最重要的當屬並發編程相關知識。在而今伺服器性能不斷提升、處理的數據量越來越多的情況下,我們不可避免地需要使用並發操作來提高應用吞吐量。並發編程領域我們應該去學習如何使用線程、線程池或者協程來實現並發,如何利用鎖、事務等方式進行並發控制並保持數據一致性,如何使用回調、Promise、Generator、Async/Awaitn 等非同步編程模式。除此之外,我們還需要對切面編程、系統調用以及本地跨語言調用有所了解。
n工程實踐與進階
n
編程語言初學階段的最後我們需要了解下工程實踐以及一些偏原理與底層實現的進階內容。首先開發者應當對具體編程語言中如何實現 S.O.L.I.Dn n編程原則與數十種設計模式有所了解,當然也不能邯鄲學步只求形似,而是能夠根據業務功能需求靈活地選擇適用的範式。而在團隊開發中我們往往還需要統一團隊內的樣式指南,包括代碼風格約定中常見的命名約定、文檔與注釋約定、項目與模塊的目錄架構以及語法檢查規範等。接下來我們還需要對語言或者常用開發工具的調試方式有所了解,掌握基本的單步調試等技巧,並且能夠為代碼編寫合適的單元測試用例。工程實踐方面的最後則是要求我們對代碼性能優化所有了解,盡量避免反模式。進階內容的話則相對更加地抽象或者需要花費更多的精力去學習,其中包括泛型編程、元編程、函數式編程、響應式編程、內存管理、數據結構與演算法等幾個部分。泛型編程與元編程中的反射、代碼生成、依賴注入等還是屬於語言本身提供的語法特性之一,而函數式編程與響應式編程則偏向於實際應用開發中有所偏愛的開發範式。即使n Java 這樣純粹的面向對象的語言,當我們借鑒純函數、不可變對象、高階函數、Monad n等函數式編程中常見的名詞時,也能為代碼優化開闢新的思路。響應式編程是非常不錯的非同步編程範式,這裡我們還需要注意下並發編程與非同步編程之間的差異。而內存管理則有助於我們理解編程語言運行地底層機制,譬如對於n JVM 或者 V8 n的內存結構、內存分配、垃圾回收機制有所了解的話能夠反過來有助於我們編寫高性能地應用程序,並且對於線上應用錯誤的調試也能更加得心應手。
n
界面基礎
n
用戶界面是前端應用程序的核心組成部分,而我們涉足前端開發的第一步往往也就是從簡單的界面搭建開始。我們可能是在 Android n中編寫簡單的基於 XML 布局的 Activity,在 iOS 中利用 StoryBoard 快速構建導航界面,或者在 Web n中使用某個框架實現 nTODOList。而界面開發最基礎的部分就是布局與定位,無論在何端開發中我們往往都會使用相對布局、絕對布局、彈性布局、網格布局等布局方式;並且面向多尺寸的屏幕我們往往也需要進行響應式布局的考慮,從橫豎屏響應式切換到不同解析度下的布局與尺寸的調整,都是為了給予用戶較好的使用體驗。而了解了布局與定位之後,我們往往就需要來學習如何使用基本的界面容器,譬如常見的滾動視圖、導航視圖、頁卡視圖與伸縮視圖。Androidn 與 iOS 往往也為我們對這些基本容器進行了較好地封裝,而 Web n中則往往需要我們自己動手去實現相應功能。譬如在滾動視圖中,我們需要去提供常見的滾動事件控制,典型的有如何在不同環境下保證平滑滾動體驗、如何設置優美的滾動條、如何設置滾動監聽等等。除此之外,我們往往還需要針對列表或者長閱讀界面封裝一些高級事件響應,譬如上拉載入、下拉刷新以及無限滾動時需要的滾動觸發規則實現。作為最常見的用戶交互方式之一,無論是在移動端還是桌面端,我們也都需要實現一些優美的動畫;譬如視差滾動就可以給用戶帶來不一樣的視覺感受,而像n Swiper 這樣的整頁滾動則是很好地產品展示或者講演頁的交互方式。在基礎的界面容器使用中我們已經接觸了一些用戶交互的監聽與響應的實現,接下來我們則是需要深入全面地了解用戶交互相關內容。最基礎的我們需要了解常用事件與手勢操作,了解如何進行事件監聽與綁定、如何捕獲事件並且進行分發、如何進行縮放、拖拽、搖晃等複雜手勢動作地監聽與識別、如何響應鍵盤事件並且進行響應處理。除此之外,筆者將音視頻錄製與播放,指紋、計步器等感測器的使用,本地通知與遠程推送等內容也都歸納於了用戶交互這個分類下。在n Android 與 iOS 開發中相信對於這些 API 的使用並不會陌生,而隨著 HTML5 的流行以及現代瀏覽器的發展,相信未來 Web n應用也會越來越多地添加這些與系統層面進行交互地功能。我們在本部分還需要了解下動畫與變換、繪圖及數據可視化等相關內容。常見的動畫引擎包含了屬性控制與幀動畫兩種方式,前者更趨向於命令式編程而後者則適用於聲明式編程;除了了解這些基礎的語法,我們還需要對常用的動畫進行收集與匯總,以便在項目開發中能夠靈活應用。而隨著大數據時代的到來,數據可視化相關應用也成了前端開發常見的任務之一。在這個部分,我們需要對n SVG、Canvas、WebGL 等相關繪畫基礎有所了解,能夠運用 D3.js 或者其他類似的庫進行簡單圖形繪製。並且我們要能夠利用 nECharts n等優秀的外部繪圖庫進行散點圖、折線圖、流程圖等常見類型圖表進行繪製。最後,地圖以及相關技術也是我們需要去了解的,作為開發者我們要能夠基於百度地圖等第三方n API 或者 SDK 開發導航、地理位置信息可視化等相關的功能。
n系統功能
n
與界面基礎相對的就是常見的系統功能以及 API 的使用語法,其主要分為系統與進程、數據存儲以及網路交互這三個部分。
n進程與存儲
n
在開發多界面應用程序或者利用 Service、ServiceWorker 等方式啟動後台線程時,我們就需要考慮如何進行組件間通信;譬如在 nAndroid 開發中我們可以利用 Otto 等庫以消息匯流排的方式在 Activity、Fragment、Service n等組件之間傳遞消息。而在 Android 或者 iOS 開發中我們也常常需要考慮並發編程,可能會涉及到如何利用 Thread、GCD n等方式實現多線程並行、如何利用 RxJava n等響應式擴展優化非同步編程模型、如何利用鎖等同步方式進行並發控制等等內容。有時候我們也需要去更多地了解系統服務相關的內容,特別是在 Androidn n或者桌面應用程序開發時,我們需要考慮如何實現守護進程以協調並且保障各個組件的正常運行。在系統與進程部分的最後,我們還需要去接觸些系統輔助相關的功能實現,譬如如何進行運行環境檢測、如何利用n DeepLink 進行 APP 之間跳轉、如何進行應用的許可權管理等等。接下來我們討論下數據存儲部分應該掌握哪些內容,最簡單的就是類似於 nSharedPreference、NSUserDefaults、localStorage 這樣的鍵值類存儲;複雜一點的情況我們可能會利用到 nSQLite 或者 IndexedDB 這樣的簡化關係型或者文檔型資料庫,有時候 Realm n這樣的第三方解決方案也是不錯的選擇。很多時候我們還需要了解如何控制緩存或者剪貼板中的內容,以及如何對文件系統進行基本的操作,譬如讀寫配置文件與資源文件、瀏覽列舉文件系統中的文件並且根據不同的文件類型選用不同的處理方式。
n網路交互
n
而網路交互部分更多地關注如何與服務端或者第三方系統進行交互,實際上對於如何在需求動態變化的情況下較好地協調服務端與客戶端對於介面的定義是很多項目開發的痛點。不過從基礎使用的角度,我們首先需要了解如何利用網路客戶端進行基於n HTTP 或 HTTPS 的網路請求。這部分我們需要了解如何構造、分析、編碼 nURI,如何管理請求頭、設置請求方法與請求參數,如何同步、非同步或者並發地執行請求,如何進行響應解析,如何進行複雜的請求管理等等內容。除了這些,我們還要能夠利用基礎的n Socket 進行通信,這樣有助於我們理解通信網路與 TCP/IP 實現原理;我們往往還需要關心如何利用 WebSocket n等技術實現推送與長連接功能,如何進行遠程與本地方法調用等等。除了這三個偏功能實現的知識點,我們還可以嘗試去了解下系統的底層設計原理。譬如在 nAndroid 開發中我們可以嘗試去了解 Dalvik 虛擬機的工作原理,使用 Xposed 或類似工具進行系統層面的一些操作;對於 Web n開發而言我們可以去更多地關注瀏覽器工作原理,了解現代瀏覽器的運行機制等等內容。
n界面插件
n
在掌握了如何構建基本的界面並且為應用添加必須的功能之後,我們就需要去嘗試進行應用項目開發。每個應用可以按照用戶交互地邏輯切分為多個獨立界面,而每個界面的開發中我們往往又需要編寫導航、菜單、列表、表單等等可重複使用的界面插件。實際上前端開發中最核心的工作之一就是界面插件的開發,好的開發者能夠在項目開發中沉澱出可復用的界面插件庫;這類可復用的界面插件往往會獨立於具體的業務邏輯,其分類自然也應按照顯示或者交互邏輯本身,而不應該受制於不同的業務場景。筆者習慣地會將界面插件區分為指示器(Indicator)、輸入器(Picker)、列表與表單(TableGrid)、對話框(Dialog)、畫廊(Galley)、WebViewn 等幾個部分。
n
指示器與輸入器
n
指示器與輸入器算是兩個寬泛的界面插件分類,最常見的指示器當屬文本顯示類別的插件,譬如標籤。標籤多用於表單中的輸入域描述、用戶引導等場景,而除了文字標籤之外我們也會使用圖標或者所謂的n Tags。除此之外我們還會關注於 MarkDown n等富文本的展示、如何針對不同屏幕對頁面進行排版與字體設置、如何針對不同地區的用戶進行國際化切換、如何為文本添加合適的動畫等等方面。在應用開發中我們也會添加專門的介紹或引導頁,一方面引導用戶使用,另一方面也可以進行後台資源請求與處理;譬如我們往往會在應用啟動時設置閃屏頁(Splash),記得最早在n Uber n見到以短視頻為背景的閃屏頁很有耳目一新的感覺。除此之外,我們常見的指示器還包括了進度指示與時間指示這兩種。在進行數據請求或者數據處理等需要用戶等待的場景中,我們往往會給用戶以進度條方式地友好反饋,這種進度條就是典型地進度指示。常用的進度條設計有線性進度條、圓形進度條或者固定在頁首或者頁尾的進度條,有些設計中我們也會以背景投射地方式反饋當前進度,這種方式可能更具有視覺衝擊力。而除了進度條之外,無限循環的載入效果、分頁器或者步驟跟蹤顯示器也是常見的進度指示的表現形式之一。而所謂的時間指示即譬如界面上放置的擬物時鐘或者電子時鐘、常見於社交媒體上的時間軸或者日曆效果以及倒計數效果等。
n而輸入器的典型代表則為按鈕與文本輸入,譬如我們除了常見的 Primary、Secondary n按鈕之外,我們可能還會用到懸浮按鈕、可擴展的按鈕或者在喜歡與點贊時用到的具有一定動畫效果的按鈕。而文本輸入系列的插件中,除了常見的文本框或者富文本編輯器,有時我們也需要去編寫具有自動補全或者類似於密碼、勾選之類的特殊格式的輸入框。選擇器也是我們常用到的輸入器之一,譬如開關、單選按鈕、勾選按鈕、分段輸入以及常用於兩個列表互選的左右穿梭器等等。除了這些,搜索、菜單、解鎖界面也是歸屬於輸入器這個類別中。
n列表、畫廊與對話框
n
在這兩個大類之外算得上最常用的插件的當屬列表、網格與表單這個系列的控制項;基本上每個應用都會包含列表或者網格布局,對於海量數據的列表渲染也是前端常見的挑戰之一。Androidn 中內置的 RecycleView 與 iOS 中內置的 UITableView 都為我們提供了不錯的懶載入、局部渲染的功能,而 Web n中我們往往需要自己定製或者尋求第三方庫的幫助。對於列表的交互也是常見問題之一,除了允許用戶正常的點擊,我們還需要添加左滑右滑時的反饋、可伸縮或者允許排序、拖拽的方式進行交互,有時候還需要為了列表項添加進出時的轉場動畫,以這種微互動增加整個界面的友好性。最後我們來聊聊畫廊與對話框,畫廊最典型的插件就是提供圖片或者視頻預覽的走馬燈效果的輪播插件,筆者也是將圖片載入、呈現、處理相關的插件劃分到了畫廊這一系列插件中。而在端開發中我們常常需要對相冊或者緩存中的圖片進行瀏覽,或者將圖片以瀑布流的方式呈現給用戶,這種性質的插件也應歸屬到畫廊這一類中。對話框的分類則稍顯的有些生硬,譬如n ActionSheet、HUD 是系統提供的消息提示性質的插件,這種彈出與顯示層自然會劃歸到對話框這個系列的組件中。而在 Web n中我們常常需要自定義的模態對話框、覆蓋層也屬於對話框系列,有時候我們還需要考慮如何為對話框提供拖拽支持,或者在對話框顯示和消失之際添加轉場動畫。
n工程化與應用架構
n
前面我們討論了開發某個前端應用所需要的必備技能,而在需要持續交付的團隊項目開發中,我們還需要考慮很多工程實踐相關的方法與技巧。命令式編程到聲明式編程的變化,將更多地功能性工作交於框架處理,而開發人員更加地專註於業務邏輯的實現。
n
工程實踐
n
代碼調試是每個程序員都掌握的技能,不過如何較好地調試代碼以快速定位錯誤所在卻並不是那麼容易。在開發中我們常常需要熱載入、增量編譯等相關技術來避免過長的等待,而單步調試則能夠幫助我們梳理代碼邏輯、循序漸進地發現問題所在。可能n iOS、Android 的開發人員更習慣使用單步調試,而在 Web 或者 Node.js 開發中我們也應適當地多使用 Chrome n等工具進行代碼的單步調試;有時候單步調試也是不錯的瀏覽分析第三方源代碼庫的方式。另一方面,日誌無論在開發環境還是生產環境中都能夠幫我們記錄應用運行狀態等信息。接下來我們還要了解應用開發周期中不同階段使用的單元測試、集成測試以及端到端測試的具體的實現方式,在團隊協同開發中統一代碼風格與約定,能夠利用多種方式對應用進行性能優化,以及在發布到生產環境之後能夠混淆加密、進行應用更新以及應用狀態跟蹤。
n應用架構
n
所謂架構二字,核心即是對於對於富客戶端的代碼組織/職責劃分,從具體的代碼分割的角度,即是功能的模塊化、界面的組件化、應用狀態管理這三個方面。縱覽這十年內的架構模式變遷,大概可以分為 MVn 與 Unidirectional 兩大類,而 Clean Architecture 則是以嚴格的層次劃分獨闢蹊徑。從筆者的認知來看,從 MVCn 到 MVP 的變遷完成了對於 View 與 Model 的解耦合,改進了職責分配與可測試性。而從 MVP 到 MVVM,添加了 View 與 nViewModel 之間的數據綁定,使得 View 完全的無狀態化。最後,整個從 MV 到 Unidirectional n的變遷即是採用了消息隊列式的數據流驅動的架構,並且以 Redux 為代表的方案將原本 MV* n中碎片化的狀態管理變為了統一的狀態管理,保證了狀態的有序性與可回溯性。 實際上從 MVC、MVP 到 MVVM,一直圍繞的核心問題就是如何分割 nViewLogic 與 nView,即如何將負責界面展示的代碼與負責業務邏輯的代碼進行分割。所謂分久必合,合久必分,從筆者自我審視的角度,發現很有趣的一點。Androidn 與iOS中都是從早期的用代碼進行組件添加與布局到專門的 XML/Nib/StoryBoard 文件進行布局,Android 中的 nAnnotation/DataBinding、iOS 中的 IBOutlet 更加地保證了 View 與 ViewLogic n的分割(這一點也是從元素操作到以數據流驅動的變遷,我們不需要再去編寫大量的 findViewById。而Web的趨勢正好有點相反,無論是 nWebComponent 還是 ReactiveComponent 都是將 ViewLogic 與 View 置於一起,特別是 JSX 的語法將n JavaScript 與 HTML 混搭,頗有幾分當年 PHP/JSP 與 HTML 混搭的風味。
n從代碼組織的角度來看,項目的構建工具與依賴管理工具會深刻地影響到代碼組織,這一點在功能的模塊化中尤其顯著。譬如筆者對於 nAndroid/Java 構建工具的使用變遷經歷了從 Eclipse 到 Maven 再到 nGradle,筆者會將不同功能邏輯的代碼封裝到不同的相對獨立的子項目中,這樣就保證了子項目與主項目之間的一定隔離,方便了測試與代碼維護。同樣的,在n Web 開發中從 AMD/CMD 規範到標準的 ES6 模塊與 Webpack n編譯打包,也使得代碼能夠按照功能儘可能地解耦分割與避免冗餘編碼。而另一方面,依賴管理工具也極大地方便我們使用第三方的代碼與發布自定義的依賴項,譬如n Web 中的 NPM 與 Bower,iOS 中的 CocoaPods n都是十分優秀的依賴發布與管理工具,使我們不需要去關心第三方依賴的具體實現細節即能夠透明地引入使用。因此選擇合適的項目構建工具與依賴管理工具也是好的GUI架構模式的重要因素之一。不過從應用程序架構的角度看,無論我們使用怎樣的構建工具,都可以實現或者遵循某種架構模式,筆者認為二者之間也並沒有必然的因果關係。而組件即是應用中用戶交互界面的部分組成,組件可以通過組合封裝成更高級的組件。組件可以被放入層次化的結構中,即可以是其他組件的父組件也可以是其他組件的子組件。根據上述的組件定義,筆者認為像n Activity 或者UIViewController 都不能算是組件,而像 ListView 或者 UITableView n可以看做典型的組件。 我們強調的是界面組件的Composable&Reusable,即可組合性與可重用性。當我們一開始接觸到 nAndroid 或者 iOS 時,因為本身 SDK n的完善度與規範度較高,我們能夠很多使用封裝程度較高的組件;凡事都有雙面性,這種較高程度的封裝與規範統一的 API n方便了我們的開發,但是也限制了我們自定義的能力。同樣的,因為 SDK 的限制,真正意義上可復用/組合的組件也是不多,譬如你不能將兩個 nListView 再組合成一個新的ListView。在 React 中有所謂的 controller-view 的概念,即意味著某個 Reactn 組件同時擔負起 MVC 中 Controller 與 View 的責任,也就是 JSX 這種將負責 ViewLogic 的 nJavaScript 代碼與負責模板的 HTML 混編的方式。 界面的組件化還包括一個重要的點就是路由,譬如 Android 中的 nAndRouter、iOS中的 JLRoutes 都是集中式路由的解決方案,不過集中式路由在 Android 或者 iOS n中並沒有大規模推廣。iOS 中的 StoryBoard 倒是類似於一種集中式路由的方案,不過更偏向於以 UI n設計為核心。筆者認為這一點可能是因為 Android 或者 iOS 本身所有的代碼都是存放於客戶端本身,而 Web n中較傳統的多頁應用方式還需要用戶跳轉頁面重新載入,而後在單頁流行之後即不存在頁面級別的跳轉,因此在 Web 單頁應用中集中式路由較為流行而 nAndroid、iOS n中反而不流行。所謂可變的與不可預測的狀態時軟體開發中的萬惡之源,我們儘可能地希望組件的無狀態性,那麼整個應用中的狀態管理應該盡量地放置在所謂 nHigh-Order Component 或者 Smart Component 中。在 React 以及 Flux n的概念流行之後,Stateless Component 的概念深入人心,不過其實對於 MVVM 中的 View,也是無狀態的 nView。通過雙向數據綁定將界面上的某個元素與 ViewModel 中的變數相關聯,筆者認為很類似於 HOC 模式中的 Container 與 nComponent 之間的關聯。隨著應用的界面與功能的擴展,狀態管理會變得愈發混亂。
推薦閱讀:
※酷安網這樣的靠大量的已付費應用生存的網站將來如何轉型?
※玩遊戲的姿勢該換新的了,為此全球第一網站專開了個實驗室
※Android安全開發之淺談密鑰硬編碼
※okhttp和http 2.0相遇引發的"血案"