技術乾貨 | Thinking in FE 更現代的 Web 開發

翁陽(滬江開發工程師)
本文爲原創翻譯,如需轉載請標明出處,不當之處敬請指出javascript

前端,是一個常常會被小覷的技術領域,在大多不明因此的人眼裏,前端不過是排排版、布布局,甚至是一些前端的新手也會這樣認爲(這裏的前端並不特指 Web 前端,移動端也可歸結爲前端)。那麼前端真的就如此無趣且一成不變麼?css

之因此本系列取名爲 Thinking in FE,是由於 Thinking 讓人沉靜、不浮躁,就該用這種心態來面對前端。做爲本系列的第一篇,我以爲是頗有必要把 Web 前端拿出來講說,這幾年 Web 前端變革得太快,若是你仍是覺得吃透了 float 就吃透了整個佈局,搞定了 css + div 就能縱橫 Web FE 的話,本篇就是爲你而準備的。html

開發模式的變革

前幾年,當我還奮戰在 Web 前端開發的第一線時,那時候的項目開發模式是簡單但容易出問題的。當時後端主要使用的技術是微軟的 WebAPI,前端的 IDE 天然就被 Visual Studio 包攬了(固然那時 WebStorm、PhpStorm 也都在不一樣項目中承擔着 IDE 的角色)。IDE 倒不是什麼問題,當時主要的問題在於前端第三方庫依賴的管理,基本上都是手動引用,長時間後會連一些庫的具體版本都忘記了。這在多人協做開發時很容易出問題,也不利於項目的持續和快速發展,長此以往一個項目會變得陳舊、死氣沉沉。前端

如今的開發模式,悄然變得更輕卻又更重了。更輕的是 IDE,咱們開始傾向於使用像 Sublime、Atom、VSCode 這種輕量級的文本編輯器,再配合一些平常須要的小插件;而更重的是項目的依賴管理和構建方式,依賴管理已經被 NodeJS 的包管理工具npm包攬了,基本上咱們須要什麼樣的庫,只要簡單的npm install一下就能夠了,而構建工具不少,好比 Webpack、Gulp、Grunt、Yeoman 等,但也慢慢的有被 Webpack 一統江湖的趨勢。java

有了依賴管理、構建工具,而且能夠經過npm配合其餘工具來執行單元測試,咱們即可以很容易的將項目進行持續集成。這纔是更加現代化的開發模式,而整個 Web 前端的生態也趨向完整了。react

百花齊放的開發語言

做爲一個站在時代前沿的 Web 前端開發者,可能會是全部開發工種中接觸開發語言最多的一個,至少你須要掌握三門語言:html、css、javascript,這是最終的宿主,也就是瀏覽器所原生支持的三種語言,分別用於:結構、樣式、交互。但若是你真的只會這三種語言,那你確定算不上一個合格的 Web 前端開發者,隨着廣大先驅者的智慧凝結,這三種基礎的語言衍化出了不少獨立的語言,而這些衍化的產物已經愈來愈被現代化的 Web 前端所普遍使用。webpack

從 html 所衍化出來的是各類模板語言,好比 backbone、angular 所提供的。模板的做用是將結構高度抽象,從而避免不少沒必要要的重複工做,而且使得前端頁面更加動態化。html 自己是靜態的描述語言,有了模板的支持,咱們能夠像下面這樣來讓其動態化:git

clipboard.png

從 css 所衍化出來的,即是和樣式相關的語言了,與 html 語言同樣,css 也是一種靜態的描述語言,自己不支持變量和條件分支。做爲對 css 的擴充,市面上出現了像 less、sass 這樣一些語言,它們使得樣式的描述更加結構化,而且能夠經過變量很方面的來修改和維護,這對須要提供樣式定製化的第三方組件而言仍是很是有用的。下面是 sass 的變量和嵌套示例:es6

clipboard.png

最後從 javascript 中衍化出來的,即是不少對 javascript 特性進行補充的語言了。javascript 自己是基於原型的語言,自身也有一些設計上的缺陷,最多見的即是變量的做用域問題,也就是所謂的變量提高問題。不過在 ES6 出來後,javascript 獲得了質的提高,而在這以前,出現了 javascript 的替代語言,以 typescript 和 coffeescript 最爲經常使用,而且如今還被普遍使用着。不管是 typescript 仍是 coffeescript,它們都是對 javascript 的補充,而 coffeescript 更像是一門新的語言。它們使得 javascript 更加的面向對象,並引入了更多函數式語言的特性,讓書寫更加優雅、溫馨,下面一段 coffeescript(摘自Coffee-Script中文網),你們感覺下:github

clipboard.png

自 ES6 出來並受到不少工具的支持後,已經更加推薦直接使用 ES6 來編寫項目了,ES6 彌補了 javascript 以前一直缺少的原生模塊化支持(這裏說的是原生,排除 CommonJS、AMD、CMD 規範的第三方實現),對面向對象也有了更好的支持,而且明確了變量、做用域,也引入了不少函數式編程的概念。最重要的是在2013年 ES6 標準就已經肯定了,對於新的提案 TC39 只會往 ES7 歸入,因此在項目中使用不會面臨像使用 Swift 同樣不斷變動的窘境。

上面說過了,瀏覽器原生只支持最基本的那三種語言,那麼若是想使用這些衍化出來的語言或者是如今還不被很好支持的ES六、ES7,咱們須要相應的轉換工具。而這些轉換操做均可以很是簡單的使用 webpack 對應的 loader 來完成。不得不說 webpack 已經成爲了 Web 前端構建的一站式工具,經過組合不一樣的 loader,咱們能夠完成轉換->合併->壓縮->打包等一系列中間過程。

React 的顛覆

若是要論這幾年來,對 Web 前端思想產生顛覆性的框架,那應該是非 React 莫屬了。Facebook 在2013年開源了這個框架,由此引起了一系列的變革。React 的核心思想是組件化,化整爲零,分而治之。而 React 出現的緣由,也正是由於 Facebook 對當時市面上全部的前端框架都不滿意,既然不滿意,他們就立馬本身作了一個。

在 React 出來以前,市面上使用較多的都是一些MV*系列的框架,比較有表明性的應該算是谷歌的 Angular 了。但這類框架的學習曲線仍是比較高的,最重要的是,對於通常人而言它們所表述的意圖不夠直觀。從視圖到模型,雖然力求低耦合,但仍是不得不進行約定、依賴,由於最終視圖和模型須要綁定,那不管如何解耦都不可能作到乾淨利落,約定只會徒增維護的複雜度。

對此,React 提出了組件的概念(固然這個概念在其它領域早就有過),一個組件就是一個高內聚的封裝。對外部而言組件的輸入是屬性(props),輸出是最終的視圖,屬性是恆定的,也就是說外部輸入以後,就不會被改變了。而讓組件改變的是狀態(state),對於 React 而言,狀態是由組件內部進行維護的,這種思想讓組件變得更加內聚、可控。下面是一個很是簡單的 React 組件:

clipboard.png

上面這個組件擁有一個hidden的狀態,而render方法中的內容也是讓人一目瞭然(JSX語法讓組件更加內聚)。經過界面交互或其它一些手段咱們能夠改變hidden的值,而這會實時體現到render方法中。React 本身維護了一套虛擬 DOM,通常狀況下咱們沒必要刻意考慮渲染性能問題,但若是你想本身控制是否重繪的話,React 的組件也給你提供了這樣的控制能力。

React 的組件除了內聚以外,還能夠進行組合,一個組件能夠嵌入其它多個組件。這使得咱們在進行實際開發以前,須要對即將完成的內容進行組件劃分,在通用和簡單兩方面來做權衡。也就是說,React 的思想已經顛覆了咱們思考問題的方式,而它給咱們帶來的收穫是組件的不斷積累,以及開發速度和可維護性的提升。

單項數據流 Flux

在絕大數MV*系列架構的框架中,視圖 DOM 和視圖模型之間是進行雙向綁定的,這種強綁定的情形在不少複雜的場景下會帶來讓人沒法維護的問題。當這樣的狀況愈來愈廣泛時,Facebook 提出了單向數據流的概念,並把這種思想稱之爲Flux,且推出了官方實現flux。不得不說 Facebook 是個了不得的公司,也不得不說 Web 前端一直是這些新思惟的探路者。不過flux很快就被另外一個開源項目慢慢取代了,也就是社區中很是火爆的redux,但思想仍是一致的。

這裏有必要解釋一下單向的概念,整個 Flux 的數據流以下:

clipboard.png

1.用戶觸發 View 的某個操做,View 向 Dispatcher 發出一個 Action
2.Dispatcher 收到 Action 後,對 Store 進行更新
3.Store 更新後,發出事件通知 View
4.View 收到事件後,進行頁面更新

這裏整個數據流都是單向流動的(概念抽象中沒有雙向箭頭),全部狀態都維護在 Store 之中,這讓咱們對狀態變動進行追蹤變得很是簡單。在redux的實現中,從 Dispatcher 到 Store 之間,咱們還能夠安裝不少自定義的中間件,來進行一些切面處理,好比日誌、受權、統計等。

Facebook 的 React 僅僅是提供了組件化的構建方案,而對於組件所構成的模塊並無提供更多架構上的支持,這點基於 Flux 思想的redux恰好能夠對其進行補充。在解釋如何讓它們銜接以前,咱們有必要先看點其它內容。

異步任務編織

全部的項目開發中,爲了追求更好的用戶體驗,咱們不可避免的要面對異步問題。同步操做下,咱們對流程管理和安排很是簡單清晰,相比之下,異步就沒有那麼容易去維護了。

而在 Web 前端,很長一段時間裏,ajax 幾乎就成了異步的代名詞,由於在實際開發中,80%以上的異步都來自於異步網絡請求。時至今日,我以爲須要從新定義下異步在 Web 前端中的定位了,特別是在使用了redux以後。從最初的action派發,到最終的狀態變動,以及狀態變動後引起的視圖渲染,這一系列的步驟,咱們都應該將其視爲異步(參考上文 Flux 的圖片)。

衆所周知,javascript 處於一個單線程的運行環境中,但異步的引入使得咱們也須要面臨一些多線程下才存在的問題。而且咱們從新定義了併發的概念,在 javascript 中,併發指的是一個異步任務還沒有完成,同時又產生了其它異步任務。好比,咱們同時發出了兩個 ajax 請求,那麼咱們就必需要面對這兩個請求返回時間、順序不肯定性的結果。

在 ES6 的語言標準中,引入了Promise概念,能夠方便咱們對異步任務進行鏈式編排,而且能夠統一進行錯誤處理,下面是一個簡單的例子:

clipboard.png

雖然這種鏈式調用從某種程度上讓代碼更加清晰,但在對異步返回數據須要進行條件分支判斷,或者一些更加複雜的邏輯操做時,Promise也就顯得有些力不從心了。在 ES6 中還引入了另一個概念,叫Generator,與之對應的關鍵字是yield,Generator的特性是函數內部維護了上一次執行到的位置,而在外部調用next()控制它進一步執行(關於這方面更多的知識,請參考相關ES6手冊)。其實這點無疑是走了微軟 C# 的老路,而且在 ES7 中引入的async和await也是與 C# 同出一轍,在 C# 推出yield關鍵字後,社區也有達人以此實現了一套異步任務編織的框架,那麼 Web 前端天然也不例外了。

這裏不得不說一下redux的一箇中間件redux-saga,它是徹底基於Generator特性實現的一套異步任務編織框架,而且很是強大。一個saga對應一個Generator函數,而且saga分爲兩種:

1.watcher saga: 負責監控 redux 的 action,而且對任務進行具體編排
2.worker saga: 處理由watcher saga編排的具體任務

若是想要了解更多關於redux-saga的內容,仍是建議去翻閱下官方文檔,這裏給出一個簡單的示例,一睹它的威力(摘自saga文檔):

clipboard.png

上面的示例中有兩個saga,其中loginFlow爲 watcher 而authorize爲 worker。在loginFlow中,當咱們收到LOGIN_REQUEST的action時,取出其中的user和password狀態,非阻塞的去調用authorize,而且開始監控LOGOUT和LOGIN_ERROR兩個action,當收到的action爲LOGOUT,此時前一個LOGIN_REQUEST可能並未執行完成,因此咱們須要取消它,在這一切完成後,咱們調用clearItem來清空本地存儲的token,再次迴歸到監控LOGIN_REQUEST。而authorize這個saga中的流程也相對簡單明瞭,這裏就不做更多的闡述了。

經過saga的實現,能夠與Promise進行對比,不難發現它更加的同步化,全部的代碼徹底看不出異步的影子,因此在進行復雜的異步任務編排和分支控制時,會很是的簡潔明瞭。

項目的最佳實踐

上面說到了項目構建工具、各類新興的開發語言、React、Redux 以及 Redux-saga,那麼在一個實際的項目中,咱們如何將它們融合起來,進行更加現代化的 Web 開發呢?其實很簡單,咱們能夠經過npm來進行項目包依賴管理,而且經過webpack來將它們所有串聯起來。

對於webpack,咱們須要安裝一系列的loader而且在webpack.config.js中進行配置,大概會用到下面這些loader:

  • babel-loader:用於轉換 ES六、ES七、JSX 語法

  • file-loader:用於簡單的文件拷貝

  • css-loader:用戶 CSS 的壓縮,打包

  • less-loader:用戶 LESS 的轉換

  • url-loader:用戶圖片資源的轉換、打包

  • html-loader:用戶 HTML 文件的連接替換、打包

  • html-minify-loader:用於 HTML 文件的壓縮

具體的配置須要根據項目自己而定,詳細的細節能夠參考webpack的官方文檔。當咱們把webpack這些loader配置完畢後,能夠按照一些咱們須要的框架和中間件了,大致應該以下:

  • react:react 組件化的核心庫

  • react-dom:react 提供的一些 DOM 操做輔助方法庫,用於在 DOM 上渲染 react 組件

  • react-router:使用 react 開發單頁應用時,這個庫是必須的,提供了基於 react 組件化思路的路由解決方案

  • redux:redux 庫,上文中已經有所解釋,這裏就很少說了

  • redux-actions:方便在 redux 中 action 和對應的state進行管理

  • redux-saga:redux 的中間件,用於異步任務編織

  • react-router-redux:redux 的中間件,用於同步 redux 中的路由狀態,能夠經過 - redux 的 ation 來控制路由

  • reselect:在較大型項目中使用,用於react和redux鏈接時connect中map參數的管理,能夠有效減小狀態變動,減小組件渲染的次數

有了上面的這些組件,咱們基本上能夠愉快的進行項目開發了,那麼在整個項目的流程中,它們是如何進行協做的呢?能夠參考下圖:

clipboard.png

能夠看到,貫穿其中最多的即是action和state,而redux顯然是整個項目鏈接起來的樞紐,saga則是用於封裝了全部的異步網絡請求(封裝成更加業務化的任務)。這裏舉一條比較常見的流程:用戶從界面上點擊了某個按鈕,而後發送了網絡請求,以及請求響應後對界面的更新:

1.首先從 react 組件的 View 上派發了一個 action 到 Redux
2.Redux 的中間件 Saga 會監控這個 action 並做出網絡請求
3.響應回來後,Saga 會經過 put 將響應的 action 派發到 Redux
4.Redux 接收到這個 action 對相應狀態做出變動
5.react 鏈接到(connect)這個狀態的相應組件會收到狀態變動,從新渲染 View

彷佛看起來比較複雜,但在瞭解透徹相應組件的職責後,其實並沒那麼複雜。爲了讓開發過程當中調試更加快捷,咱們還能夠安裝一些開發中須要用到的工具模塊。這裏推薦使用 dora,配合dora-plugin-proxy咱們能夠在開發過程當中模擬後端響應數據,再配合dora-plugin-webpack-hmr能夠實現開發過程當中,模塊的熱加載,讓咱們無需不斷刷新瀏覽器就能看到最新的界面效果,固然選擇還有不少,dora 只是我以爲比較好用的一款。

Thinking

本篇帶着你們蜻蜓點水的將現代化的 Web 前端看了個大概,這其中更多的是在闡述思想和理念,隨着時代的發展,Web 前端並非大多數人們眼中的「作作網頁而已」,它的理念走在了時代的前言,它的生態也比不少其它方向豐富、健全。

不小看、自覺得是,要永遠抱着敬畏的態度,這是成長的基礎,也是咱們做爲技術人員該有的素質。

參考

iKcamp原創新書《移動Web前端高效開發實戰》已在亞馬遜、京東、噹噹開售。

相關文章
相關標籤/搜索