您的位置:首頁 >  新聞中心 > 云通訊公告
  云通訊公告
 

當(dāng)我們談?wù)撉岸斯こ袒瘯r(shí),我們在談?wù)撌裁矗?/h3>來源:原創(chuàng)    時(shí)間:2017-11-13    瀏覽:0 次


在日常前端開發(fā)工作中,你是否困惑于 npm、Yarn、Browserify、Gulp、Grunt、Webpack 等工具而不清楚他們有什么區(qū)別?是否覺得手頭的項(xiàng)目環(huán)境過于復(fù)雜,各種配置已經(jīng)超出了可控的范圍,以至于都不敢更改它?你是否覺得工程模塊太多構(gòu)建太慢,點(diǎn)擊發(fā)布按鈕要等幾分鐘才能看到效果?
 
如果答案是肯定的,那么你可能遇到了有關(guān)前端工程化的問題。在這篇文章以及接下來的課程中,我會(huì)去解釋前端工程化,并且嘗試去解決上面的那些問題。
前端工程化是什么?
這是個(gè)很大的概念,但是在我們的日常開發(fā)中又很常見。當(dāng)我們對一個(gè)工程進(jìn)行設(shè)計(jì)并把它拆分成各個(gè)組件和模塊時(shí),我們是在做工程化;當(dāng)我們用 Webpack 構(gòu)建項(xiàng)目,配置好各個(gè)環(huán)境的打包配置時(shí),我們是在做工程化;當(dāng)我們?yōu)轫?xiàng)目添加了 ESLint,并在每次提交之前自動(dòng)檢查代碼質(zhì)量時(shí),我們是在做工程化。
 
如果要用一句話來概括,在我的理解中前端工程化是把前端開發(fā)工作帶入到更加系統(tǒng)和規(guī)范體系的一系列過程。這個(gè)過程會(huì)包括源代碼的預(yù)編譯、模塊處理、代碼壓縮等構(gòu)建方面的工作。工程化會(huì)盡可能保證開發(fā)者的開發(fā)體驗(yàn)更加友好,保證源代碼的質(zhì)量以及依賴的完整性。工程化也會(huì)盡可能高效地將構(gòu)建完成后的代碼送達(dá)給客戶端,來追求更加良好的用戶體驗(yàn)。所有這些都屬于工程化。
 
為什么要了解前端工程化?
如果一個(gè)開發(fā)者想做現(xiàn)代 JavaScirpt 應(yīng)用,那么他就有理由了解前端工程化,因?yàn)橥ㄟ^工程化可以提高代碼質(zhì)量,降低開發(fā)成本。
 
會(huì)有人質(zhì)疑說現(xiàn)在的前端開發(fā)是不是過于復(fù)雜了?一個(gè)沒有用過 npm 和 Webpack 的開發(fā)者想寫一個(gè) Hello World 可能都需要學(xué)習(xí)和配置半天。在以前沒有所謂“工程化”的時(shí)候,還不是照樣寫代碼和發(fā)布代碼來實(shí)現(xiàn)用戶需求。
 
其實(shí)我們可以打個(gè)比方,把發(fā)送給客戶端的頁面理解成呈獻(xiàn)給用戶的一道菜。前端開發(fā)者是廚師,而工程化可以使開發(fā)者遵循正確的做菜流程——洗菜、切菜、炒菜,而不是將生的東西直接放到用戶面前。
 
在 Web 技術(shù)剛開始的時(shí)候,還沒有前端工程化這樣一個(gè)東西。人們只是簡單地把 HTML、CSS 和 JavaScript 直接混在一起丟到用戶。而就如人類對于食物的追求在不斷進(jìn)步一樣,雖然在最初級(jí)的階段需求只是能填飽肚子,但慢慢地人們開始追求食物的質(zhì)量。對于前端來說也是一樣,用戶的需求從最開始簡單的頁面在向復(fù)雜的應(yīng)用發(fā)展。前端需要做的事情更多,同時(shí)也要追求更友好的用戶體驗(yàn)。
 
對于廚師來說,要想做出高質(zhì)量的食物需要順手的工具以及正確的烹飪方法。對于開發(fā)者來說,要想輸出高質(zhì)量的代碼也同樣需要工程化來輔助。作為一個(gè)前端工程師,光會(huì)寫代碼是不夠的,最多只是提供了好的原材料。要想將代碼轉(zhuǎn)化為高質(zhì)量的產(chǎn)出提供給用戶,必須要了解工程化。
另外,工程化也是為開發(fā)者服務(wù)的。通過預(yù)編譯語言、模塊熱加載等技術(shù)可以提升開發(fā)效率,而利用自動(dòng)化測試、lint 工具等可以保證代碼的功能和質(zhì)量。工程化可以有效降低開發(fā)成本,誰不想省下埋頭 debug 的時(shí)間去做更有趣的事呢。
 
從工程化出現(xiàn)之前說起
前端工程化不是憑空出現(xiàn)的,一定是為了解決當(dāng)時(shí)的一些問題而出現(xiàn)的,讓我們先簡單回溯一下前端開發(fā)的歷史。
 
曾經(jīng)的 Web 開發(fā)不同于現(xiàn)在,頁面功能比較簡單。開發(fā)者想添加一段邏輯,最直接的做法就是在 HTML 中插入一個(gè) script 標(biāo)簽,然后直接在里面書寫代碼。聽上去似乎十分的原始,但在當(dāng)時(shí)確實(shí)很多都是這樣做的,而且在業(yè)務(wù)需求簡單的時(shí)候這也確實(shí)是最直接了當(dāng)?shù)膶?shí)現(xiàn)方式。但是這種原始的做法也必然有它的缺點(diǎn),主要有以下兩個(gè)方面:
全局作用域的污染。由于在每個(gè) script 標(biāo)簽下頂層作用域即全局作用域,直接進(jìn)行變量和函數(shù)聲明會(huì)造成全局命名空間污染。假如一個(gè)頁面有多個(gè) script 標(biāo)簽,它們之間很有可能發(fā)生命名沖突。
 
代碼重用性差。在一個(gè)多頁面應(yīng)用的場景下,經(jīng)常會(huì)有一些邏輯是這些頁面之間共有的,此時(shí)我們不得不將這些代碼復(fù)制粘貼到各個(gè)頁面中。而當(dāng)此處邏輯改動(dòng)的時(shí)候我們也需要去更新所有頁面的代碼,造成很多額外的成本。
 
后來逐漸有一些針對這些問題的解決辦法。首先,可以將 HTML 中內(nèi)聯(lián)的 JavaScript 提取出來成為單獨(dú)的 JavaScript 文件。比如說一些頁面公有的邏輯可以放在類似 common.js 中來被各個(gè)頁面引用,這可以解決各個(gè)頁面之間重用的問題。至于全局作用域污染的問題,則可以使用立即執(zhí)行函數(shù)表達(dá)式將它包起來( IIFE ),只把接口暴露到全局上。
 
(function() {
  // 通過立即執(zhí)行函數(shù)表達(dá)式將作用域隔離
  var foo = 'bar';})();
看上去問題已經(jīng)得到了解決,然而隨著頁面邏輯的復(fù)雜度增加開發(fā)者又面臨了新的問題:
頁面 JavaScript 文件的引用順序。由于 HTML 頁面引用和處理 JavaScript 文件只能是順序的(不考慮 async 等),因此頁面的 JavaScript 之間依賴關(guān)系也必須是順序的。而我們知道一個(gè)大型工程內(nèi)部的模塊依賴關(guān)系通常是樹狀的(比如 index.js 依賴 a、b、c 三個(gè)模塊,而 a、b、c 又有各自的依賴),簡單的順序依賴關(guān)系無法滿足需求。例如在 jQuery 最流行的時(shí)期,jQuery 本身以及其相關(guān)的插件之間有著各種各樣的依賴關(guān)系,有些庫可能自身包含 jQuery,不同的插件可能需要不同的 jQuery 版本,這些問題都不是簡單的順序依賴關(guān)系可以解決的。
 
頁面引用的 JavaScript 文件的長度與數(shù)量如何權(quán)衡。隨著頁面邏輯的增加,工程中的 JavaScript 文件越來越長,也越來越難以維護(hù)。一個(gè)頁面的單個(gè) JavaScript 文件可能有數(shù)千行甚至上萬行。而如果按照功能來把頁面邏輯切割成一個(gè)個(gè)小的 JavaScript 文件,則最終會(huì)走到另一個(gè)極端——頁面請求過多。我們知道每個(gè) HTTP 請求都是需要連接時(shí)間的,對于小模塊而言每一個(gè)都要單獨(dú)建立連接總歸得不償失,必然會(huì)導(dǎo)致頁面渲染速度的下降。
 
上面所說的這些問題都屬于前端開發(fā)的“原始時(shí)期”,那時(shí)候還沒有工程化這種說法。然而逐漸暴露出來的問題已經(jīng)讓人們覺得不能再簡單粗暴地采用如此原始的開發(fā)方式,模塊化是第一步。
 
走向正軌的第一步——模塊化
 
 
一個(gè)設(shè)計(jì)良好的系統(tǒng)應(yīng)該是模塊化的。一個(gè)最簡單的原因,在一個(gè)模塊化的系統(tǒng)中,當(dāng)外界的需求亦或環(huán)境變化的時(shí)候,開發(fā)者可以更快地將問題定位到相應(yīng)的模塊,而不必面對糾纏在一起的邏輯不知如何下手。模塊化可以使系統(tǒng)具備更強(qiáng)的可維護(hù)性。
 
被封裝良好的模塊應(yīng)該具備特定且單一的功能,對外界只提供接口,而將具體實(shí)現(xiàn)封裝在內(nèi)部。Webpack 中有一個(gè)核心的理念——”一切皆模塊”,即 HTML、JavaScript、CSS、圖片等等都是模塊,在后面的文章中會(huì)展開講。
 
雖然模塊化很重要,但是 JavaScript 誕生的時(shí)候并不具備模塊這一特性,這主要是因?yàn)樵缙?Web 中的腳本大都比較簡單,在設(shè)計(jì)之初只是為了實(shí)現(xiàn) Web 上一些簡單的功能。一直到 CommonJS 以及 AMD 的出現(xiàn),為前端定義了模塊的標(biāo)準(zhǔn)。也有了實(shí)現(xiàn)這些模塊化的庫,比如 RequireJS 以及 Browserify??梢宰岄_發(fā)者將自己工程中的代碼按模塊進(jìn)行劃分,模塊之間也不再僅僅是簡單的順序依賴關(guān)系。
 
另外在將代碼提供給客戶端之前,開發(fā)者可以通過 Browserify、Webpack 這些工具將工程代碼進(jìn)行打包,把所有依賴模塊打包為單一的 JavaScript 文件。這樣一來,對于開發(fā)者而言開發(fā)體驗(yàn)更加友好,因?yàn)殚_發(fā)中每次需要關(guān)注的僅僅是單個(gè)模塊,而不是堆放在一起的上千行 JavaScript 文件;而對于客戶端來說則只用接受單一的打包產(chǎn)物,解決了文件數(shù)量過多導(dǎo)致 HTTP 請求耗時(shí)長的問題。解決模塊之間的依賴,并根據(jù)依賴樹進(jìn)行打包,是工程化解決的最基本的問題之一。
 
提升前端開發(fā)效率——預(yù)編譯語言
上面說的只是 JavaScript 的模塊化,那么我們很自然地就想到 CSS 的模塊化。然而因 CSS 本身 @import 的性能問題,一般都是要通過 SASS、LESS 等預(yù)編譯語言去實(shí)現(xiàn)其模塊化。
 
例如在 SASS 中,通過 @import 語句我們可以導(dǎo)入其它模塊。SASS 的編譯器會(huì)處理模塊之間的依賴,并最終將代碼打包在一起生成 CSS。
 
在實(shí)際開發(fā)中,我們很多時(shí)候都會(huì)使用預(yù)編譯語言來進(jìn)行編碼工作,然后經(jīng)過 Webpack 等工具的構(gòu)建將其編譯為實(shí)際頁面中的代碼。使用預(yù)編譯語言的主要目的是為了實(shí)現(xiàn) HTML、CSS、JavaScript 不具備的特性。比如說上面提到的最常見的 SASS,它是 CSS 的預(yù)編譯語言,通過它開發(fā)者可以使用模塊、定義變量、編寫嵌套規(guī)則等等來提高開發(fā)效率。
 
除了 CSS 的預(yù)編譯語言,HTML 對應(yīng)的有 HAML,JavaScript 對應(yīng)的有 Coffee 等等??傮w而言這些預(yù)編譯語言的目的就是使開發(fā)體驗(yàn)更友好,開發(fā)者可以更高效地編寫和維護(hù)代碼。
 
當(dāng)然現(xiàn)在預(yù)編譯已經(jīng)不僅僅是這些,我們還可以使用 Babel 預(yù)編譯 JavaScript 來實(shí)現(xiàn)新的 ES 特性,以及使用 TypeScript 去做類型檢查等,在預(yù)編譯這里還可以有更多的想象力。
 
現(xiàn)代 JavaScript 應(yīng)用必不可少的部分——包管理器
和 Java、C++ 這些語言不同,JavaScript 沒有強(qiáng)大的標(biāo)準(zhǔn)庫。許多常用的功能,比如日期處理、URL 處理、異步流程控制等往往都需要手工去編寫,而采用外部已有的開源框架庫或許是節(jié)約成本的最好辦法。
 
Bower 作為包管理器最早進(jìn)入人們的視野,大部門前端框架庫也都提供了通過 Bower 安裝的方式。通過它你可以獲取項(xiàng)目需要的依賴,并且通過打包工具和業(yè)務(wù)代碼打包到一起。
 
雖然現(xiàn)在當(dāng)我們說包管理器可能首先想到的是 npm,但它最開始其實(shí)主要是為 Node.js 服務(wù)的,而人們逐漸意識(shí)到它也可以用于前端并且真正擔(dān)當(dāng)起前端包管理器的大任也就是近兩年的時(shí)間。在現(xiàn)階段來說 npm 已經(jīng)超越 Bower 成為開發(fā)者首選的包管理器,而 Yarn 作為 Facebook 出品的新生代也不過是管理 node_modules 的另一個(gè)工具罷了,與 npm 并沒有什么本質(zhì)上的區(qū)別。在后續(xù)的相關(guān)文章中會(huì)更詳細(xì)地介紹包管理器常見的問題和處理辦法。
 
讓機(jī)器做更多的事——構(gòu)建流程管理
隨著工程化的發(fā)展,交給構(gòu)建過程的任務(wù)也越來越多,并且在不同環(huán)境下需要對任務(wù)進(jìn)行區(qū)分。比如對于一個(gè)前端工程來說,除了需要預(yù)編譯各種類型的文件、資源打包之外,本地環(huán)境下還要生成 source-map、配置模塊熱加載等等便于調(diào)試代碼;而到了生產(chǎn)環(huán)境下則要對資源進(jìn)行壓縮,生成版本號(hào)等等。
 
因此對于開發(fā)者來說需要將這些任務(wù)統(tǒng)一起來進(jìn)行管理,也就有了 Gulp 和 Grunt 等構(gòu)建流程管理工具。這類工具的出現(xiàn)使得構(gòu)建變得更加傻瓜化,通過項(xiàng)目中的一些配置,開發(fā)者可以使用簡單的一行命令啟動(dòng)本地開發(fā)環(huán)境或者構(gòu)建和發(fā)布整個(gè)工程。
 
相比于 Gulp 和 Grunt,Webpack 出現(xiàn)的更晚。它和前兩者的核心定位其實(shí)不太一樣,Webpack 本身只是作為一個(gè)模塊打包工具的姿態(tài)出現(xiàn)的,但是利用一些相關(guān)工具和插件我們也可以完成整個(gè)工程的構(gòu)建。Webpack 的“一切皆模塊”以及“按需加載”兩大特性使得它更好地服務(wù)于工程化。在后面的文章中會(huì)有很多關(guān)于 Webpack 的部分,包括 Webpack 的打包原理及優(yōu)化、從零搭建起一個(gè)開發(fā)環(huán)境等,會(huì)詳細(xì)講解 Webpack 的使用以及其最新的特性。
 
擺脫冗長的等待——構(gòu)建流程優(yōu)化
 
現(xiàn)在當(dāng)我們討論工程化,效率和優(yōu)化是出現(xiàn)得越來越頻繁的詞。當(dāng)把工程化的各種功能都實(shí)現(xiàn)在我們的工程中之后,卻發(fā)現(xiàn)完整地構(gòu)建一遍需要好幾分鐘甚至更長。工程本身的越發(fā)龐大以及越來越多的構(gòu)建任務(wù)使得耗時(shí)越來越久,此時(shí)是不是又懷念起了在 HTML 里寫內(nèi)聯(lián)腳本的日子。
 
除了構(gòu)建速度的問題,推給用戶端的資源體積過大也是問題。需要針對項(xiàng)目的特點(diǎn)采用按需加載、異步加載、長效緩存等等策略。在后面的文章中會(huì)單獨(dú)有一篇來講構(gòu)建方面的優(yōu)化。
 
關(guān)于本課程
在這個(gè)系列文章中,我會(huì)將工程化相關(guān)的原理和實(shí)踐結(jié)合起來,穿插進(jìn)行。首先是模塊化的相關(guān)內(nèi)容,后面則會(huì)介紹 Webpack、包管理器、構(gòu)建優(yōu)化等等。在實(shí)踐部分會(huì)有實(shí)際的工程演示,包括源代碼和配置都會(huì)給出 Github 地址,方便大家運(yùn)行。
 
在這個(gè)系列文章的最后會(huì)介紹在去哪兒網(wǎng)前端開發(fā)流程中,大型項(xiàng)目是如何做工程化相關(guān)工作的,我們曾經(jīng)踩過的坑以及解決方案。最后歡迎大家在讀者圈多多跟我交流。


免费视频观无码一区,国内精品一区二区无码,99精品无码视频在线播放,ā片国产在线播放