前端構建工具,是幹嘛的?

這篇文章通過對比兩個在用構建工具,來幫助大家理解什麼是構建工具,構建工具到底做了什麼事情?從而幫助我們在今後開發使用過程中更好的理解構建流程,調試構建結果,甚至通過配置操縱構建結果,優化性能。

1、什麼是構建工具

文章的開始,我們可以先從宏觀的角度上去試著解釋一下什麼是構建工具,當文章結束的時候我們再回來驗證看看——我們在沒有自己研究過構建工具的情況下對構建工具的理解對不對,深不深。

所謂好看的皮囊千篇一律,有趣的靈魂萬里挑一

套用這句經典可以造個句

前端的構建工具千篇一律,高效的構建原理萬里挑一

從這句「名言」可以很好的理解構建工具的生態,市面上的構建工具很多——grunt,gulp,webpack,parcel,fis等等。如果不了解各自的內部構造,他們對我們來說就都叫——構建工具(千篇一律),但是當你真正去研究其背後的構建流程和原理,你會發現每個構建工具的流程原理不盡相同,有的配置簡單、有的配置複雜、有的專註流程、有的專註結果、有的儘力在優化構建流程、有的在儘力優化打包結果和載入速度等等等等。你會發現「高效」的構建工具萬里挑一。

怎麼去界定一個構建工具是高效的?是最新的構建工具就是最好的嗎?還是最牛X的包就是一定要用的?

對於你來說,對你的當前項目效果最優的就是最好的,配置簡單,結果很優,那就是對你來說是」高效「的構建工具。

作為前端開發者,如果你只是個用過但是沒有仔細研究過構建工具的人,讓你去定義構建工具,你會怎麼說?我們可以從這幾個方面來拼湊解釋這個概念:

什麼時候使用或想到構建工具?

  • 推代碼到rd機器我要用;
  • 我代碼less怎麼還沒編譯成css;
  • 我這塊js怎麼沒有合併在一起;
  • 代碼塊太大了,載入很慢,應該要壓縮一下;
  • 代碼構建好慢啊,那個項目比較快啊;
  • 這個好厲害啊,我這邊改完代碼,頁面就自動刷新了,真方便;

等等...

好,這個時候我們回頭看我們說的幾個場景,當你試著回答這個場景的時候,你對構建宏觀的解釋就有了:

構建工具可以推送代碼到伺服器,構建工具可以編譯解釋文件成瀏覽器可以識別的樣子,構建工具可以構建文件關係,構建工具可以優化代碼,構建工具本身可以配置構建速度,構建工具可以簡化我們的開發。

綜上我們可以對構建工具下一個理解性的定義:

構建工具是可以幫助開發者管理本地源文件,優化開發流程,降低開發複雜度,使開發者更加專註在業務邏輯開發上的一種工具。

接下來我們通過兩種不同的構建工具來仔細研究一下「千篇一律」的構建工具以及「萬里挑一」的構建效率,等我們研究完了以後回來驗證我們的理解定義是否成立。

2、fis3

相信大部分同學都會比較好奇,fis3到底是哪位?

確實現在fis3普及度不太高,尤其當webpack,parcel等一系列優秀的構建工具出現以後,fis3更是很快的被替代。之所以先拿這個構建工具來介紹是因為我們公司在一直用fis3構建項目,其實本身是個很優秀的構建工具,因為經常用,了解的更多一些,所有首先拿來介紹一下,其實大家對於不了解的構建工具更應該感興趣,看看她跟我們常常用的構建工具有什麼不同,也好幫助我們理解構建工具本質。

介紹一下fis3的背景,fis3是百度前端團隊研發的一款構建工具,百度系的公司前端都在延用使用這個構建工具。

先給大家復現一個我的工作場景:

前端開發一個需求項目完畢,rd通知我聯調,我需要將我本地的代碼推送到rd環境里,這樣才能配合他的環境調試介面,此時,我需要使用到構建工具。

表面上看我只是把本地代碼推送到rd機器下,構建工具是推代碼的作用,可僅僅是推送代碼這麼簡單嗎?這裡面還有沒有別的工作,這個像黑盒一樣的構建工具對於開發者來說可以控制嗎?那推代碼這些文件怎麼集成的?推到rd環境里又是放到哪裡了?怎麼就知道這麼放rd就自動打開我推送代碼的頁面了?

別急!!!這些問題都有答案!這些問題確實都是構建工具在做的。

看一條命令:

// media是rd機器配置名稱fis3 release media -cw

這是當rd告訴我聯調的時候我執行的一行命令,這背後做了什麼事情?看下圖

一條命令引發的故事

1、檢查參數

這個很好理解,為了保證命令的正確執行,命令本身不能輸入錯誤。簡單解釋一下上面命令的含義:

  • fis3 :命令標記位,找到fis3工具
  • release:推送代碼
  • media:這是個參數,背後是配置某個RD機器
  • -cw:-clean & -watch 很好理解 每次推送情況緩存而且實現實時監聽的效果

fis3規定了命令執行的排列組合以及參數的格式,如果輸入錯誤那麼停止執行。

2、初始化

這個階段是為了後期代碼的編譯打包做前期準備,從命令里的參數來初始化環境。例如-c則在編譯前清空緩存。

3、開啟release

這部分較為重要且複雜,完成文件的編譯和打包工作,也是整個構建工具的最核心部分,我儘力解釋的有趣一些,也請耐心觀看。

現在要跟大家講個故事,你的故事。

現如今A市對於麵包的需求量驟增,因此需要麵包加工廠加工批量生產並送往A市,供當地百姓食用。

麵包加工廠要做麵包需要什麼?麵粉、水、牛奶和雞蛋等等原材料,還要製作師傅,還要烘烤箱,還要打包裝箱機器,還要發送汽車。

你的故事,故事主人公你在哪兒呢?你是一個還未進入烤箱的生的麵包。。。

試想你是一個由各種原材料組成並且被製作師傅按各種比例製作在一起的一塊生麵包,接下來你要幹嘛?你還不是A市人能夠食用的麵包:

那你第一步是不是需要烤熟;

接下來要封裝起來並且裝成箱;

然後通過工廠的物流送到A市即可完成任務。

其實上面的故事就是很接地氣兒的類比說明了開啟release整個流程,我幫大家對號入座。

原材料就是我們的基礎語言html、css和js等;

製作師傅就是前端開發人員,把這些組合在一起完成需要的需求功能;

每一個你就是本地的源文件;

烤箱就是編譯過程;

機器封裝成箱就是打包過程;

物流就是推送能力;

這樣解釋大家應該可以更快的明白這塊做什麼的。

那烤箱將生的麵包(本地源文件)烤成A市可以食用的熟麵包(瀏覽器可以識別的文件)都發生了什麼呢?當然我不能解釋食物的變化過程?那編譯都做了什麼事情呢?

  • init:lint,代碼檢測
  • parse:sass less es6 ts等編譯轉化
  • preprocessor:hook
  • standard:標準化html,例如頁面引用的文件路徑等
  • postprocessor:hook
  • optimizer:文件壓縮等
  • save:保存編譯完的文件到緩存區

當所有的麵包(源文件)都烘烤(編譯)完畢以後,需要裝箱,

裝箱也有這樣一個過程:

  • prepackager:hook
  • packager:編譯文件的打包並記錄文件的關聯關係
  • spriter:圖片壓縮
  • postpackager:hook

上面的所有hook都是可以人為控制的部分,我們可以在這些階段分別增加我們想要做的一些操作,從而將結果轉化為我們想要的樣子。(麵包為例的話,烘烤階段我們可以做草莓味的,樣子可愛的麵包,打包裝箱可以通過更好的包裝袋來吸引顧客購買,增加銷量)

到這裡released的部分基本演示完畢,最後看一張核心原理圖,你會看的更明白一些:

fis3原理圖

4、hot reload腳本

這個部分是執行熱載入的腳本文件,為後面開啟熱載入準備。熱載入是頁面的實時熱更新,很重要也方便的功能。

5、deploy

代碼發布,即發送過程,當我們拿到所有的編譯文件需要將文件發送目標環境下,進而完成頁面的訪問載入。發布過程也很簡單,一個http-push插件直接將文件推送到目標伺服器。

6、output配置

這是fis3特有的功能,即output是fis3本地編譯產出的文件夾,而此時這個文件夾不需要在二次構建的時候被檢索到,因此這裡是個優化的步驟,將output放入fis.project.ignore里,保證下次構建文件不考慮這個文件夾。

以上則是通過一行命令來說明fis3構建原理,而fis3本身還有很多其他功能。

  • fis3本身提供了一些api,幫助我們在構建之前定製一些功能,例如制定文件打包合併的去處,指定文件特殊處理,制定文件壓縮等,上面的hook也是在這個配置,這個配置文件叫fis-config.js
  • fis3支持本地起服務開發方式,本地Mock數據

3、webpack

webpack是當下十分流行的一個構建工具,相信很多人都用過,至少是聽說過。說webpack是構建工具並不確切,更確切的說法應該是靜態模塊打包器,在webpack里包(bundle)的概念很重要,另一個重要的概念是依賴關係圖,這組成了這個打包器的重要核心。

webpack打包關係圖

上面圖片展示左邊則是一個依賴關係圖,右邊則是最終的產出bundle。

webpack有四大核心要素,這也是跟我們使用配置webpack時十分密切的四個部分:

  • entry: 配置入口文件,即產生依賴關係圖的入口
  • output:文件的產出位置配置
  • loader:匹配文件的編譯過程
  • plugins:針對整個構建打包流程的插件處理(文件壓縮,dev環境的熱載入)

出入口決定了我們需要將那些文件打包到哪裡去,而loader承接這裡的匹配文件的編譯工作,plugins則是針對整個構建過程的操作,需要什麼功能引入什麼插件。


那webpack的具體工作步驟是什麼呢?

1、查找入口文件

從配置文件里找到entry的配置,進而找到入口文件

2、分析依賴關係

找到入口文件以後,分析這樣一個入口文件依賴那些文件,並且這些依賴的文件可能還有別的依賴文件,從而形成了一個依賴關係圖

3、模塊函數

找到依賴關係中的所有文件,然後把這些文件轉化為模塊函數,目的是為了讓webpack可以調用執行這些文件

4、執行配置loader&plugins

依照開發人員在配置文件里配置的loader和plugins,依次執行相應的功能,在這裡舉兩個例子簡單區分說明一下loader和plugins

// 配置文件Module.exports = { module: { loaders: { {test: /.less$/, loader: "style!css!less|postcss"} } }, plugins: [ new webpack.optimize.UglifyJsPlugin(), new CommonsChunkPlugin({ name: [common, load], minChunks: 2 }) ]}

上面是一段loader和plugins的配置,分別的意思是:loader中找到匹配文件執行less文件編譯過程;plugins中分別執行js文件的壓縮和公共代碼部分的提取壓縮,提升性能。

其實對於上面一個fis3構建工具,編譯less和壓縮是編譯過程的事情,而提取打包則是打包過程的事情,可以這麼理解,loader配置的是編譯過程,而plugins中含有編譯和打包部分,控制的範圍更大一些。

這裡就是完成文件的編譯打包過程。

5、產出

打包完畢的文件可以產出到配置文件的output指定路徑里,在這裡還可以指定產出文件的指紋,防止緩存;同時還會產出一個manifest配置文件,這個文件記錄了打包過程中這些文件的關係。

manifest有兩個意義:這裡存儲配置文件信息,而每次打包都會更新配置文件信息,因此這個manifest都會更新,如果沒有這樣一個獨立的配置文件,而是把信息打包到js里,那麼公共的JS(不用二次打包的Js)必須因為配置文件的更新重新打包,這回增加構建的速度,降低效率;第二個意義則是說明書,在文件頭部引用這個說明文件,從而完成頁面的其他依賴文件的載入。


至此webpack的構建打包工具結束,除此之外webpack在其強大的生態環境下,有非常非常非常多的比較好用的插件和api,webpack著力優化構建速度(devTool)和代碼載入速度和性能優化(比如code splitting,tree shaking等)以及簡化配置,這也是webpack得以迅速發展的原因。

webpack還在很多細節方面都做的很好,比如你可以有多個配置文件,開發環境的配置文件和上線環境的配置文件,這樣我們就可以針對不同的場景特點配置不同的參數:例如開發環境我們可以把打包過程加速,簡化生成map文件的方法(devTool)和壓縮代碼,加快開發也更好debug;而線上生產環境則需要不需要快速打包,而且需要壓縮代碼。

就像fis3一樣,webpack也有本地開發,她是基於nodeJs構建,只需要簡單配置server參數即可:

{ devServer: { // 啟動文件路徑 contentBase: ./public, // 埠號 port: 8080, // 熱載入 inline: true, // 熱更新 hot: true, // 代理 proxy: { rule: { match router } } }}

通過這份簡單配置可以看出,server端的基本能力,支持熱載入、熱更新,支持代理,而且代理方式很多種。

通過以上簡單介紹下面這樣一份webpack的簡單配置應該可以輕鬆解讀:

webpack的基礎部分基本介紹完畢,接下來就是針對自己的項目合理使用插件進行適合的配置,webpack也在著力降低開發配置成本。

不過webpack本身相對於fis3還是沒有推送和mock能力,不過在webpack現如今的生態環境下,這類插件很多,同樣可以補充我們的開發需求,甚至我們可以自己開發一個插件使用。

4、總結

上面把兩個構建工具都介紹完畢,可以簡單做個對比

fis3 vs webpack

可以看到構建工具都有文件編譯和文件打包過程,這是很重要的兩個部分,而本地開發能力像是標配,webpack更為豐富和強大一些,因為普及的關係也必須迫使他們在構建這條路上更加精益求精。目前webpack4 beat版本提出:多場景下更好的性能提升,更好的默認配置。也看得出其發展方向。

最近又有一個新的構建工具parcel,這個構建工具幾乎零配置,但是究竟零配置能否完全滿足所有項目的開發有待考驗。

了解了這麼多的構建工具原理知識我們可以給構建工具下一個更為明確的定義:

構建工具是將本地源文件按照一定的邏輯規範,編譯打包成瀏覽器識別的文件,然後推送到相應伺服器環境下供展示的一種前端工具,工具的目的就是幫助開發者降低開發成本,使開發者更加專註業務邏輯開發本身。

這個過程里有很多可以做的事情,既包括構建工具本身,也包括開發者。構建工具本身可以決定構建速度,構建文件多少,是否熱更新;而開發者可以規定構建的規則,針對某類文件要如何處理,某些文件要打包在一起,是否需要清除不運行的代碼等等。這些可以控制的部分都是因為構建工具本身留出了hook,基本都是通過一個配置文件來處理,同時開發者可以開發插件來完成自定義的需求。

此時我們把文章開頭定義的構建工具定義拿出來看一看:

構建工具是可以幫助開發者管理本地源文件,優化開發流程,降低開發複雜度,使開發者更加專註在業務邏輯開發上的一種工具。

從上面兩個定義看,基本一致。只是當我們通過兩個不同的構建工具更加深刻體會的構建工具的作用,以及各自實現上的不同。

構建工具本身的目的都是一樣的,只是實現上的方法各不相同,目的都是為了更加高效的執行。

最後,比心。。。


推薦閱讀:

TAG:前端開發 | Web開發 |