JavaScript Web應(yīng)用開發(fā)(上篇)_第1頁
JavaScript Web應(yīng)用開發(fā)(上篇)_第2頁
JavaScript Web應(yīng)用開發(fā)(上篇)_第3頁
JavaScript Web應(yīng)用開發(fā)(上篇)_第4頁
JavaScript Web應(yīng)用開發(fā)(上篇)_第5頁
已閱讀5頁,還剩165頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

JavaScriptWeb應(yīng)用開發(fā)(上篇)目錄\h第一部分構(gòu)建過程\h第1章構(gòu)建優(yōu)先\h1.1問題出現(xiàn)了\h1.1.145分鐘內(nèi)每秒損失17萬美元\h1.1.2構(gòu)建優(yōu)先\h1.1.3繁瑣的前戲\h1.2遵守構(gòu)建優(yōu)先原則,提前計(jì)劃\h構(gòu)建優(yōu)先原則的核心法則\h1.3構(gòu)建過程\h1.4處理應(yīng)用的復(fù)雜度和設(shè)計(jì)理念\h1.5鉆研構(gòu)建優(yōu)先原則\h1.5.1檢查代碼質(zhì)量\h1.5.2在命令行中使用lint工具\(yùn)h1.6總結(jié)\h第2章編寫構(gòu)建任務(wù),制定流程\h2.1介紹Grunt\h2.1.1安裝Grunt\h2.1.2設(shè)置第一個(gè)Grunt任務(wù)\h2.1.3使用Grunt管理構(gòu)建過程\h2.2預(yù)處理和靜態(tài)資源優(yōu)化\h2.2.1詳述預(yù)處理\h2.2.2處理LESS\h2.2.3打包靜態(tài)資源\h2.2.4簡化靜態(tài)資源\h2.2.5創(chuàng)建子圖集\h2.3檢查代碼完整性\h2.3.1清理工作目錄\h2.3.2使用lint程序檢查代碼\h2.3.3自動運(yùn)行單元測試\h2.4首次自己編寫構(gòu)建任務(wù)\h2.5案例分析:數(shù)據(jù)庫任務(wù)\h2.6總結(jié)\h第3章精通環(huán)境配置和開發(fā)流程\h3.1應(yīng)用的環(huán)境\h3.1.1配置構(gòu)建模式\h3.1.2環(huán)境層面的配置\h3.1.3開發(fā)環(huán)境有什么特別之處\h3.2配置環(huán)境\h3.2.1瀑布式存儲配置的方法\h3.2.2通過加密增強(qiáng)環(huán)境配置的安全性\h3.2.3使用系統(tǒng)級方式設(shè)置環(huán)境層面的配置\h3.2.4在代碼中使用瀑布式方法合并配置\h3.3自動執(zhí)行繁瑣的首次設(shè)置任務(wù)\h3.4在持續(xù)開發(fā)環(huán)境中工作\h3.4.1監(jiān)視變動,爭分奪秒\h3.4.2監(jiān)視Node應(yīng)用的變動\h3.4.3選擇一款合適的文本編輯器\h3.4.4手動刷新瀏覽器已經(jīng)過時(shí)了\h3.5總結(jié)\h第4章發(fā)布、部署和監(jiān)控\h4.1發(fā)布應(yīng)用\h4.1.1優(yōu)化圖像\h4.1.2緩存靜態(tài)資源\h4.1.3內(nèi)嵌對首屏至關(guān)重要的CSS\h4.1.4部署前要測試\h4.2預(yù)部署操作\h4.2.1語義化版本\h4.2.2使用更改日志\h4.2.3提升版本號時(shí)提交更改日志\h4.3部署到Heroku\h4.3.1在Heroku的服務(wù)器中構(gòu)建\h4.3.2管理多個(gè)環(huán)境\h4.4持續(xù)集成\h4.4.1使用Travis托管的CI\h4.4.2持續(xù)部署\h4.5監(jiān)控和診斷\h4.5.1日志和通知\h4.5.2調(diào)試Node應(yīng)用\h4.5.3分析性能\h4.5.4運(yùn)行時(shí)間和進(jìn)程管理\h4.6總結(jié)\h第二部分管理復(fù)雜度\h第5章理解模塊化和依賴管理\h5.1封裝代碼\h5.1.1理解單一職責(zé)原則\h5.1.2信息隱藏和接口\h5.1.3作用域和this關(guān)鍵字\h5.1.4嚴(yán)格模式\h5.1.5提升變量的作用域\h5.2JavaScript模塊\h5.2.1閉包和模塊模式\h5.2.2原型的模塊化\h5.2.3CommonJS模塊\h5.3管理依賴\h5.3.1依賴圖\h5.3.2介紹RequireJS\h5.3.3Browserify:在瀏覽器中使用CJS模塊\h5.3.4Angular管理依賴的方式\h5.4理解包管理\h5.4.1Bower簡介\h5.4.2大型庫,小組件\h5.4.3選擇合適的模塊系統(tǒng)\h5.4.4學(xué)習(xí)循環(huán)依賴\h5.5ECMAScript6新功能簡介\h5.5.1在Grunt任務(wù)中使用Traceur\h5.5.2Harmony中的模塊\h5.5.3創(chuàng)建塊級作用域的let關(guān)鍵字\h5.6總結(jié)\h第6章理解JavaScript中的異步流程控制方法\h6.1使用回調(diào)\h6.1.1跳出回調(diào)之坑\h6.1.2解開混亂的回調(diào)\h6.1.3嵌套請求\h6.1.4處理異步流程中的錯(cuò)誤\h6.2使用async庫\h6.2.1使用瀑布式、串行還是并行\(zhòng)h6.2.2異步函數(shù)式任務(wù)\h6.2.3異步任務(wù)隊(duì)列\(zhòng)h6.2.4制定流程和動態(tài)流程\h6.3使用Promise對象\h6.3.1Promise對象基礎(chǔ)知識\h6.3.2鏈接Promise對象\h6.3.3控制流程\h6.3.4處理被拒絕的Promise對象\h6.4理解事件\h6.4.1事件和DOM\h6.4.2自己實(shí)現(xiàn)事件發(fā)射器\h6.5展望:ES6生成器\h6.5.1創(chuàng)建第一個(gè)生成器\h6.5.2生成器的異步性\h6.6總結(jié)

第一部分構(gòu)建過程本書第一部分專門介紹構(gòu)建過程,還會通過實(shí)例介紹Grunt。這一部分既有理論也有實(shí)踐,目的是告訴你什么是構(gòu)建過程,為什么以及如何使用構(gòu)建過程。第1章說明構(gòu)建優(yōu)先原則包含的兩層意思:構(gòu)建過程和應(yīng)用復(fù)雜度管理。然后開始編寫第一個(gè)構(gòu)建任務(wù):使用lint程序檢查代碼,避免有句法錯(cuò)誤。第2章專門介紹構(gòu)建任務(wù)。你會了解組成一次構(gòu)建的各項(xiàng)任務(wù),如何配置任務(wù),以及如何自己編寫任務(wù)。針對每種情況,我們都會先講理論,然后再使用Grunt編寫實(shí)例。第3章介紹如何配置應(yīng)用的環(huán)境,而且要安全存儲敏感信息。我們會說明搭建開發(fā)環(huán)境的流程,以及如何自動完成這些構(gòu)建步驟。第4章再介紹一些需要在發(fā)布應(yīng)用時(shí)執(zhí)行的任務(wù),例如優(yōu)化靜態(tài)資源和管理文檔。你會學(xué)到如何使用持續(xù)集成服務(wù)檢查代碼的質(zhì)量。我們還會把應(yīng)用部署到線上環(huán)境,讓你實(shí)際體驗(yàn)一把。

第1章構(gòu)建優(yōu)先本章內(nèi)容現(xiàn)代應(yīng)用設(shè)計(jì)面臨的問題什么是構(gòu)建優(yōu)先原則構(gòu)建過程管理應(yīng)用中的復(fù)雜度使用正確的方式開發(fā)應(yīng)用可能很難,我們要合理規(guī)劃。我曾只用一個(gè)周末就開發(fā)出了應(yīng)用,但應(yīng)用設(shè)計(jì)得可能并不好。創(chuàng)建隨時(shí)會扔掉的原型可以即興發(fā)揮,但是開發(fā)一個(gè)可維護(hù)的應(yīng)用則需要規(guī)劃,要知道怎么把腦海中設(shè)想的功能組織在一起,甚至還要考慮到不久之后可能會添加的功能。我曾付出無數(shù)努力,但應(yīng)用的前端還是差強(qiáng)人意。后來我發(fā)現(xiàn),后端服務(wù)通常都有專門的架構(gòu),專門用于規(guī)劃、設(shè)計(jì)和概覽這些服務(wù),而且往往還不止一個(gè)架構(gòu),而是一整套。可是前端開發(fā)的情況卻完全不同,前端開發(fā)者會先開發(fā)出一個(gè)可以運(yùn)行的應(yīng)用原型,然后運(yùn)行這個(gè)原型,希望在生產(chǎn)環(huán)境中依然正常。前端開發(fā)同樣需要規(guī)劃架構(gòu),像后端開發(fā)一樣去設(shè)計(jì)應(yīng)用。以前,我們會從網(wǎng)上復(fù)制一些代碼片段,然后粘貼到頁面中,就這樣收工了??墒沁@樣的日子早已過去,先把JavaScript代碼攪和在一起,事后再做修改,不符合現(xiàn)代標(biāo)準(zhǔn)了。如今,JavaScript是開發(fā)的焦點(diǎn),有很多框架和庫可以選擇,這些框架和庫能幫助我們組織代碼,我們不會再編寫一整個(gè)龐大的應(yīng)用了,更多的是編寫小型組件??删S護(hù)性不是隨意就能實(shí)現(xiàn)的,我們從開發(fā)應(yīng)用伊始就要考慮可維護(hù)性,并在這個(gè)原則的指導(dǎo)下設(shè)計(jì)應(yīng)用。設(shè)計(jì)應(yīng)用時(shí)如果不考慮可維護(hù)性,隨著功能的不斷增加,應(yīng)用就會像疊疊樂搭出的積木塔一樣慢慢傾斜。如果不考慮可維護(hù)性,最后根本無法再往這個(gè)塔上放任何積木。應(yīng)用的代碼會變得錯(cuò)綜復(fù)雜,缺陷越來越難追查。重構(gòu)就要中斷產(chǎn)品開發(fā),業(yè)務(wù)可經(jīng)不起這樣折騰。而且還要保持原有的發(fā)布周期,根本不能讓積木塔倒下,所以我們只能妥協(xié)。1.1問題出現(xiàn)了你可能想把一個(gè)新功能部署到生產(chǎn)環(huán)境,而且想自己動手部署。你要用多少步完成這次部署?八步還是五步?為什么你要在部署這樣的日常工作中冒險(xiǎn)呢?部署應(yīng)該和在本地開發(fā)應(yīng)用一樣,只需一步就行??上聦?shí)并非如此。我以前會手動執(zhí)行部署過程中的很多步驟,你是不是也是這樣?當(dāng)然,你一步就能編譯好應(yīng)用,或者可能會使用服務(wù)器端解釋型語言,根本不用事先編譯。如果以后需要把數(shù)據(jù)庫更新到最新版本,你甚至可能會編寫一個(gè)腳本執(zhí)行升級操作,但還是要登入數(shù)據(jù)庫服務(wù)器,上傳這個(gè)腳本文件,然后自己動手更新數(shù)據(jù)庫模式。做得不錯(cuò),數(shù)據(jù)庫已經(jīng)更新了,可是有地方出錯(cuò)了,應(yīng)用拋出了錯(cuò)誤。你看了下時(shí)間,應(yīng)用已經(jīng)下線超過10分鐘了。這只是一次簡單的升級啊,怎么會出錯(cuò)呢?你查看日志,發(fā)現(xiàn)原來是忘記把新變量添加到配置文件里了,真是太傻了。你立即加上了新變量,抱怨著這次與代碼基的斗爭。你忘記在部署前修改配置文件,在部署到生產(chǎn)環(huán)境前忘了更新配置。這種情況是不是聽起來很熟悉?不要害怕,這種情況很常見,在很多應(yīng)用中都存在。我們來看看下面這個(gè)危險(xiǎn)的案例。1.1.145分鐘內(nèi)每秒損失17萬美元我敢肯定,一個(gè)嚴(yán)重問題導(dǎo)致?lián)p失幾乎五億美元的案例會讓你打起精神。在騎士資本公司就發(fā)生過這樣的事。1他們開發(fā)了一個(gè)新功能,讓股票交易員參與一個(gè)叫“零售流動性計(jì)劃”(RetailLiquidityProgram,簡稱RLP)的項(xiàng)目中。RLP的目的是取代已經(jīng)停用九年的“權(quán)力限定”(PowerPeg,簡稱PP)功能。RLP的代碼中重用了一個(gè)用來激活PP功能的標(biāo)志,添加RLP時(shí),他們把PP移除了,所以一切都正常運(yùn)行著,至少他們認(rèn)為是正常的。但是,當(dāng)他們打開這個(gè)標(biāo)志時(shí),問題出現(xiàn)了。1關(guān)于騎士資本公司這次事件的詳情,請?jiān)L問\hhttp://bevacqua.io/bf/knight。他們在部署時(shí)沒有采用正式的過程,而且只由一個(gè)技術(shù)人員手動執(zhí)行。這個(gè)人忘記把代碼改動部署到八個(gè)服務(wù)器中的某一個(gè),因此,在這個(gè)服務(wù)器中,這個(gè)標(biāo)志控制的是PP功能,而不是RLP功能。直到一星期后他們打開這個(gè)標(biāo)志時(shí)才發(fā)現(xiàn)問題:他們在七個(gè)服務(wù)器中激活了RLP,卻在最后一個(gè)服務(wù)器上激活了停用九年的PP功能。這臺服務(wù)器上處理的訂單觸發(fā)執(zhí)行的是PP代碼,而不是RLP。這樣一來,發(fā)送到交易中心的訂單類型是錯(cuò)誤的。他們試圖補(bǔ)救,但情況進(jìn)一步惡化了,因?yàn)樗麄儚囊呀?jīng)部署了RLP的服務(wù)器中把RLP刪除了。長話短說,他們在不到一小時(shí)的時(shí)間內(nèi)損失了差不多4億6千萬美元。他們只要使用更正式的構(gòu)建過程,就能避免公司的衰敗。想到這一點(diǎn),就會發(fā)現(xiàn)這整件事都是那么不可思議,不負(fù)責(zé)任,其實(shí)又應(yīng)該是很容易避免的。當(dāng)然,這是個(gè)極端案例,但明確表明了我的觀點(diǎn):自動化的過程能盡量避免人為錯(cuò)誤,至少也能更早發(fā)現(xiàn)問題。1.1.2構(gòu)建優(yōu)先我寫這本書的目的是教你使用構(gòu)建優(yōu)先原則,在還未編寫任何代碼之前就做好設(shè)計(jì),讓應(yīng)用的結(jié)構(gòu)清晰,易于測試。你會學(xué)習(xí)過程自動化的知識,減少人為出錯(cuò)的可能性,避免重蹈騎士資本的覆轍。構(gòu)建優(yōu)先原則是設(shè)計(jì)結(jié)構(gòu)清晰、易于測試的應(yīng)用之基礎(chǔ),使用這一原則開發(fā)出來的應(yīng)用易于維護(hù),也易于重構(gòu)。構(gòu)建優(yōu)先原則的兩個(gè)基本要素是過程自動化和合理的設(shè)計(jì)。為了教你使用構(gòu)建優(yōu)先原則,本書會向你展示能改進(jìn)軟件質(zhì)量和Web開發(fā)流程的技術(shù)。在第一部分,首先要學(xué)習(xí)如何建立適用于現(xiàn)代Web應(yīng)用開發(fā)的構(gòu)建過程,然后示范能提高日常開發(fā)效率的最佳實(shí)踐,例如修改代碼后執(zhí)行的任務(wù)、在終端里只輸入一個(gè)命令就部署應(yīng)用的方式,以及如何監(jiān)控生產(chǎn)環(huán)境中的應(yīng)用狀態(tài)。本書第二部分講管理復(fù)雜度和設(shè)計(jì),專注于應(yīng)用的質(zhì)量。在這一部分,我會比較當(dāng)前可用的一些模塊化方案,介紹如何更好地編寫模塊化的JavaScript組件。JavaScript中的異步流越來越復(fù)雜,越來越長,因此我單獨(dú)準(zhǔn)備了一章,讓你深入了解如何編寫簡潔的異步代碼,此外還會學(xué)習(xí)用來提升異步代碼質(zhì)量的不同工具。Backbone是入門首選的客戶端MVC框架,我會介紹一些足夠你開始使用JavaScript開發(fā)MVC應(yīng)用所需的知識。前面我提到過,易于測試對應(yīng)用來說很重要,雖然我們已經(jīng)實(shí)現(xiàn)了模塊化,向正確的方向邁出了一大步,但還是要在單獨(dú)的一章中說明測試。最后一章剖析一個(gè)流行的API設(shè)計(jì)思想,即REST(RepresentationalStateTransfer的縮寫,即“表現(xiàn)層狀態(tài)轉(zhuǎn)換”),我會幫助你設(shè)計(jì)自己的API,還會深入說明服務(wù)器端的應(yīng)用架構(gòu),不過仍然會密切關(guān)注前端。在探索構(gòu)建過程之前,我們再來看一個(gè)危險(xiǎn)的案例。這個(gè)案例遇到的問題,只要遵循構(gòu)建優(yōu)先原則,通過實(shí)現(xiàn)過程自動化就能避免。1.1.3繁瑣的前戲如果新成員加入團(tuán)隊(duì)后的設(shè)置步驟太復(fù)雜,也表明自動化程度不高。我以前參與的一些項(xiàng)目,首次搭建開發(fā)環(huán)境要一周時(shí)間,真是痛苦。在你想弄明白代碼的作用之前,竟然要浪費(fèi)一周時(shí)間。我要下載大約60GB的數(shù)據(jù)庫備份,還要創(chuàng)建一個(gè)數(shù)據(jù)庫,配置一些以前從未聽說過的選項(xiàng),例如排序規(guī)則,然后還得運(yùn)行一系列腳本,升級模式,可這些腳本甚至不能完全正常運(yùn)行。解決這個(gè)問題之后,還要在自己的環(huán)境中安裝指定的過時(shí)已久的Windows媒體播放器的解碼器,那感覺就像把一頭豬塞進(jìn)放滿東西的冰箱一樣,純屬徒勞。最后,我沖了一杯咖啡,試圖一次編譯好130多個(gè)大型項(xiàng)目??墒牵税惭b外部依賴,我想,安裝依賴就行了吧,但不行,還要編譯C++程序,這樣解碼器才能重新運(yùn)行。我再次編譯,又過了20分鐘。還不行!真煩?;蛟S我可以問問身邊的人,可是沒人確切知道該怎么做。他們一開始都經(jīng)歷過這樣痛苦的過程,但都不記得具體應(yīng)該怎么做了。查查維基百科?當(dāng)然可以,但信息記得零零散散,并不能用來解決遇到的具體問題。公司從未制定正式的初始化流程,事情變得越來越復(fù)雜,也就很難再去制定一個(gè)流程。他們不得不處理巨量的備份、升級腳本、解碼器和網(wǎng)站所需的多個(gè)服務(wù),哪怕是改動一個(gè)分號也要花一小時(shí)編譯項(xiàng)目。如果他們從一開始就自動執(zhí)行這些步驟,遵循構(gòu)建優(yōu)先原則,這個(gè)過程會順利得多。騎士資本的潰敗和這個(gè)過度復(fù)雜的設(shè)置故事有一個(gè)共同點(diǎn):如果他們能提前做好計(jì)劃,自動執(zhí)行構(gòu)建和部署,就能避免問題。提前計(jì)劃,自動執(zhí)行應(yīng)用相關(guān)的操作,這是構(gòu)建優(yōu)先原則的兩個(gè)基本要素,下一節(jié)會詳細(xì)說明。1.2遵守構(gòu)建優(yōu)先原則,提前計(jì)劃在騎士資本的案例中,他們忘了把代碼部署到其中一個(gè)服務(wù)器,即使有一步部署方案能自動把代碼部署到所有服務(wù)器,也無法避免這個(gè)公司破產(chǎn)。這個(gè)案例深層次的問題是代碼質(zhì)量,因?yàn)樗麄兊拇a基中存在已經(jīng)差不多十年不用的代碼。不增加功能的徹底重構(gòu)對產(chǎn)品經(jīng)理沒有吸引力,他們的目標(biāo)是提升面向客戶的可視化產(chǎn)品,而不是底層的軟件。不過,你可以逐漸改進(jìn)代碼基,重構(gòu)你接觸到的代碼,為重構(gòu)后的功能編寫測試,把過時(shí)的代碼包裝到接口中,以后再重構(gòu)——這樣做能不斷提升項(xiàng)目中代碼的平均質(zhì)量。不過,單單重構(gòu)還不夠。好的設(shè)計(jì)在一開始就要帶入項(xiàng)目中,不能等出現(xiàn)問題后才試圖強(qiáng)行用于糟糕的結(jié)構(gòu)中。除了前面提到的構(gòu)建過程之外,本書要闡述的另一個(gè)基本要素就是設(shè)計(jì)。在我們深入構(gòu)建優(yōu)先這個(gè)未知領(lǐng)域之前,我要強(qiáng)調(diào)一點(diǎn),構(gòu)建優(yōu)先并不只適用于JavaScript。多數(shù)人通常在后端語言(例如Java、C#或PHP)中使用我要介紹的原則,但在這里我把這些原則應(yīng)用到了JavaScript應(yīng)用的開發(fā)過程中。正如我前面提到的,客戶端代碼往往沒有得到應(yīng)有的關(guān)注和尊重,常常沒有適當(dāng)?shù)販y試代碼,導(dǎo)致代碼有缺陷,或者致使代碼基難以閱讀和維護(hù),最終受影響的是產(chǎn)品(以及開發(fā)者的工作效率)。對JavaScript來說,因?yàn)檫@門語言不需要編譯器,天真的開發(fā)者或許就以為根本不需要一套構(gòu)建過程。這樣的想法就像在黑暗中射擊一樣:在瀏覽器中執(zhí)行代碼之前,開發(fā)者不知道代碼是否能運(yùn)行,也不知道代碼是否能像預(yù)期那樣做該做的事。然后,這些人可能還要手動把應(yīng)用部署到線上環(huán)境,再遠(yuǎn)程登錄服務(wù)器,調(diào)整一些配置選項(xiàng),讓應(yīng)用能運(yùn)行。構(gòu)建優(yōu)先原則的核心法則構(gòu)建優(yōu)先原則的核心法則不僅鼓勵(lì)建立一套構(gòu)建過程,還鼓勵(lì)使用簡潔的方式設(shè)計(jì)應(yīng)用。下面概述了使用構(gòu)建優(yōu)先原則能獲得的好處:減少出錯(cuò)的可能性,因?yàn)榻换ミ^程中沒有人類參與;自動執(zhí)行重復(fù)性的任務(wù),能提高工作效率;模塊化、可伸縮的應(yīng)用設(shè)計(jì);能降低復(fù)雜度,讓應(yīng)用易于測試和維護(hù);讓發(fā)布版本符合性能方面的最佳實(shí)踐;部署的代碼在發(fā)布前都經(jīng)過了測試。在圖1-1中,從上到下分為四個(gè)部分。構(gòu)建過程:使用自動化方式編譯和測試應(yīng)用。構(gòu)建的目的是便于持續(xù)開發(fā),還能調(diào)校應(yīng)用,讓發(fā)布版本得到最好的性能。設(shè)計(jì):你的大部分時(shí)間都要用到設(shè)計(jì)上,在開發(fā)的過程中實(shí)現(xiàn)并改進(jìn)架構(gòu)。在設(shè)計(jì)的過程中,你可能要重構(gòu)代碼,更新測試,確保組件能按照預(yù)期的方式運(yùn)行。制定好構(gòu)建過程或準(zhǔn)備好部署時(shí),就要設(shè)計(jì)應(yīng)用的架構(gòu),并在代碼基中迭代開發(fā)。部署和環(huán)境:這兩部分的目的是自動執(zhí)行發(fā)布過程和配置不同的主機(jī)環(huán)境。部署過程的作用是把代碼變動傳送到主機(jī)環(huán)境中,而環(huán)境配置的作用是定義與應(yīng)用交互的環(huán)境和服務(wù),也包括數(shù)據(jù)庫。圖1-1概覽構(gòu)建優(yōu)先原則關(guān)注的四個(gè)方面:構(gòu)建過程,設(shè)計(jì),部署和環(huán)境從圖1-1可以看出,使用構(gòu)建優(yōu)先原則開發(fā)應(yīng)用主要涉及兩方面:項(xiàng)目相關(guān)的過程,例如構(gòu)建和部署應(yīng)用;應(yīng)用代碼本身的設(shè)計(jì)和質(zhì)量,這方面在日常開發(fā)新功能時(shí)要不斷提升。這兩方面同等重要,而且二者之間相互依賴,這樣才能得到最好的結(jié)果。如果應(yīng)用設(shè)計(jì)得不好,過程再好也不管用。類似地,沒有合適的構(gòu)建和部署步驟,再好的設(shè)計(jì)也不能挽救前面所述的那種危機(jī)。和構(gòu)建優(yōu)先原則一樣,本書也分為兩部分。第一部分介紹構(gòu)建過程(開發(fā)和發(fā)布都適用)和部署過程,還會介紹如何配置環(huán)境。第二部分探討應(yīng)用本身的問題,說明如何實(shí)現(xiàn)簡潔明了的模塊化設(shè)計(jì),還會介紹開發(fā)現(xiàn)代應(yīng)用時(shí)需要考慮的實(shí)用設(shè)計(jì)因素。下面兩節(jié)概述這兩部分要討論的概念。1.3構(gòu)建過程構(gòu)建過程涵蓋自動完成重復(fù)性的任務(wù),包括安裝依賴、編譯代碼、運(yùn)行單元測試,以及執(zhí)行其他重要的操作。能一步執(zhí)行完所有需要執(zhí)行的任務(wù)(一步構(gòu)建)非常重要,這么做優(yōu)勢明顯。只要制定好了一步構(gòu)建方案,想執(zhí)行多少次就能執(zhí)行多少次,而且效果不變。這種特性叫冪等:不管執(zhí)行多少次,結(jié)果都一樣。圖1-2更詳細(xì)地列出了組成自動構(gòu)建和部署過程的重要步驟。圖1-2構(gòu)建優(yōu)先原則中的構(gòu)建和部署過程自動構(gòu)建過程的優(yōu)缺點(diǎn)自動構(gòu)建過程的最大優(yōu)點(diǎn)是只要需要隨時(shí)都能部署。功能開發(fā)完畢后立即就讓用戶使用,有利于收窄反饋循環(huán),這樣我們就能更好地預(yù)見應(yīng)該開發(fā)什么樣的產(chǎn)品。自動構(gòu)建過程主要的缺點(diǎn)是在真正獲益之前,要花一定的時(shí)間制定這個(gè)過程,可是自動化過程的好處絕對物超所值,例如我們能自動測試,得到的代碼質(zhì)量更高,開發(fā)流程更精益,而且部署流程更安全。一般來說,這個(gè)過程只需設(shè)置一次,以后隨時(shí)都能再次執(zhí)行,而且在開發(fā)的過程中還可以適當(dāng)調(diào)整。1.構(gòu)建圖1-2的上半部分是構(gòu)建過程(如圖1-1所示)中構(gòu)建這一步的詳細(xì)說明,包含開發(fā)和發(fā)布兩方面的內(nèi)容。如果你關(guān)注的是開發(fā),就專注“調(diào)試”能力,我保證你想要一個(gè)無需干預(yù)就知道何時(shí)應(yīng)該執(zhí)行這些任務(wù)的構(gòu)建過程。這叫持續(xù)開發(fā)(ContinuousDevelopment,簡稱CD),在第3章中介紹。構(gòu)建過程中的“發(fā)布”和持續(xù)開發(fā)沒有關(guān)系,不過你應(yīng)該花時(shí)間優(yōu)化靜態(tài)資源,盡量讓應(yīng)用在生產(chǎn)環(huán)境中運(yùn)行得更快。2.部署圖1-2的下半部分是圖1-1中部署過程的詳細(xì)說明,這部分將調(diào)試或發(fā)行版作為應(yīng)用發(fā)行版(我在操作流程中使用“發(fā)行版”這個(gè)詞是有特殊目的的,全書都會這樣用)部署到主機(jī)環(huán)境。打包代碼得到的發(fā)行版會和環(huán)境相關(guān)的配置(用于安全存儲機(jī)密信息,例如數(shù)據(jù)庫連接字符串和API密鑰,第3章會討論)一起,服務(wù)于應(yīng)用。第一部分專門討論構(gòu)建優(yōu)先原則中構(gòu)建方面的話題。第2章說明構(gòu)建任務(wù),教你如何使用Grunt編寫和配置任務(wù)。Grunt是任務(wù)運(yùn)行程序,第一部分會一直使用這個(gè)工具。第3章介紹環(huán)境,如何安全配置應(yīng)用,還會介紹開發(fā)流程。第4章討論發(fā)布構(gòu)建版本時(shí)應(yīng)該執(zhí)行的任務(wù)。然后介紹部署方面的知識,如何每次推送到版本控制系統(tǒng)后都運(yùn)行測試,以及如何在生產(chǎn)環(huán)境中監(jiān)控應(yīng)用。3.構(gòu)建過程的好處讀完第一部分后你就能自信地在自己的應(yīng)用中執(zhí)行下述操作了。自動執(zhí)行重復(fù)的任務(wù),例如編譯、簡化和測試。制作圖標(biāo)子圖集表單,把對圖標(biāo)的HTTP請求數(shù)減少到只有一個(gè)。這種子圖技術(shù)和其他的HTTP1.x優(yōu)化技巧在第2章討論,目的是提升頁面的加載速度和應(yīng)用的交付性能。輕松搭建新環(huán)境,忽略開發(fā)環(huán)境和生產(chǎn)環(huán)境之間的區(qū)別。相關(guān)文件改動后自動重啟Web服務(wù)器,以及重新編譯靜態(tài)資源。通過靈活的一步部署方案,支持多個(gè)環(huán)境。處理繁瑣的任務(wù)時(shí),構(gòu)建優(yōu)先原則能節(jié)省人工,而且從一開始就能提升工作效率。構(gòu)建過程在構(gòu)建優(yōu)先原則中有重要意義,能打造出可維護(hù)的應(yīng)用,還能不斷減弱應(yīng)用的復(fù)雜度。本書第二部分會討論如何實(shí)現(xiàn)簡潔的應(yīng)用設(shè)計(jì)和架構(gòu),涵蓋應(yīng)用內(nèi)的復(fù)雜度管理,以及設(shè)計(jì)時(shí)為了提升質(zhì)量要考慮的因素。下面概述第二部分的內(nèi)容。1.4處理應(yīng)用的復(fù)雜度和設(shè)計(jì)理念不管使用什么語言開發(fā),如果想保證代碼在具有一定規(guī)模時(shí)仍能正常運(yùn)行,就一定要做到以下幾點(diǎn):模塊化、管理依賴、理解異步流、認(rèn)真遵守正確的模式和測試。在第二部分你會學(xué)到不同的概念、技術(shù)和模式,運(yùn)用這些知識后你的應(yīng)用就會變得更模塊化、更專注、更易于測試也更易于維護(hù)。在圖1-3中,從上到下就是第二部分的行文順序。圖1-3第二部分要討論的應(yīng)用設(shè)計(jì)和部署方面的內(nèi)容1.模塊化你會學(xué)習(xí)如何把應(yīng)用分成不同的組件,如何再把組件分成不同的模塊,然后在模塊中編寫作用單一的簡潔函數(shù)。模塊可以由外部包提供,由第三方開發(fā),也可以自己開發(fā)。外部包應(yīng)該交給包管理器處理,讓管理器管理版本,執(zhí)行升級操作,這樣就不用我們手動下載依賴了(例如jQuery)——整個(gè)過程都自動完成。在第5章你還會學(xué)到,模塊的依賴能在代碼中聲明,而不用從全局命名空間中獲取——這樣做能讓模塊更加獨(dú)立。模塊系統(tǒng)會利用這些信息,解析出所有的依賴,因此,為了能讓應(yīng)用正常運(yùn)行,我們就不必按一定順序維護(hù)一長串<script>標(biāo)簽了。2.設(shè)計(jì)你會學(xué)習(xí)如何分離關(guān)注點(diǎn),使用“模型-視圖-控制器”模式分層設(shè)計(jì)應(yīng)用,進(jìn)一步增強(qiáng)應(yīng)用的模塊化。在第7章我會告訴你關(guān)于共享渲染的知識,這個(gè)技術(shù)首先在服務(wù)器端渲染視圖,然后同一個(gè)單頁應(yīng)用中的后續(xù)請求都在客戶端渲染視圖。3.異步代碼我會教你使用不同的異步代碼流技術(shù),包括回調(diào)、Promise對象、生成器和事件,幫你馴服異步這頭猛獸。4.測試實(shí)踐第5章會討論模塊化的方方面面,學(xué)習(xí)閉包和模塊模式,還會討論不同的模塊系統(tǒng)和包管理器,并嘗試找出每種方案的優(yōu)勢。第6章會深入介紹JavaScript中的異步編程,告訴你如何避免編寫一周后就會讓人困惑的回調(diào),然后再學(xué)習(xí)Promise對象和ES6中的生成器API。第7章專門介紹各種模式和做法,例如如何寫出最好的代碼,對你來說jQuery是不是最好的選擇,以及如何編寫在客戶端和服務(wù)器中都能使用的JavaScript代碼。然后介紹Backbone這個(gè)MVC框架。記住,Backbone只是我用來向你介紹MVC知識的工具,并不是這方面唯一可用的框架。在第8章我們會介紹測試方案、自動化和很多客戶端JavaScript單元測試實(shí)例。你會學(xué)到如何為單個(gè)組件編寫單元測試,如何為整個(gè)應(yīng)用編寫集成測試。本書最后一章介紹RESTAPI設(shè)計(jì),如何在前端使用RESTAPI,以及為了充分發(fā)揮REST架構(gòu)的功能而推薦使用的結(jié)構(gòu)。5.設(shè)計(jì)時(shí)要考慮的實(shí)際問題本書的目的是讓你在開發(fā)真正的應(yīng)用時(shí)考慮一些設(shè)計(jì)方面的實(shí)際問題,充分考慮后再選擇最合適的工具,始終注重過程和應(yīng)用本身的質(zhì)量。當(dāng)你準(zhǔn)備開發(fā)應(yīng)用時(shí),首先要確定規(guī)模,選擇一個(gè)技術(shù)棧,再制定一個(gè)最小可行的構(gòu)建過程,然后開始開發(fā)應(yīng)用。你可能會使用MVC架構(gòu),或者在瀏覽器和服務(wù)器中都能使用的視圖渲染引擎,這些話題在第7章討論。在第9章你會學(xué)習(xí)開發(fā)API的重要知識,還會學(xué)習(xí)如何定義服務(wù)器端的視圖控制器和RESTAPI都能用到的后端服務(wù)。圖1-4簡要說明了使用構(gòu)建優(yōu)先原則開發(fā)時(shí)應(yīng)用的典型組織方式。圖1-4務(wù)實(shí)的架構(gòu)方式6.構(gòu)建過程從圖1-4的左上角開始看,可以看出,我們首先要制定一個(gè)構(gòu)建過程,這樣有助于開始著手架構(gòu)應(yīng)用,還要決定如何組織代碼基。定義一個(gè)模塊化的應(yīng)用架構(gòu)對可維護(hù)的代碼基來說是至關(guān)重要的,在第5章你會看出這一點(diǎn)。然后還要實(shí)現(xiàn)過程自動化,提供持續(xù)開發(fā)、持續(xù)集成和持續(xù)部署功能,以此增強(qiáng)架構(gòu)。7.設(shè)計(jì)和RESTAPI設(shè)計(jì)應(yīng)用本身,以及能顯著提升可維護(hù)性的RESTAPI時(shí),一定要明確每個(gè)組件的作用,讓組件之間形成正交關(guān)系(意思是,組件之間在任何方面都不會爭奪資源)。在第9章我們會探討一種設(shè)計(jì)應(yīng)用的多層方式,我們會嚴(yán)格定義各層以及層與層之間的通信路徑,把Web界面與數(shù)據(jù)和業(yè)務(wù)邏輯明確地隔開。8.積極測試設(shè)計(jì)好構(gòu)建過程和架構(gòu)后,我們要積極測試,關(guān)注可靠性方面的問題。我們要探索持續(xù)集成,每次把代碼推送到版本控制系統(tǒng)后都要執(zhí)行測試;或許還要探索持續(xù)開發(fā),每天多次把應(yīng)用部署到生產(chǎn)環(huán)境。我們還會討論容錯(cuò)方面的知識,例如記錄日志、監(jiān)控和搭建集群。這些內(nèi)容會在第4章概述,為的是讓生產(chǎn)環(huán)境更穩(wěn)健,至少在出問題時(shí)能提醒你。在這個(gè)過程中我們會編寫測試,調(diào)整構(gòu)建過程,還會微調(diào)代碼。對你來說,這是個(gè)好機(jī)會,能讓你仔細(xì)審視構(gòu)建優(yōu)先原則。駕輕就熟后,再開始學(xué)習(xí)構(gòu)建優(yōu)先原則的細(xì)節(jié)。1.5鉆研構(gòu)建優(yōu)先原則質(zhì)量是構(gòu)建優(yōu)先原則的基石,這個(gè)原則采取的每項(xiàng)措施都是為了一個(gè)簡單的目標(biāo),即提升代碼的質(zhì)量,并使用更合理的方式組織代碼。在本節(jié)你要學(xué)習(xí)代碼質(zhì)量方面的知識,以及如何在命令行使用檢查代碼質(zhì)量的工具:lint程序。衡量代碼的質(zhì)量是向編寫結(jié)構(gòu)良好的應(yīng)用邁出的第一步。盡早這么做容易讓代碼基符合一定的質(zhì)量標(biāo)準(zhǔn),所以接下來我們就要來做這件事。學(xué)會使用lint程序后,在第2章我會介紹如何使用Grunt。本書會一直使用這個(gè)構(gòu)建工具制定自動化構(gòu)建過程。使用Grunt能在構(gòu)建過程中檢查代碼質(zhì)量,以防你忘記做這件事。Grunt:實(shí)現(xiàn)自動化的工具第一部分會大量使用Grunt,第二部分也會適量使用。我們使用這個(gè)工具實(shí)現(xiàn)構(gòu)建過程。選擇Grunt是因?yàn)樗芰餍?,而且易于學(xué)習(xí),能滿足大多數(shù)人的需求:完全支持Windows;使用時(shí)只需少量的JavaScript知識,而且易于安裝和運(yùn)行。記住,Grunt只是一種工具,使用它能輕易實(shí)現(xiàn)本書介紹的構(gòu)建過程,并不是說Grunt始終是最佳選擇。為了明確這一點(diǎn),我會把Grunt和另外兩個(gè)工具做對比:一個(gè)是npm,這是一個(gè)包管理器,也能當(dāng)作簡單的構(gòu)建工具使用;另一個(gè)是Gulp,這是一個(gè)由代碼驅(qū)動的構(gòu)建工具,和Grunt有很多共同點(diǎn)。如果你對其他構(gòu)建工具(例如Gulp)好奇,或者想把npmrun當(dāng)成構(gòu)建系統(tǒng)使用,請閱讀附錄C,其中詳細(xì)說明了如何選擇合適的構(gòu)建工具。lint程序是檢查代碼質(zhì)量的工具,特別適合用來檢查使用解釋型語言(例如JavaScript)編寫的程序。我們不用打開瀏覽器檢查代碼是否有句法錯(cuò)誤,在命令行中執(zhí)行l(wèi)int程序就能找出代碼中潛在的問題,例如未聲明的變量、缺少分號或句法錯(cuò)誤。不過lint程序也不是萬能的,它檢測不到代碼中的邏輯問題,只能提醒句法和風(fēng)格錯(cuò)誤。1.5.1檢查代碼質(zhì)量lint程序能判斷給定的代碼片段中有沒有句法錯(cuò)誤,還能實(shí)施一些JavaScript編程的最佳實(shí)踐規(guī)則。第二部分的開頭第5章,在討論模塊化和依賴管理時(shí)會介紹這些最佳實(shí)踐。大約10年前,DouglasCrockford發(fā)布了JSLint。這個(gè)工具檢查代碼時(shí)很嚴(yán)格,會報(bào)告代碼中所有的小問題。lint程序的作用是幫助我們提升代碼的整體質(zhì)量。lint程序直接在命令行中執(zhí)行,能報(bào)告代碼片段或文件中潛在的問題。這么做有個(gè)額外好處,我們甚至不用執(zhí)行代碼就能找出問題。對JavaScript代碼來說,這個(gè)過程特別有用,因?yàn)樵谀撤N程度上,lint程序可以當(dāng)做編譯器,盡量確保代碼能被JavaScript引擎解釋。除此之外,我們還能配置lint程序,讓它發(fā)現(xiàn)太復(fù)雜的代碼時(shí)提醒我們,比如說行數(shù)太多的函數(shù),可能會讓別人困惑的晦澀結(jié)構(gòu)(對JavaScript來說,例如with塊,new語句,或者過度使用this),諸如此類的代碼風(fēng)格問題。以下述代碼片段為例(位于在線示例的ch01/01_lint-sample文件夾中):

functioncompose_ticks_count(start){

start||start=1;

this.counter=start;

returnfunction(time){

ticks=+newDate;

returnticks+'_'+this.counter++

}

}

這么一小段代碼中有很多問題,不過可能很難發(fā)現(xiàn)。使用JSLint分析這段代碼時(shí),既會得到預(yù)料之中的結(jié)果,也會得到意料之外的結(jié)果。JSLint會提醒你,變量在使用之前必須先聲明,而且缺少分號。如果使用其他lint程序,可能還會抱怨你使用了this關(guān)鍵字。大多數(shù)lint程序都會抱怨你使用了||運(yùn)算符,而沒使用更易于閱讀的if語句。你可以在線檢查這段代碼。2圖1-5是使用Crockford的工具檢查得到的結(jié)果。2訪問\h/,然后輸入這段代碼。這是最先出現(xiàn)的JavaScriptlint工具,由Crockford維護(hù)。圖1-5在一段代碼中發(fā)現(xiàn)的錯(cuò)誤對編譯型語言來說,這些錯(cuò)誤類型在編譯代碼時(shí)就能捕獲,因此不需要使用lint工具。而JavaScript沒有編譯器,這是由這門語言的動態(tài)特性決定的。這種方式無疑很強(qiáng)大,但和編譯型語言相比卻更容易出錯(cuò),一開始代碼甚至無法執(zhí)行。JavaScript代碼無需編譯,由引擎解釋執(zhí)行,例如V8(GoogleChrome使用的引擎)和SpiderMonkey(MozillaFirefox使用的引擎)。雖然有些引擎(最著名的是V8引擎)會編譯JavaScript代碼,但在瀏覽器之外享受不到靜態(tài)代碼分析的好處。3像JavaScript這樣的動態(tài)語言有個(gè)缺點(diǎn),執(zhí)行代碼時(shí)無法確保代碼一定能正常運(yùn)行。雖然如此,但是使用lint工具能大大降低這種不確定性。而且JSLint還會建議我們不要使用某種編程風(fēng)格,例如使用了eval,沒聲明變量,語句塊缺少花括號等。3在終端使用Node.js能獲得這個(gè)功能,但V8引擎檢測到句法問題時(shí)已經(jīng)太晚了,此時(shí)程序會崩潰。Node.js是服務(wù)器端JavaScript平臺,也運(yùn)行在V8引擎之上。你發(fā)現(xiàn)前面代碼片段中的函數(shù)有什么問題了嗎?看一下本書的配套代碼示例(ch01/01_lint-sample文件夾),驗(yàn)證一下自己的答案。提示:問題是有重復(fù)。修正后的版本也在源碼示例中,你一定要看一下好的寫法。對本書配套源碼的說明本書的配套源碼包含很多重要的信息,例如上述示例函數(shù)有一個(gè)調(diào)整后的版本,能通過lint程序的驗(yàn)證,而且有很多注釋,便于理解改動的部分。這個(gè)示例也證明了lint程序不是萬能的。本書配套源碼中的其他代碼示例也有類似的建議和重要的信息,所以一定要看一下!配套源碼中的示例按章組織,而且和在書中出現(xiàn)的順序一致。很多示例在書中只有簡單討論,不過在配套源碼中所有代碼示例都有完整的注釋,拿來就可以使用。書中的代碼和配套源碼之間出現(xiàn)這種差異是因?yàn)?,有時(shí)我想說明某個(gè)話題,但可能涉及的代碼太多,在書中不能全部列出。遇到這種情況時(shí),我不想太過偏離要講解的概念,又想給你提供真實(shí)的代碼。使用這種方式,在閱讀本書的過程中能讓你集中精力學(xué)習(xí),瀏覽代碼示例時(shí)再集中精力去試驗(yàn)。通常,寫完代碼后第一件事就是使用lint程序檢查,lint程序發(fā)現(xiàn)不了的問題則交給單元測試。這并不意味著沒必要使用lint程序,而是說僅使用lint程序是不夠的。單元測試的作用是確保代碼的表現(xiàn)與預(yù)期一樣。單元測試在第8章討論,你會學(xué)習(xí)如何為第二部分編寫的代碼編寫測試。第二部分的內(nèi)容旨在說明如何編寫模塊化、可維護(hù)和可測試的JavaScript代碼。接下來我們要從零開始制定一個(gè)構(gòu)建過程。我們從簡單的任務(wù)開始,先編寫一個(gè)運(yùn)行l(wèi)int程序檢查代碼的任務(wù),然后在命令行中運(yùn)行這個(gè)任務(wù),就像使用編譯器編譯代碼的過程一樣。你會學(xué)著養(yǎng)成習(xí)慣,每次修改代碼后都執(zhí)行這個(gè)任務(wù),查看代碼是否能夠通過lint程序的檢查。第3章會教你如何自動執(zhí)行這個(gè)任務(wù),這樣就不必每次都手動執(zhí)行了。不過現(xiàn)在可以手動執(zhí)行?!叭绾沃苯釉诿钚兄惺褂肑SLint這樣的lint工具呢?”我很高興你能提出這個(gè)問題。1.5.2在命令行中使用lint工具把任務(wù)添加到構(gòu)建過程最常見的方式之一,是在命令行中執(zhí)行這個(gè)任務(wù)。如果能在命令行中執(zhí)行任務(wù),那么這個(gè)任務(wù)就能輕易集成到構(gòu)建過程中。下面介紹如何使用JSHint4檢查你的軟件。4關(guān)于JSHint更多的信息,請?jiān)L問\h。JSHint是一個(gè)命令行工具,使用Node.js編寫,用于檢查JavaScript文件和代碼片段。Node.js是一個(gè)使用JavaScript開發(fā)應(yīng)用的平臺,如果你想簡單了解Node.js的基礎(chǔ)知識,可以翻到附錄A,在這篇附錄中我說明了什么是模塊,以及模塊的工作方式。如果你想深入學(xué)習(xí)Node.js,可以閱讀MikeCantelon等人寫的《Node.js實(shí)戰(zhàn)》。掌握Node.js的知識也有助于使用下一章我們選定的構(gòu)建工具——Grunt。Node.js簡介Node.js是相對較新的平臺,你肯定聽說過。Node最初于2009年發(fā)布,遵從事件驅(qū)動和單線程模式,能高效并發(fā)處理請求。從這方面來看,Node和Nginx的設(shè)計(jì)理念一致。Nginx是高度可伸縮的多用途反向代理服務(wù)器,非常流行,作用是伺服靜態(tài)內(nèi)容,以及把請求轉(zhuǎn)發(fā)給應(yīng)用服務(wù)器(例如Node)。Node.js廣受贊譽(yù),尤其是對前端工程師來說,特別容易上手,因?yàn)榇笾露?,它只不過是在服務(wù)器端運(yùn)行的JavaScript。Node.js還能把前端完全從后端抽象出來5,只通過數(shù)據(jù)和RESTAPI接口交互。我們在第9章就會使用這樣的方式設(shè)計(jì)和開發(fā)應(yīng)用。5關(guān)于把前端從后端抽象出來的更多信息,請?jiān)L問\hhttp://bevacqua.io/bf/node-frontend。1.安裝Node.js和JSHint安裝Node.js和JSHint命令行界面(Command-lineInterface,簡稱CLI)的步驟如下。安裝Node.js的其他方式和排除故障的方法參見附錄A。訪問\h,點(diǎn)擊頁面中的“INSTALL”按鈕(如圖1-6),下載最新版Node.js。圖1-6Node.js的網(wǎng)站運(yùn)行下載得到的文件,按照安裝說明安裝。安裝完成后會得到一個(gè)命令行工具,名為npm(NodePackageManager的簡稱),因?yàn)檫@個(gè)工具和Node.js是捆綁在一起的。npm是個(gè)包管理器,在終端里使用,用于安裝、發(fā)布和管理Node.js項(xiàng)目用到的模塊。包可以安裝在各個(gè)項(xiàng)目中,也可以全局安裝——這樣更便于從終端調(diào)用。其實(shí),這兩種安裝方式之間的區(qū)別是,全局安裝的包存放在環(huán)境變量PATH對應(yīng)的文件夾中,而另一種安裝方式把包存放在一個(gè)名為node_modules的文件夾中,而這個(gè)文件夾位于執(zhí)行安裝命令所在的文件夾中。為了讓項(xiàng)目自成一體,都推薦把包安裝在項(xiàng)目中。不過,對JSLint這樣的實(shí)用工具來說,我們希望在整個(gè)系統(tǒng)中都能使用,因此全局安裝更合適。修飾符-g能讓npm全局安裝JSHint。使用這種方式安裝,我們能在命令行中通過jshint命令使用JSHint。打開你最喜歡的終端,執(zhí)行npminstall-gjshint命令,如圖1-7所示。如果安裝失敗,可能要使用sudo提升權(quán)限,例如sudonpminstall-gjshint。此行文本用于列表編號,不因該出現(xiàn)在正文中。圖1-7使用npm安裝JSHint執(zhí)行jshint--version。這個(gè)命令應(yīng)該輸出JSHint的版本號,如圖1-8所示。你看到的版本號可能和圖中不一樣,因?yàn)殚_發(fā)活躍的包經(jīng)常會變更版本號。圖1-8在終端里驗(yàn)證jshint可用下一節(jié)說明如何檢查代碼。2.檢查代碼你現(xiàn)在應(yīng)該在系統(tǒng)中安裝好了JSHint,而且已經(jīng)確認(rèn)可以在終端里調(diào)用。如果想使用JSHint檢查代碼,可以使用cd命令進(jìn)入項(xiàng)目的根目錄,然后輸入jshint.(點(diǎn)號告訴JSHint檢查當(dāng)前文件夾里的所有文件)。如果執(zhí)行的時(shí)間太長,或許要加上--excludenode_modules選項(xiàng),告訴JSHint只檢查自己編寫的代碼,忽略通過npminstall安裝的第三方代碼。命令執(zhí)行完畢后,你會看到一份詳細(xì)報(bào)告,說明代碼的狀況。如果代碼中有問題,這個(gè)工具會報(bào)告預(yù)期的結(jié)果和出現(xiàn)問題的行號,然后退出,返回一個(gè)錯(cuò)誤碼。如果通不過檢查,我們可以使用這個(gè)錯(cuò)誤碼中斷構(gòu)建過程。只要有構(gòu)建任務(wù)沒得到預(yù)期的輸出,整個(gè)構(gòu)建過程就應(yīng)該中止。這么做有很多好處,出錯(cuò)后不會繼續(xù)運(yùn)行,在問題解決前不會完成整個(gè)構(gòu)建過程。圖1-9顯示的是檢查某段代碼后得到的結(jié)果。圖1-9在命令行中使用JSHint檢查代碼安裝好JSHint之后你可能就想收工了,因?yàn)檫@是你唯一的任務(wù)??墒牵绻朐跇?gòu)建過程中增加任務(wù),還不方便。你或許想在構(gòu)建過程中增加一步,運(yùn)行單元測試,這時(shí)就會遇到問題,因?yàn)槟悻F(xiàn)在至少要執(zhí)行兩個(gè)命令:一個(gè)是jshint,另一個(gè)是運(yùn)行測試的命令。這樣做的伸縮性不好,你要記住如何使用jshint,還有很多其他命令及其參數(shù),太麻煩,難記,而且容易出錯(cuò)。你肯定不想損失五億美元吧!那么你最好把構(gòu)建任務(wù)放在一起,雖然現(xiàn)在只有一個(gè)任務(wù),但很快就會變多。制定構(gòu)建過程時(shí)要考慮自動化,避免重復(fù)各個(gè)步驟,以節(jié)省時(shí)間。每門語言都有多個(gè)專用的構(gòu)建工具,而且多數(shù)情況下都有一個(gè)工具比較出眾,使用范圍比其他工具廣。對JavaScript來說,Grunt是最受歡迎的構(gòu)建工具之一,有成千上萬個(gè)插件(輔助構(gòu)建任務(wù))供使用。如果你要為其他語言制定構(gòu)建過程,或許需要自己搜索,找到合適的工具。雖然本書編寫的構(gòu)建任務(wù)是針對JavaScript的,而且使用Grunt,不過我講的原則應(yīng)該能應(yīng)用于任何語言和構(gòu)建工具。翻到第2章,看看如何把JSHint集成到Grunt中,以此開啟制定構(gòu)建過程的旅程。1.6總結(jié)本章概覽了本書后面幾章要深入探討的概念。下面列出你在本章學(xué)到的內(nèi)容?,F(xiàn)代JavaScript應(yīng)用開發(fā)是有問題的,因?yàn)槿鄙賹υO(shè)計(jì)和架構(gòu)的重視。使用構(gòu)建優(yōu)先原則能得到自動化的過程,設(shè)計(jì)出可維護(hù)的應(yīng)用,而且鼓勵(lì)思考你所開發(fā)的應(yīng)用。學(xué)會了使用lint程序檢查代碼,不使用瀏覽器就提升了代碼質(zhì)量。在第一部分你會學(xué)習(xí)構(gòu)建過程、部署和環(huán)境配置的所有知識。你將使用Grunt開發(fā)構(gòu)建過程,在附錄C中還能學(xué)習(xí)可以使用的其他工具。第二部分專門說明應(yīng)用設(shè)計(jì)的復(fù)雜性。模塊化、異步代碼流、應(yīng)用和API設(shè)計(jì),以及可測試性都有一定的作用,會在第二部分介紹。說到使用構(gòu)建優(yōu)先原則設(shè)計(jì)應(yīng)用的好處,現(xiàn)在你只看到了皮毛,還有很多知識要學(xué)。下面我們進(jìn)入第2章,討論構(gòu)建過程中最可能要執(zhí)行的任務(wù),再通過示例說明如何使用Grunt實(shí)現(xiàn)這些任務(wù)。

第2章編寫構(gòu)建任務(wù),制定流程本章內(nèi)容理解在構(gòu)建過程中應(yīng)該做什么學(xué)習(xí)關(guān)鍵的構(gòu)建任務(wù)使用Grunt執(zhí)行關(guān)鍵的構(gòu)建任務(wù)使用Grunt配置構(gòu)建流程自己編寫Grunt任務(wù)前一章簡單概述了構(gòu)建優(yōu)先原則,還稍微提到了一個(gè)使用lint程序檢查代碼的任務(wù)。本章我們要介紹一些常見的構(gòu)建任務(wù),還會介紹一些高級任務(wù)。我會告訴你這些任務(wù)的使用場景,以及使用它們的原因,然后介紹如何使用Grunt實(shí)現(xiàn)。學(xué)習(xí)理論的過程可能很枯燥,但如果你想使用Grunt之外的任務(wù)運(yùn)行程序——我相信最終你會這么做的——理論就顯得尤為重要了。Grunt是由配置驅(qū)動的構(gòu)建工具,能輕易執(zhí)行復(fù)雜的任務(wù),只要你知道你想做什么就行。使用Grunt能制定出我在第1章說過的那種工作流程,提高開發(fā)效率,并優(yōu)化發(fā)布流程。而且在部署過程中Grunt也能提供幫助,這一點(diǎn)將在第4章詳述。本章關(guān)注的是構(gòu)建任務(wù),不會教你Grunt的全部知識。只要理解了工具目標(biāo)背后的概念,就能學(xué)會使用工具,但如果不理解這些基本概念,肯定學(xué)不會如何正確使用其他工具。如果你想深入學(xué)習(xí)Grunt,可以閱讀附錄B。閱讀該附錄對理解本章的內(nèi)容沒有幫助,不過這篇附錄講解了第一部分會用到的Grunt功能。本章首先簡要介紹Grunt及其核心概念,剩下的內(nèi)容則教你構(gòu)建任務(wù)的知識,還會教你使用一些不同的工具。我們會學(xué)習(xí)預(yù)處理任務(wù),例如把代碼編譯成另一種語言,還會學(xué)習(xí)后處理任務(wù)如簡化靜態(tài)資源、創(chuàng)建圖像子圖集,以及代碼完整性任務(wù)如運(yùn)行JavaScript單元測試、使用lint程序檢查CSS代碼。隨后我們會學(xué)習(xí)如何使用Grunt自己編寫任務(wù),我會舉一個(gè)案例,教你編寫一套數(shù)據(jù)庫模式更新任務(wù),這個(gè)任務(wù)還支持回滾操作。我們開始學(xué)習(xí)吧!2.1介紹GruntGrunt1是一個(gè)任務(wù)運(yùn)行程序,能幫你執(zhí)行命令、運(yùn)行JavaScript代碼,還能使用完全由JavaScript編寫的代碼配置各個(gè)任務(wù)。Grunt的構(gòu)建概念借鑒自Ant,讓你使用JavaScript定義自己的流程。1關(guān)于Grunt的更多信息請?jiān)L問\hhttp://bevacqua.io/bf/grunt,也可以閱讀附錄B。圖2-1是從較高層次上對Grunt進(jìn)行的詳細(xì)解析,展示了如何配置Grunt以及定義構(gòu)建任務(wù)時(shí)需要理解的關(guān)鍵概念。任務(wù)用于執(zhí)行操作。目標(biāo)定義任務(wù)的上下文。任務(wù)的配置決定具體的任務(wù)和目標(biāo)組合使用哪些選項(xiàng)。圖2-1Grunt一覽:任務(wù)和目標(biāo)都在配置中Grunt任務(wù)使用JavaScript代碼配置。大多數(shù)情況下,配置都是通過把一個(gè)對象傳給grunt.initConfig方法完成的。在配置中可以指明任務(wù)會作用于哪些文件,還能傳入一些選項(xiàng),調(diào)整某個(gè)任務(wù)目標(biāo)的行為。對運(yùn)行單元測試的任務(wù)來說,在本地開發(fā)時(shí)你可能只想運(yùn)行幾個(gè)測試,或者在發(fā)布用于生產(chǎn)環(huán)境的版本前運(yùn)行所有測試。圖2-2展示了用于配置的JavaScript代碼,詳細(xì)說明了grunt.initConfig方法及其約定。枚舉文件時(shí)可以使用通配符,而使用這種模式叫通配。2.2.2節(jié)會詳細(xì)說明通配模式。任務(wù)可以從插件中導(dǎo)入。插件是Node模塊(設(shè)計(jì)良好、自成一體的代碼),包含一個(gè)或多個(gè)Grunt任務(wù)。你只需要知道插件能使用哪些配置,任務(wù)本身則由插件處理。本章會大量使用插件。22Grunt插件可以在線搜索,地址是\h/plugins。你也可以自己編寫任務(wù),2.4節(jié)和2.5節(jié)會介紹方法。Grunt自帶了一個(gè)CLI(Command-LineInterface,命令行接口),名為grunt,提供了一個(gè)簡單的接口,用于直接在命令行中執(zhí)行構(gòu)建任務(wù)。下面我們來安裝Grunt。圖2-2Grunt任務(wù)配置的代碼詳解圖。每個(gè)任務(wù)和任務(wù)目標(biāo)都單獨(dú)配置。2.1.1安裝Grunt你應(yīng)該已經(jīng)安裝了npm,因?yàn)樵诘?章安裝lint工具JSHint時(shí)安裝了Node.js,而Node.js中就有包管理器npm。Grunt的安裝方法很簡單,在終端里執(zhí)行下述命令就能安裝grunt3的CLI:3關(guān)于Grunt的更多信息,請?jiān)L問\hhttp://bevacqua.io/bf/grunt。

npminstall-ggrunt-cli

-g標(biāo)志表明這個(gè)包要全局安裝,這樣不管當(dāng)前工作目錄是什么,都能在終端里執(zhí)行g(shù)runt命令。找到本書配套源碼中有注解的示例本書的配套源碼中有完整可用的示例。這一節(jié)的示例在ch02目錄里的01_intro-to-grunt文件夾中,本章其他的示例也在ch02目錄里。大部分示例都有代碼注解,在你產(chǎn)生困惑時(shí)能幫助你理解。接下來你需要創(chuàng)建一個(gè)名為package.json的清單文件。這個(gè)文件用于描述Node.js項(xiàng)目,指明項(xiàng)目依賴的包列表,還有一些元信息,例如項(xiàng)目名稱、版本、描述和主頁。為了能在你的項(xiàng)目中使用Grunt,你需要把它添加到package.json文件中,作為一個(gè)開發(fā)依賴。之所以作為開發(fā)依賴,是因?yàn)槌吮镜亻_發(fā)環(huán)境之外,在其他地方用不到Grunt。你可以創(chuàng)建一個(gè)最簡單的package.json文件,寫入下述JSON代碼,并把這個(gè)文件保存到項(xiàng)目的根目錄中:

{}

這樣就行了。只要package.json文件存在,而且包含一個(gè)有效的JSON對象,哪怕是空對象{}也行,Node包管理器(npm)就能向其中添加依賴。1.在本地安裝Grunt接下來要安裝grunt包。這一次我們不能使用-g修飾符了,因?yàn)楝F(xiàn)在要在本地安裝Grunt,而不是全局安裝4——這也就是為什么要創(chuàng)建package.json文件的原因?,F(xiàn)在我們要使用--save-dev修飾符,指明這是個(gè)開發(fā)依賴。4Grunt包和任務(wù)插件都要求在本地安裝Grunt,這樣代碼才能在不同的設(shè)備中正常運(yùn)行,因?yàn)樵趐ackage.json文件中無法包含全局安裝的包。我們要執(zhí)行的命令是:npminstall--save-devgrunt。npm安裝完這個(gè)包后,package.json文件的內(nèi)容會變成類似下面這樣:

{

"devDependencies":{

"grunt":"~0.4.1"

}

而且,Grunt模塊會安裝到項(xiàng)目的node_modules目錄里。Grunt用到的所有模塊都會安裝在這個(gè)目錄中,而且都會在包清單文件中列出來。2.創(chuàng)建Gruntfile.js文件最后一步是創(chuàng)建Gruntfile.js文件。Grunt使用這個(gè)文件加載可用的任務(wù),并使用所需的參數(shù)配置任務(wù)。下述代碼是最簡單的Gruntfile.js模塊:

module.exports=function(grunt){

grunt.registerTask('default',[]);//注冊default任務(wù)別名

};

這個(gè)文件看似平常,但有幾點(diǎn)要注意。Gruntfile.js文件是符合CommonJS模塊規(guī)范5的Node模塊,因此在其他文件中無法立即訪問這個(gè)文件中的代碼。這個(gè)文件中的module是個(gè)隱式對象,不像瀏覽器中的window,是個(gè)全局對象。導(dǎo)入其他模塊時(shí),得到的只是module.exports提供的公開接口。5請?jiān)L問\hhttp://bevacqua.io/bf/commonjs閱讀CommonJS模塊規(guī)范。Node.js模塊Node.js模塊采用的規(guī)范CommonJS,在附錄A中有進(jìn)一步介紹。第5章說明模塊化時(shí)還會討論CommonJS。附錄B是對附錄A的擴(kuò)充,能增強(qiáng)你對Grunt的理解。上述代碼片段中g(shù)runt.registerTask那行告訴Grunt定義一個(gè)默認(rèn)任務(wù),在命令行中執(zhí)行g(shù)runt且不帶任何參數(shù)時(shí)執(zhí)行的就是這個(gè)任務(wù)。數(shù)組表明這是個(gè)任務(wù)別名,只要數(shù)組中有任務(wù),就會執(zhí)行其中所有的任務(wù)。例如,設(shè)為['lint','build']時(shí)會執(zhí)行l(wèi)int任務(wù)和build任務(wù)?,F(xiàn)在執(zhí)行g(shù)runt命令沒有任何作用,因?yàn)槲覀冏缘膭e名是空的。你肯定迫切地想設(shè)置第一個(gè)Grunt任務(wù),那就來設(shè)置吧。2.1.2設(shè)置第一個(gè)Grunt任務(wù)設(shè)置Grunt任務(wù)的第一步是安裝能滿足需求的插件,然后添加配置,最后再運(yùn)行任務(wù)。Grunt插件一般以npm模塊的形式分發(fā),這些模塊是別人發(fā)布的JavaScript代碼,你可以直接使用。我們要先安裝Grunt的JSHint插件,安裝了這個(gè)插件后就能使用Grunt運(yùn)行JSHint了。注意,這里完全不需要第1章安裝的CLI工具jshint,因?yàn)镚runt插件包含運(yùn)行JSHint所需的一切,它會自動安裝jshintCLI。下述命令會從npm源獲取JSHint插件,安裝到node_modules目錄中,然后再把這個(gè)插件添加到package.json文件中,作為一個(gè)開發(fā)依賴:

npminstall--save-devgrunt-contrib-jshint

接下來我們要修改Gruntfile文件,讓Grunt使用lint程序檢查這個(gè)文件,因?yàn)樗彩莻€(gè)JavaScript文件。你要告訴Grunt,讓它加載包含檢查任務(wù)的JSHint插件包,還要更新default任務(wù),這樣在命令行中只執(zhí)行g(shù)runt就能檢查代碼了。配置Gruntfile.js文件的方式如下述代碼清單所示(在代碼示例的ch02/01_intro-to-grunt文件夾里)。代碼清單2.1Gruntfile.js文件示例

module.exports=function(grunt){<導(dǎo)出的函數(shù)有個(gè)名為grunt的參

grunt.initConfig({<任務(wù)在initConfig方法中配置,傳入的是一個(gè)描述配置信息的對象。

jshint:['Gruntfile.js']

});

grunt.loadNpmTasks('grunt-contrib-jshint');<插件要分別載入Grunt。

grunt.registerTask('default',['jshint']);<注冊default別名,執(zhí)行jshint任務(wù)。

};

安裝插件包后都要在Gruntfile.js文件中使用grunt.loadNpmTasks將其載入Grunt,如代碼清單2.1所示。Grunt要從包中加載任務(wù),這樣才能配置和執(zhí)行任務(wù)。隨后你要配置任務(wù),方法是把一個(gè)對象傳給grunt.initConfig方法。每個(gè)任務(wù)插件都要配置,介紹各個(gè)插件時(shí)我都會告訴你怎么配置。最后,我還更新了default別名,讓它執(zhí)行jshint任務(wù)。default別名用于定義沒有任何參數(shù)的grunt命令執(zhí)行哪些任務(wù)。執(zhí)行g(shù)runt命令得到的輸出如下面的截圖所示。圖2-3我們的第一個(gè)Grunt任務(wù)及其輸出。我們的代碼通過了檢查,也就是說沒有句法錯(cuò)誤。2.1.3使用Grunt管理構(gòu)建過程現(xiàn)在我們實(shí)現(xiàn)的功能幾乎和第1章結(jié)束時(shí)的一樣,也就是能使用JSHint檢查JavaScript代碼了,不過這次有所不同。Grunt能幫我們制定一個(gè)成熟的構(gòu)建過程,這對構(gòu)建優(yōu)先原則至關(guān)重要。省下的精力能讓我們專注于為本地開發(fā)環(huán)境中的構(gòu)建、診斷或構(gòu)建用戶最終要使用的產(chǎn)品這個(gè)過程編寫不同的任務(wù)。我們來看看構(gòu)建任務(wù)的幾個(gè)特性。前面設(shè)置的lint任務(wù)是個(gè)基礎(chǔ)。在閱讀本書第一部分的過程中,你的理解會越來越深,有了這個(gè)基礎(chǔ)就能寫出更強(qiáng)大的構(gòu)建任務(wù)。這個(gè)任務(wù)清楚地表明了構(gòu)建任務(wù)的一個(gè)基本特性:絕大多數(shù)情況下,任務(wù)都是冪等的,即重復(fù)執(zhí)行任務(wù)不會得到不同的結(jié)果。對這個(gè)lint任務(wù)來說,這可能意味著,只要不修改源碼,每次執(zhí)行都能得到相同的提醒。通常,構(gòu)建任務(wù)都會操作一個(gè)或多個(gè)輸入文件。有了冪等特性,加之我們不再手動執(zhí)行任何操作了,這樣得到的結(jié)果會更加一致。1.創(chuàng)建工作流程和持續(xù)開發(fā)構(gòu)建過程中的任務(wù)要按照明確定義的順序執(zhí)行才能實(shí)現(xiàn)特定的目標(biāo),例如準(zhǔn)備一個(gè)發(fā)布版本。我們在第1章提到過,這叫工作流程。某些任務(wù)在具體的工作流程中是可有可無的,有些則可能會提供一些幫助。例如,在本地開發(fā)環(huán)境中沒必要優(yōu)化圖像,把圖像變得更小,因?yàn)檫@么做不能顯著提升性能,所以最好跳過這個(gè)任務(wù)。但不管在開發(fā)流程還是發(fā)布流程中,你或許都想執(zhí)行l(wèi)int任務(wù),以確保代碼沒問題。圖2-4能幫助你理解構(gòu)建過程中涉及的開發(fā)、發(fā)布和部署方面的任務(wù)。圖中展示了各個(gè)任務(wù)之間的關(guān)系,以及如何把任務(wù)組合起來,構(gòu)成不同的工作流程。2.開發(fā)流程看一眼這幅圖最上面一行的內(nèi)容就能發(fā)現(xiàn),效率和監(jiān)視變動是開發(fā)流程的重點(diǎn),而在發(fā)布流程中則毫不重要,甚至還可能成為干擾。你可能還會注意到,這兩個(gè)流程都構(gòu)建出了應(yīng)用,不過開發(fā)流程得到的應(yīng)用是針對持續(xù)開發(fā)的,我們在第3章會詳述這一點(diǎn)。3.發(fā)布流程在發(fā)布流程中,我們關(guān)注的是性能優(yōu)化,以及構(gòu)建整體測試良好的應(yīng)用。和開發(fā)流程稍有不同,這個(gè)流程執(zhí)行的任務(wù)重視的是減少應(yīng)用占用的字節(jié)量。4.部署流程部署流程完全不構(gòu)建應(yīng)用,而是直接使用前兩個(gè)流程準(zhǔn)備好的構(gòu)建版本,將其傳到主機(jī)環(huán)境中。第4章會詳細(xì)說明部署流程。圖2-4構(gòu)建和部署流程中關(guān)注的重點(diǎn)有所不同任何合理的構(gòu)建流程都要自動執(zhí)行其中的每一步,否則就無法提升效率、減少出錯(cuò)的可能性。開發(fā)時(shí),我們在文本編輯器和瀏覽器之間來回切換,不用自己執(zhí)行構(gòu)建過程。這叫作持續(xù)開發(fā),因?yàn)檫@不需要打開shell,輸入命令編譯應(yīng)用。第3章會教你如何使用文件監(jiān)視功能和其他機(jī)制實(shí)現(xiàn)持續(xù)開發(fā)。部署應(yīng)用的過程應(yīng)該和構(gòu)建流程分開,但也要自動化,一步就能構(gòu)建并部署應(yīng)用。類似地,伺服應(yīng)用這一步也應(yīng)該嚴(yán)格和構(gòu)建過程分開。下一節(jié)我們會詳細(xì)說明如何使用Grunt執(zhí)行構(gòu)建任務(wù)。具體而言,我們會先從預(yù)處理任務(wù)開始,例如把LESS文件編譯成CSS文件,然后再介紹后處理任務(wù),例如打包和簡化,以幫你優(yōu)化和調(diào)整發(fā)布版本。2.2預(yù)處理和靜態(tài)資源優(yōu)化談到開發(fā)Web應(yīng)用,不可避免地要討論預(yù)處理。我們通常會使用瀏覽器原生不支持的語言,因?yàn)檫@些語言提供了CSS(例如廠商前綴)、HTML或JavaScript原本不支持的功能,能避免重復(fù)性的工作。本節(jié)的重點(diǎn)不是教你LESS(一個(gè)CSS預(yù)處理器,下一節(jié)會介紹)或CSS,因?yàn)檫@兩門語言有很多專門的教程。本節(jié)的重點(diǎn)是讓你了解使用預(yù)處理語言的巨大好處。預(yù)處理和CSS無關(guān)。預(yù)處理器能把使用一門語言編寫的代碼轉(zhuǎn)換成多種目標(biāo)語言。例如,強(qiáng)大且富有表現(xiàn)力的LESS語言在構(gòu)建時(shí)能轉(zhuǎn)換成純正的CSS。不同人使用預(yù)處理器的目的各異,但基本上可分這么幾類:為了提升效率、減少重復(fù)或使用更舒適的句法。后處理任務(wù),例如簡化和打包,基本上是為了優(yōu)化發(fā)布版本,但這些任務(wù)和預(yù)處理任務(wù)聯(lián)系緊密,所以這一節(jié)也會介紹到它。我們首先介紹使用LESS做預(yù)處理,然后介紹Grunt用來匹配文件路徑的通配模式,最后介紹打包和簡化,它們能優(yōu)化應(yīng)用的性能,供用戶使用。讀完本節(jié)之后,你會更加理解如何使用更合適的語言預(yù)處理靜態(tài)資源,以及如何對靜態(tài)資源作后處理,以提升性能,增強(qiáng)用戶體驗(yàn)。2.2.1詳述預(yù)處理現(xiàn)今,在Web開發(fā)中使用語言預(yù)處理器是相當(dāng)普遍的現(xiàn)象。除非過去十年你隱居了,否則你應(yīng)該知道預(yù)處理器能幫助我們編寫更簡潔的代碼,就像第1章介紹的lint程序一樣。不過我們要多做些事情才能讓預(yù)處理器發(fā)揮作用。說白了,如果使用能轉(zhuǎn)換成其他語言的語言編寫代碼,要增加預(yù)處理這一步,它用來轉(zhuǎn)換代碼。你不想使用目標(biāo)語言編寫代碼可能有這幾個(gè)原因:目標(biāo)語言太啰嗦、太容易出錯(cuò),或者你就是不喜歡那門語言。此時(shí),這些高級語言就發(fā)揮作用了,它們能讓代碼簡潔明了。不過使用這些高級語言編寫代碼也要付出代價(jià):瀏覽器不理解這些語言。因此,在前端開發(fā)中最常見的任務(wù)之一就是把使用高級語言編寫的代碼編譯成瀏覽器能理解的代碼,即JavaScript和CSS樣式。有時(shí)預(yù)處理器還能提供Web的原生語言(HTML、CSS和JavaScript)沒有的實(shí)用功能。例如,有些CSS預(yù)處理器提供了必要的工具,我們不必再為每種瀏覽器編寫專用的樣式。預(yù)處理語言能消除瀏覽器之間的差異,提高我們的效率,讓工作變得更有趣。1.LESS:簡約而不簡單下面以LESS為例介紹預(yù)處理器。LESS是一門強(qiáng)大的語言,是CSS的一個(gè)變種。它是遵從應(yīng)用設(shè)計(jì)的DRY(Don'tRepeatYourself,不要自我重復(fù))原則而設(shè)計(jì),寫出的代碼重復(fù)性較少。使用純粹的CSS往往要不斷自我重復(fù),為所有的廠商前綴指定相同的值,盡量讓應(yīng)用的樣式支持更多的瀏覽器。我們以CSS屬性border-radius為例來說明這個(gè)問題。這個(gè)屬性的作用是為元素加上圓角樣式,使用純粹的CSS要像下述代碼清單這樣編寫樣式。代碼清單2.2使用純粹的CSS實(shí)現(xiàn)圓角樣式

.slightly-rounded{

-webkit-border-radius:2px;<需要使用“廠商前綴”才能讓某些瀏覽器應(yīng)用特定的樣式。

-moz-border-radius:2px;

border-radius:2px;

background-clip:padding-box;<避免背景溢出圓角。

}

.very-rounded{<多次實(shí)現(xiàn)圓角樣式時(shí)問題更嚴(yán)重。

-webkit-border-radius:16px;

-moz-border-radius:16px;

border-radius:16px;

background-clip:padding-box;

}

這種樣式如果只編寫一次還行,但對border-radius這樣經(jīng)常使用的屬性來說,使用純粹的CSS很快就會讓人無法忍受。但使用LESS編寫就會變得更容易,而且代碼也易于閱讀和維護(hù)。針對這種情況,我們可以編寫一個(gè)能重用的.border-radius函數(shù),把代碼改成下述代碼清單這樣。代碼清單2.3使用LESS實(shí)現(xiàn)圓角樣式

.border-radius(@value){<這是可重用的函數(shù),在LESS的俚語中,這叫“混入”。

-webkit-border-radius:@value;

-moz-border-radius:@value;

border-radius:@value;

background-clip:padding-box;

}

.slightly-rounded{

.border-radius(2px);<使用這個(gè)函數(shù),傳入半徑大小。

}

.very-rounded{

.border-radius(16px);<再次使用這個(gè)函數(shù),多次設(shè)定border-radius屬性。

}

LESS及類似的工具就是通過讓你重用CSS代碼片段的方式來提升效率。2.LESS能讓你事半功倍需要在多處使用border-radius屬性時(shí),你便會發(fā)現(xiàn)不要什么都寫兩次(WritingEverythingTwice,簡稱WET)的好處。遵守DRY原則,每次需要實(shí)現(xiàn)圓角樣式時(shí)就不用列出所有四個(gè)屬性,只需重用.border-radius這個(gè)LESS混入即可。預(yù)處理在精益開發(fā)流程中扮演著重要的角色:要使用這個(gè)規(guī)則時(shí)不用每次都編寫所有廠商前綴,而且在一處就能修改所有前綴,讓代碼更易于維護(hù)。LESS能做的不止這些,它還能把靜態(tài)規(guī)則和設(shè)定這些規(guī)則的值清楚地分開。在CSS樣式表中經(jīng)常能看到類似下面的代碼:

a{

background-color:#FFC;

}

blockquote{

background-color:#333;

color:#FFC;

}

LESS允許我們使用變量,這樣就不用到處復(fù)制粘貼顏色代碼了。為變量起個(gè)恰當(dāng)?shù)拿Q,在瀏覽樣式表時(shí)就能輕易識別使用的是什么顏色。3.使用LESS變量使用LESS可以把顏色賦值給變量,避免潛在的問題,例如在一個(gè)地方修改了顏色,但忘了修改使用這個(gè)顏色的其他地方。使用變量還能把顏色和設(shè)計(jì)中的其他可變參數(shù)統(tǒng)一放在一個(gè)地方。下述代碼展示了如何使用LESS變量:

@yellowish:#FFC;<聲明變量有助于定位和替換顏色,還能避免出現(xiàn)問題。

a{

background-color:@yellowish;<直接引用變量就能使用了。

}

blockquote{

background-color:#333;

color:@yellowish;

}

就像我在2.2節(jié)開頭提到的一樣,這樣做能讓代碼遵守DRY原則。在這里使用DRY原則特別有用,因?yàn)檫@樣一來,我們就不用復(fù)制粘貼顏色代碼了,因此能避免輸入錯(cuò)誤導(dǎo)致的問題。除此之外,LESS等語言還提供了生成其他顏色的函數(shù),例如生成更深的綠色或更透明的白色等有趣的顏色算法。現(xiàn)在我們轉(zhuǎn)變一下話題,介紹如何使用Grunt任務(wù)把LESS代碼編譯成CSS。2.2.2處理LESS本章前面說過,Grunt任務(wù)由兩個(gè)不同的部分組成——任務(wù)和配置:任務(wù)是最重要的,這是運(yùn)行構(gòu)建時(shí)Grunt要執(zhí)行的代碼,一般都能找到符合需求的插件;配置是傳給grunt.initConfig方法的對象,幾乎所有Grunt任務(wù)都需要配置。在閱讀本章剩余內(nèi)容的過程中,你會看到如何配置各個(gè)任務(wù)。為了使用Grunt把LESS文件編譯成能直接伺服的CSS,我們要使用grunt-contrib-less包。還記得怎么安裝JSHint插件嗎?這個(gè)包的安裝方法與之相同,只不過是包名變了,因?yàn)槲覀円褂玫牟寮兞?。在終端里執(zhí)行下述命令安裝grunt-contrib-less包:

npminstallgrunt-contrib-less--save-dev

這個(gè)插件提供了一個(gè)任務(wù),名為less,在Gruntfile.js文件中加載這個(gè)任務(wù)的方式如下:

grunt.loadNpmTasks('grunt-contrib-less');

從現(xiàn)在開始,為了行文簡潔,我會省略各個(gè)示例中npminstall這一步和grunt.loadNpmTasks這部分代碼。不過你仍然要執(zhí)行npminstall獲取包,并在Gruntfile.js文件中加載插件。任何時(shí)候你都可以查看本書的配套源碼,那里有完整的代碼。設(shè)置這個(gè)構(gòu)建任務(wù)的方法很簡單:把屬性名指定為輸出文件的名稱,把對應(yīng)的值設(shè)為用來生成CSS文件的源文件的路徑。這個(gè)例子在本書配套源碼的ch02/02_less-task文件夾中。

grunt.initConfig({

less:{

compile:{

files:{

'build/css/compiled.css':'public/css/layout.less'

}

}

}

});

執(zhí)行任務(wù)的最后一步是在命令行中調(diào)用grunt。在本例中,我們需要在終端里執(zhí)行g(shù)runtless命令。我們通常建議你明確指定目標(biāo)。本例中,指定目標(biāo)的方式是gruntless:compile。如果不指定目標(biāo)名,所有目標(biāo)都會被執(zhí)行。Grunt的配置方式具有一致性在往下看之前,我要提一個(gè)在使用Grunt的過程中會讓你感到舒服的細(xì)節(jié)。不同任務(wù)之間的配置方式不會有太大差異,尤其是那些由Grunt團(tuán)隊(duì)開發(fā)的任務(wù)。即便是npm中的任務(wù),只要有配置,方式也差不多。在閱讀本章的過程中你會發(fā)現(xiàn),我介紹的各個(gè)任務(wù),就算提供了大量操作方式,其配置的方式也都類似。使用Grunt執(zhí)行構(gòu)建目標(biāo)less:compile,會把layout.less文件編譯成compiled.css文件。輸入文件不僅可以是單個(gè)文件,還可以是一組文件。此時(shí),得到的是一個(gè)打包文件,包含編譯所有LESS輸入文件后得到的CSS。我們稍后就會詳細(xì)介紹打包。下述代碼清單是個(gè)示例。代碼清單2.4聲明一組輸入文件

grunt.initConfig({

less:{

compile:{

files:{

'build/css/compiled.css':[

'public/css/layout.less',

'public/css/components.less',

'public/css/views/foo.less',

'public/css/views/bar.less'

]

}

}

}

});

挨個(gè)列出每個(gè)文件不是不可以,但是如果有上百個(gè)文件,最好使用通配模式。下面就介紹這個(gè)模式。精通通配模式我們可以使用Grunt提供的通配功能,進(jìn)一步改進(jìn)上述代碼中的配置。通配6是一種文件路徑匹配機(jī)制,可以幫你使用文件路徑模式來包含或排除文件。這個(gè)模式特別有用,因?yàn)槲覀儾挥檬謩恿谐鲮o態(tài)資源文件夾中的所有文件,這樣能避免一些常見的錯(cuò)誤,例如,忘記把新樣式表添加到列表中。6Grunt網(wǎng)站很好地說明了通配的用法,詳情請?jiān)L問\hhttp://bevacqua.io/bf/globbing。如果想讓構(gòu)建任務(wù)排除某些文件,例如第三方提供的文件,也用得到通配模式。下述代碼展示了一些有用的通配模式:

[

'p

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論