如果你正在籌劃新的前端項(xiàng)目或者重構(gòu)現(xiàn)有項(xiàng)目,那么你需要認(rèn)識(shí)到現(xiàn)在的前端開發(fā)環(huán)境已經(jīng)今非昔比,這其中有太多的選擇了:React、Flux、Angular、Aurelia、Mocha、Jasmine、Babel、TypeScript、Flow…… 它們的本意是將開發(fā)簡單化,卻無形中提高了學(xué)習(xí)成本,也給未來項(xiàng)目的維護(hù)帶來了不確定性。
好在這一現(xiàn)象正在退熱,優(yōu)勝劣汰,優(yōu)秀的項(xiàng)目慢慢沉淀下來,開發(fā)方式也越來越清晰。有些開發(fā)者正在嘗試使用基于上述技術(shù)的框架進(jìn)行開發(fā),也在一定程度上減少了學(xué)習(xí)成本。
本文中主要介紹了一些我在 web 應(yīng)用開發(fā)中所涉及和推崇的技術(shù),其中有一些技術(shù)上存在爭議,所以我對(duì)于每一技術(shù)都只做簡單的介紹和分析。所有的這些觀點(diǎn)都是基于我個(gè)人的經(jīng)驗(yàn)和對(duì)社區(qū)的接觸總結(jié)而來的,所以各位還請(qǐng)按需各取所用。
React 可謂風(fēng)頭正盛一時(shí)無兩:
雖然比起 Ember、Aurelia 和 Angular 這些功能豐富的框架,React 不是全能手,但 React 的開發(fā)環(huán)境更加健壯。就目前而言,使用 React 已經(jīng)不是一個(gè)技術(shù)選擇,而是一個(gè)商業(yè)行為,它能提供更高效和更有效的生產(chǎn)力。
當(dāng)你想開發(fā)移動(dòng)應(yīng)用時(shí),因?yàn)橐呀?jīng)學(xué)習(xí)了 React 語法,所以可以直接上手 React Native 開發(fā)跨平臺(tái)應(yīng)用。
現(xiàn)在,我們已經(jīng)具有了開發(fā)視圖層的能力,接下來,我們需要使用其他工具管理應(yīng)用程序中的狀態(tài)和生命周期,在這里推薦的工具就是:Redux。
為了配合 React,F(xiàn)acebook 開發(fā)了管理單向數(shù)據(jù)流的工具 Flux,雖然 Flux 基本上實(shí)現(xiàn)了對(duì)單項(xiàng)數(shù)據(jù)流的支持,但是同時(shí)也帶了其他問題,比如如何保存狀態(tài)、何處發(fā)起 Ajax 請(qǐng)求等等。
為了解決這些問題,又衍生了一系列效仿 Flux 模式的框架:Fluxible、Reflux、Alt、Flummox、Lux、Nuclear、Fluxxor……
目前來說被開發(fā)社區(qū)廣泛支持的一個(gè)實(shí)現(xiàn)就是 Redux。
在 Redux 中,大多數(shù)的組件都是純函數(shù)式的組件,也只有一個(gè)集中的存儲(chǔ)和資源中心。Redux 的實(shí)例方法負(fù)責(zé)整個(gè)數(shù)據(jù)的操作和維護(hù)。相比 Flux 來說,Redux 的思路更加清晰。
更重要的是,Redux 非常易于學(xué)習(xí)。Redux 的作者 Dan Abramov 是一個(gè)優(yōu)秀的教師,他制作了一系列深入淺出的 Redux 視頻教程。通過觀看這些視頻,即可成為一個(gè) Redux 方面的專家。我曾經(jīng)見識(shí)到一個(gè)零基礎(chǔ)的 React 團(tuán)隊(duì)在短短幾周內(nèi)迅速開發(fā)出了測試版產(chǎn)品,且代碼非常穩(wěn)健和老練。
Redux 周邊的生態(tài)系統(tǒng)和 Redux 本身一樣健壯。從神奇的 devtool 到強(qiáng)大的記憶化工具 reselect,Redux 開發(fā)社區(qū)為開發(fā)者提供了應(yīng)有盡有的工具。
開發(fā)者可能會(huì)本能地去嘗試抽象出一個(gè) Redux 模板,這么做有諸多好處,但請(qǐng)?jiān)谡J(rèn)清需求的基礎(chǔ)上來封裝模板,而不要盲目的去嘗試。
是時(shí)候拋棄 CoffeeScript 了,這是因?yàn)樗闹T多特性已在 ES6 中出現(xiàn)類似的語法,而 ES6 是實(shí)施標(biāo)準(zhǔn),代表了 JavaScript 未來的發(fā)展方向。
目前最新的瀏覽器已經(jīng)支持了 ES6 的大部分特性。Babel 是一個(gè)強(qiáng)大的轉(zhuǎn)換工具,用于將 ES6 轉(zhuǎn)換為 ES5。此外,根據(jù)目標(biāo)瀏覽器可以調(diào)整代碼轉(zhuǎn)換的程度。
那么是否有類型系統(tǒng)呢?TypeScript 和 Flow 都為 JavaScript 提供了靜態(tài)類型系統(tǒng),使用靜態(tài)類型檢查,可以有效捕獲錯(cuò)誤,減少測試量。目前來說,我建議對(duì)此持觀望態(tài)度。
TypeScript 在盡力讓 JavaScript 向 C# 或 Java 的方向發(fā)展,但缺少了許多高級(jí)的類型系統(tǒng)特性,比如代數(shù)數(shù)據(jù)類型(algebraic data types)。此外,它不能像 Flow 一樣有效地處理 null。
相比而言,F(xiàn)low 更加強(qiáng)大,捕獲的錯(cuò)誤類型也更多,但難于配置。此外,它對(duì) JavaScript 新特性的支持弱于 Babel,也不支持 Windows 系統(tǒng)。
就我個(gè)人的角度而言,在前端開發(fā)中類型系統(tǒng)并不是至關(guān)重要的一環(huán)(此處可能有爭議)。在類型系統(tǒng)更加健壯且對(duì) Babel 更友好之前,還是讓我們靜觀其變吧。
另一個(gè)無可爭議的工具是 ESLint。ESLint 支持 ES6 語法,還提供了 React 插件,已經(jīng)不單單是一個(gè)代碼審查工具了。目前來說,JSLint 已經(jīng)過時(shí)了,ESLint 可以替代 JSHint 和 JSCS 獨(dú)樹一幟了。
開發(fā)者可以根據(jù)自己的需求配置 ESLint,不過在這里我建議根據(jù) AirBNB 的開發(fā)規(guī)范進(jìn)行配置,也可以直接使用 ESLint airbnb config。當(dāng)然這份規(guī)范中尚有不足之處,但保持團(tuán)隊(duì)整體代碼的一致性,可以有效提高代碼的可讀性。
當(dāng)你熟悉了 ESLint 之后,建議開發(fā)者深入地嘗試其中的規(guī)則。ESLint 捕獲的錯(cuò)誤越多,產(chǎn)品的穩(wěn)定性越高。
忘記 Bower 吧,用 NPM 接管一切。類似 Browserify 和 Webpack 的構(gòu)建工具間接提高了 NPM 在 web 開發(fā)中的地位。使用 NPM,版本管理將會(huì)更加簡單,也將更多地與 Node.js 生態(tài)系統(tǒng)接觸。目前對(duì)于 CSS 的處理尚不足夠完善。
你可能會(huì)考慮如何在部署服務(wù)器上執(zhí)行構(gòu)建呢?與 Ruby 的 Bundler 有所不同,NPM 使用了通配符檢索文件,且第三方包可以在代碼開發(fā)中以及項(xiàng)目發(fā)布前做任意修改。使用 shrinkwrap 文件可以凍結(jié)項(xiàng)目中的第三方依賴,我建議使用 User 的 shrinkwrap,提高輸出的一致性。此外,開發(fā)者也可以考慮使用類似Sinopia 的工具托管自己的私有 NPM 服務(wù)器。
Babel 會(huì)將 ES6 module 語法轉(zhuǎn)換為 CommonJS。CommonJS 是一種歷經(jīng)實(shí)踐的語法,這意味著穩(wěn)定和通用,此外,使用類似 tree shaking (Webpack 2.0 和 Rollup 已經(jīng)支持該特性)的機(jī)制我們還能實(shí)現(xiàn)靜態(tài)代碼分析。
除非你樂意在頁面添加數(shù)百個(gè)腳本標(biāo)簽,否則的話你應(yīng)該嘗試用構(gòu)建工具來打包頁面的資源了。此外,你還需要某些工具讓瀏覽器支持 NPM 第三方包。在這里,我推薦你使用 Webpack。
一年之前對(duì)于上述工作,開發(fā)者還有諸多工具可以選擇,比如基于 JavaScript 的 RequireJS、Browserify 和 Webpack 解決方案,此外還有號(hào)稱能對(duì) ES6 的模塊進(jìn)行最佳優(yōu)化的 RollupJS.
在嘗試了所有的工具之后,我強(qiáng)烈建議開發(fā)者選擇 Webpack:
Webpack 也非常善于處理大型的單頁應(yīng)用,支持代碼分割和惰性加載。
但是值得注意的是,Webpack 的學(xué)習(xí)曲線異常陡峭。不過一旦你學(xué)會(huì)了它,那么你就掌握了最強(qiáng)大的構(gòu)建系統(tǒng)。
那么 Gulp 和 Grunt 呢?相比而言,Webpack 更善于處理各類資源。如果你需要執(zhí)行其他類型的構(gòu)建任務(wù),那么 Gulp 和 Grunt 還是有用的。對(duì)于類似運(yùn)行 Webpack 的基本任務(wù),我建議直接使用 NPM 腳本。
在 JavaScript 中,有大量可選的單元測試工具,每一個(gè)都很穩(wěn)定和健壯。如果你只是用于單元測試,那么現(xiàn)有工具完全可以勝任你的需求。
常見的測試工具有 Jasmine、Mocha、Tape、Ava、Jest 等,它們各有所長。
我對(duì)一個(gè)測試框架的要求有如下幾條:
第一條標(biāo)準(zhǔn)就排除了 Ava 和 Jest。
我喜歡 Chai 斷言是因?yàn)槠浞N類豐富、功能齊全的插件,喜歡 Mocha 是因?yàn)槠鋵?duì)異步的良好支持。強(qiáng)烈建議使用 Dirty Chai 避免某些問題。Webpack 的 mocha-leader 插件允許開發(fā)者自動(dòng)執(zhí)行測試。
對(duì)于 React 而言,開發(fā)者可以參考一下 AirBNB 的 Enzyme 和 Teaspoon。
我非常鐘愛 Mocha 的特性,如果你想要的只是最基礎(chǔ)的功能,可以參考這篇文章了解一下 Tape。
JavaScript 并沒有一個(gè)類似 Java 或 .NET 的核心工具庫,所以開發(fā)者大都會(huì)從外部引用一個(gè)外部工具庫。
目前來說,Lodash 是此類工具中的佼佼者。此外,由于它惰性執(zhí)行的特性,也讓它是目前性能最佳的工具之一。使用 Lodash 時(shí)無需引用全部資源,開發(fā)者可以按需使用其中的函數(shù)。在 4.x 版本中,Lodash 為偏愛函數(shù)式編程的開發(fā)者提供了一個(gè)“函數(shù)式開發(fā)”模式。
如果你熟悉函數(shù)式編程,你可以了解一下 Ramda。如果你決定使用這個(gè)庫,可能需要引用一些 Lodash 函數(shù)。
許多基于 React 的應(yīng)用程序都不再使用 jQuery 了。除非你正在維護(hù)一個(gè)陳舊的項(xiàng)目或者用到的第三方庫依賴了 jQuery,否則已經(jīng)沒有必要使用它了。
我喜歡讓項(xiàng)目保持簡潔,在代碼中只使用 fetch 。fetch 基于 promise,F(xiàn)irefox 和 Chrome 都封裝了該接口。對(duì)于其他瀏覽器,則需要提供一個(gè)膩?zhàn)幽_本。我建議使用 isomorphic-fetch 在各個(gè)瀏覽器和服務(wù)端保持功能的一致性。
當(dāng)然也可以其他優(yōu)秀的第三方庫異步獲取數(shù)據(jù),但我覺得 fetch 已經(jīng)夠用了。
同構(gòu) JavaScript 是指同時(shí)運(yùn)行在客戶端和服務(wù)端的 JavaScript,常用于在服務(wù)端預(yù)先渲染頁面,提高性能,便于 SEO。使用 React 可以實(shí)現(xiàn)同構(gòu) JavaScript,但是并不簡單,它提高了程序的復(fù)雜度,限制了開發(fā)者可選的工具和第三方庫。
如果你正在構(gòu)建一個(gè) B2C 的站點(diǎn),比如電商網(wǎng)站,那么你可能就需要使用同構(gòu) JavaScript。不過,對(duì)于內(nèi)部站點(diǎn)或者 B2B 程序,性能就不是最重要的了,則同構(gòu) JavaScript 也就不是太重要了。
最近每個(gè)人好像都在思考如何處理 API。每個(gè)人都在隨波逐流的使用 RESTfull API,SOAP 已經(jīng)成為了過去時(shí)。目前業(yè)界存在各種 API 協(xié)議,比如 HATEOAS、JSON API、HAL、GraphQL 等。
GraphQL 賦予了客戶端進(jìn)行任意查詢的能力。搭配 Relay,可以更好地處理客戶端的狀態(tài)和緩存。不過,創(chuàng)建 GraphQL 的服務(wù)端接口的難度還較大,且大多數(shù)的文檔都是面向 Node.js 的。
Netflix 的 Falcor 看起來提供了和 GraphQL/Relay 相似的能力,同時(shí)還降低了服務(wù)端的需求,但它目前尚處于開發(fā)者預(yù)覽狀態(tài),尚未應(yīng)用于實(shí)際開發(fā)。
所有已知的規(guī)范都各有缺陷,有些過于復(fù)雜,有些只能處理數(shù)據(jù)讀取而不嗯那個(gè)更新,有些和 REST 差異顯著。許多開發(fā)者選擇自己開發(fā),但是還會(huì)遇到上述的問題。
我不認(rèn)為上述有一個(gè)完美的解決方案,但我對(duì) API 有一個(gè)自己的認(rèn)知:
到目前為止,我還沒有發(fā)現(xiàn)滿足上述所有條件的解決方案。
如果你正在使用 RESFful,建議參考 Swagger 來編寫 API。
Electron 可以使用前端技術(shù)構(gòu)建桌面程序,GitHub 團(tuán)隊(duì)出品的 Atom 編輯器就是基于 Electron 創(chuàng)建的。本質(zhì)上,Electron 內(nèi)部封裝了一個(gè) Node.js,可以打開 Chrome 窗口渲染 UI,還可以訪問操作系統(tǒng)本地的 API,并且沒有瀏覽器中的沙盒機(jī)制。開發(fā)者可以通過 Electron 打包和分發(fā)應(yīng)用程序。
這是創(chuàng)建跨平臺(tái)軟件最簡單的方式,而且還可以利用上述的所有工具。此外,Electron 有完整的文檔和活躍的開發(fā)社區(qū)。
你可能聽說過 nw.js 的大名,雖然它已經(jīng)存在了多年,但相比來說,Electron 更加穩(wěn)定和易用。
這里有一個(gè)基于 Electron、React 和 hot reload 的模板,嘗試一下吧。
下面是一些我在 Twitter 上關(guān)注的對(duì)象:
建議閱讀 Pate Hunt 的 Learning React!
Dan Abramov 發(fā)布一系列的視頻教程 Getting started with Redux,強(qiáng)烈推薦!此外,Dan 還發(fā)布過一個(gè)關(guān)注列表,比上述更加詳細(xì)。
Mark Erikson 的 React/Redux links 集合也是很好的學(xué)習(xí)材料。
JavaScript 的生態(tài)環(huán)境發(fā)展迅速,正日益強(qiáng)大起來。React 的最佳實(shí)踐正在固化,周邊工具的職責(zé)和能力也日益清晰。
最重要的事情就是要牢記:保持簡潔,按需使用。
如果你的應(yīng)用程序只有兩三屏,那么就無需使用路由系統(tǒng);如果你正在創(chuàng)建一個(gè)單頁應(yīng)用,那么甚至不需要 Redux,只需要 React 自己的 state 屬性即可;如果你正在創(chuàng)建一個(gè)簡單的 CRUD 程序,那么你就不需要使用 Relay;如果你正在學(xué)習(xí) ES6,并不需要深入地了解 Async/Await 或裝飾器;如果你剛剛開始學(xué)習(xí) React,并不需要使用熱重載和服務(wù)端渲染;如果你剛剛接觸 Webpack,你就不需要分離代碼和合并多個(gè)資源;如果你剛剛學(xué)習(xí) Redux,你不需要理解使用 Redux-Form 和 Redux-Sagas。
保持簡潔,每次只做一件事!
聯(lián)系客服