前端構建工具之爭——Webpack vs Gulp 誰會被拍死在沙灘上

文章有點長,總共 1800 字,閱讀須要 18 分鐘。哈哈,沒耐心的直接戳我到高潮部分javascript

理想的前端開發流程

在說構建工具以前得先說說咱指望的前端開發流程是怎樣的?css

  • 寫業務邏輯代碼(例如 es6,scss,pug 等)
  • 處理成瀏覽器認識的(js,css,html)
  • 瀏覽器自動刷新看到效果

前端開發就是在不斷的 123..123..123.... 循環中進行的,上面的後兩步(也就是 2 和 3)應該是 自動化 的,前端開發者理應只需關注第 1 步——寫業務邏輯代碼。html

自動化的事情應該交由構建工具來作,時下流行的前端構建工具備 gulp 和 webpack(有人說 webpack 不算是構建工具,我以爲這沒什麼好爭的。橫當作嶺側成峯,我以爲從當前 webpack 所能作的事情來看,說它是構建工具絲絕不爲過)。本文不會對 gulpwebpack 的概念和內容作深刻解析,而是但願從宏觀的角度研究他們的優點短缺和適用場景,從而說清長期瀰漫在前端圈兩者之間撲朔迷離的關係。前端

什麼是構建工具
構建工具是一段自動根據源代碼生成可以使用文件的程序,構建過程包括打包、編譯、壓縮、測試等一切須要對源代碼進行的相關處理。構建工具的目的是實現構建過程的自動化,使用它可讓我們避免機械重複的勞動(這怕是程序員最不能忍受的了),從而解放咱們的雙手。

解放了雙手幹什麼
哇槽,愛幹什麼幹什麼。java

Gulp 爲什麼物

先來聽聽 Ta 的官網是怎麼說:node

Gulp 致力於 自動化和優化 你的工做流,它是一個自動化你開發工做中 痛苦又耗時任務 的工具包。webpack

想想我們平常的開發工做中痛苦又耗時任務有哪些呢?git

  • 用 es6,typescript 編寫的腳本文件須要編譯成瀏覽器認識的 javascript
  • 用 scss,less 編寫的樣式文件須要編譯成瀏覽器認識的 css
  • 檢查代碼是否符合書寫規範,跑單元測試和集成測試
  • 開發環境若是有 sourcemaps 的話調試起來就方便多了,修改完代碼瀏覽器能自動刷新當即看到效果就更好了
  • 生產環境部署代碼須要壓縮合並靜態文件,添加文件指紋控制緩存
  • blabla...更多的你本身想吧

Gulp 聲稱要幫我們實現 自動化,那他是怎樣幫助我們實現自動化的呢?這就不得不先提一嘴牛逼哄哄的 NodeJS程序員

Node 背景小知識es6

Node 使前端 Jser 有了脫離瀏覽器工做的能力,要擱之前的話我們寫的 js 要麼嵌到 html 頁面裏,而後用瀏覽器打開 html 頁面才能運行js,要麼就是在瀏覽器開發者工具的 Console 面板裏編寫運行代碼片斷。總之沒了瀏覽器這個宿主,我們的 js 就 run 不起來。Node 這貨突發奇想,把開發者工具的 Console 給摳下來了,今後 js 能夠脫離瀏覽器直接在 node 裏運行。至關於 js 如今有了兩個宿主環境,一個是瀏覽器,一個是 node。固然了,Node 可不是開發者工具裏的 Console,那只是打個比方。它是基於Chrome V8 引擎實現的一個 JavaScript 運行環境,功能其實相似 Console 面板,但提供了大量實用的 API,感興趣的同窗可前往 Node官網 詳細瞭解,英文吃力的騷年 戳這裏。Node 能夠算是前端革命式的創新,隨 node 一塊兒發佈的 node 包管理器 npm(node package manager) 也已是全球最大的開源庫生態系統。node/npm 這對組合一出,前端生態迎來了大爆發,一時間爲解決各類問題的 node 包層出不窮,遍地開花。gulp 就是披荊斬棘,一路過五關斬六將闖出來的一個小 node 包。

扯談完畢,接下來就來看看 gulp 是否是在裝逼,他到底能不能幫咱們實現自動化。

做爲一個 node 包,標準打開方式固然是:

npm i -g gulp

而後呢,這裏以編譯 less 爲例,首先安裝編譯 less 須要用到的 node 包:

npm i --save-dev gulp gulp-less

前面已經全局安裝過 gulp 了,怎麼又本地安裝了一遍
前面的 -g 是全局安裝,是爲了執行你所編寫的 gulp 任務,即 gulp yourTask。然後面的 --save-dev 是本地安裝,是爲了我們編寫任務時使用 gulp 提供的 api,例如 gulp.src()gulp.task()gulp.dest() 等等。固然也是能夠直接使用全局安裝的 gulp 的 api 的,可是強烈不推薦,由於這樣涉及到 gulp 版本控制的問題,並且使用全局 gulp 的 api 的話就會產生環境依賴(你假設環境已經全局安裝了gulp,萬一沒裝呢,程序不就出錯了)。

接着在項目的根目錄下新建一個 gulpfile.js 文件,這是 gulp 的默認配置文件。

gulpfile.js 必須放在項目根目錄?
固然也可放在其餘目錄,但這樣的話就得在啓動 gulp 任務時手動指定 gulp 配置文件 gulp yourTask --gulpfile yourGulpfilePath,可能還須要全局安裝 gulp-cli,因此除非有特殊須要,不然就放在項目根目錄就好了,這樣最簡單。

配置文件的名字必須是 gulpfile.js 嗎?
不區分大小寫,取成 gULPFile.js 的話 gulp 也能認識,只要 toLowerCase 以後是 gulpfile 就好了,若是取其它名字那你就又得使用 --gulpfile 選項去指定了。

如今工程目錄結構已經成了下面的樣子:

構建前 gulp 工程目錄結構

接下來就是在 gulpfile.js 裏編寫 gulp task(gulp 把爲每一個痛苦又耗時任務編寫的處理方法稱爲一個 task):

const gulp = require('gulp');
const less = require('gulp-less');

gulp.task('build:less', function(){
    return gulp.src('./src/*.less')
        .pipe(less())
        .pipe(gulp.dest('./dist'));
});

最後就是打開一個終端,在終端裏運行 gulp build:less。好了,編譯後的文件已經被輸出到了 dist 目錄:

構建後 gulp 工程目錄結構

至此你已經算是一個 gulp 磚家了,這基本上就是 gulp 的所有內容。怎麼樣,是否是夠簡單,夠絲滑。這也是 gulp 的突出特色——易於學習,易於使用,五分鐘成磚家。若是想要執行解決其餘痛苦又耗時的任務,只需下載安裝對應的 gulp 插件包,而後依次類推寫一個 gulp.task 出來就好了。

這些源代碼具體是怎樣被處理的
這一般不須要關心,由於 gulp 插件包已爲你作好了,而且封裝的很是漂亮,你只須要告訴 gulp 你要什麼,gulp 及其插件會幫你打點好一切。這就比如你把一份電子文檔傳進打印機,告訴它我要一份 A4 紙打印,呲呲呲~,打印機就吐出來一張 A4 紙,上面是你的文檔內容。源代碼就是你的電子文檔,gulp 插件就是打印機,生成的可用文件就是你手裏的那張 A4 紙,你不用關心打印機內部是怎樣工做的,由於它封裝的很好,或者你能夠把打印機拆了一探究竟也行。

Gulp 是基於流的?
流(Stream)不是 gulp 創造的概念,而是從 unix 時代就開始使用的 I/O 機制,一直到如今仍在普遍使用。Node 封裝了一個 stream 模塊專門用來對流進行操做。gulp 所基於的流便是 Node 封裝起來的 stream。上面 gulp.task() 代碼裏面的 pipe 方法並非 gulp 提供的 api,而是 node 的 api,準確的說應該是 node 的 stream 模塊提供的 api。具體是怎麼實現的呢:gulp.src() 的返回值是 node Stream 的一個實例,以後的 pipe 調用的實際上是這個實例的 pipe 方法,而 pipe 方法的返回值依然是 node Stream 實例,以此實現前面的 .pipe().pipe().pipe() 這種串聯寫法。熟悉 jQuery 的同窗應該很清楚這種技巧。

webpack 又是從哪冒出來的

gulp 彷佛是完美的,對前端開發工做中每一項痛苦又耗時任務都能見招拆招,各個擊破。然而前端發展速度之快超乎想象,對頁面性能和用戶體驗更是追求極致,以致於 gulp 某些領域尤爲大型 SPA(單頁應用)顯得有些不夠用了:

  • 單頁應用的核心是模塊化,ES6 以前 JavaScript 語言自己一直是沒有模塊系統的,致使 AMD,CMD,UMD 各類輪子模塊化方案都蹦出來。對這種模塊化亂象,gulp 顯得無能爲力,gulp 插件對這一塊也沒有什麼想法。不過也能夠理解,模塊化解決方案可不是誰都能 hold 住的,須要考慮的問題太多了;
  • 對前沿的 SPA 技術 gulp 處理起來顯得有些力不從心,例如 Vue 的單文件組件,gulp 配合一些插件能夠勉強處理,可是很蹩腳。其實歸根結底,仍是模塊化處理方面的不足;
  • 優化頁面加載速度的一條重要法則就是減小 http 請求。gulp 只是對靜態資源作流式處理,處理以後並未作有效的優化整合,也就是說 gulp 忽略了系統層面的處理,這一塊還有很大的優化空間,尤爲是移動端,那才真的是一寸光陰一寸金啊,哪怕是幾百毫秒的優化所帶來的收益(用戶?流量?付費?)絕對超乎你的想象。別跟我說 gulp-concat,CSS Sprites,這倆玩意兒小打小鬧還行,趕上大型應用根本拿不上臺面。如今的頁面動輒上百個零碎資源(圖片,樣式表,腳本),也就是上百個 http 請求,所以這個優化需求仍是至關迫切的。關於爲什麼減小 http 請求能夠有效下降頁面加載時間戳這裏
  • blabla... 你本身想吧,主要就是大型單頁應用方面有短板;

時勢造英雄。webpack 一聲吼,大張旗鼓地挖起了gulp 的牆角。

老規矩,先看看webpack官網怎麼吹牛逼介紹本身的:

Webpack 是當下最熱門的前端資源模塊化 管理和打包 工具。它能夠將許多鬆散的模塊按照依賴和規則打包成符合生產環境部署的前端資源。還能夠將按需加載的模塊進行代碼分割,等到實際須要的時候再異步加載。

是否是看完一臉懵逼,不明覺厲。其實翻譯過來就是 「在我眼裏,什麼都是模塊」。webpack 「萬物皆模塊」 的理念和 SPA 配合起來簡直是金童玉女,天做之合。這也是 webpack 短期內名聲大噪,直接撼動 gulp 地位的主要緣由。

webpack 的理念比較前衛,它自己也帶來了不少新的概念和內容,諸如加載器(loader)、依賴圖(Dependency Graph)等等。和 gulp 兩小時成磚家的學習難度相比,webpack 或許你研究兩天仍然會暈頭轉向。

接下來簡單看一下 webpack 的主要工做方式。

webpack 和 gulp 同樣也是一個小 node 包,打開方式天然是:

npm i -g webpack
npm i --save-dev webpack

和 gulp 同樣,全局安裝是爲了執行 webpack 任務,本地安裝是爲了使用 webpack 提供的 api。

安裝完 webpack 以後在項目根目錄下新建一個 webpack.config.js,這是 webpack 的默認配置文件,同 gulp 的 gulpfile.js 的功能相似。webpack.config.js 一樣是不區分大小寫的,取成 webPACk.CONfig.js 的話 webpack 也能認識,可是取成其餘名字或放在別的目錄就須要使用 --config 選項去指定配置文件了。

如今工程目錄結構以下:

構建前webpack工程目錄結構

接下來就是在 webpack.config.js 裏配置須要的選項,注意了,webpack 與 gulp 的重要不一樣就是使用方式 由編程式變成了配置式

const path = require('path');

module.exports = {
    entry: './src/index.js',        // 告訴 webpack 你要編譯哪一個文件
    output: {                       // 告訴 webpack 你要把編譯後生成的文件放在哪
        filename: 'bundle.js',
        path: path.join(__dirname, 'dist')
    }
};

最後仍然和 gulp 相似,就是在終端裏運行 webpack(終端裏通常會出現一大坨編譯信息)。好了,如今 webpack 已經把編譯好的文件輸出到了 dist 目錄:

構建後webpack工程目錄結構

看到這是否是已經一頭霧水了,在你還沒明白髮生了什麼的時候 webpack 已經把事情幹完了。這也是 webpack 和 gulp 做業方式的重要不一樣:Gulp 是搭了個臺子,讓 gulp 插件在上面唱戲,盡情表演,全部構建相關的具體事情都交由 gulp 插件去作。而 Webpack 就牛逼了,webpack 先搭了個臺子,而後本身在上面唱嗨了,仔細一聽,他在上面唱的是《咱們不同》,固然了他也是讓 webpack 插件在上面唱戲的。

也就是說 webpack 把不少功能都封裝進了本身身體裏面,使得本身強大同時臃腫。如今你能夠在 ./src/index.js 文件裏直接寫 ES6 代碼,由於 webpack 把編譯 ES6 的工做已經封裝到本身的實現裏了,使得 webpack 看起來原生支持 ES6 而不須要藉助第三方插件,其實他內部也是用了第三方插件的,因此你不用再專門去下一個 babel 之類的插件去轉譯 ES6。這樣封裝的好處是使用起來很方便,很差的地方就是使用者徹底不知道發生了什麼,構建完了還一臉懵逼。

上面僅是 webpack 使用的最最最簡單示例,簡直連 「hello world」 都算不上。具體怎樣打包各類資源(typescript,樣式表,圖片,字體等等)可前往 webpack官網 深刻學習,想看中文的同窗使勁 戳這裏

webpack 「一切皆模塊」 的特色完美解決了上面 gulp 暴露的幾個短板,連 webpack 官網也說本身是由於看到現存的模塊打包器都不太適合大型 SPA 應用,因而決定打造一個適合大型 SPA 應用的模塊打包器,也就是說 webpack 其實就是爲大型 SPA 而生的

webpack 怎麼實現像 gulp 同樣對大量源文件進行流式處理
人家 webpack 原本就沒打算作這事。webpack 不是以取代 gulp 爲目的的,而是爲了給大型 SPA 提供更好的構建方案。對大量源文件進行流式處理是 gulp 擅長的事,webpack 不想搶,也不必搶。即便搶,也無非是再造一個蹩腳的 gulp 出來而已。

既然 webpack 模塊化這麼強,那之後模塊化就全用 webpack 好了
webpack 模塊化是強,可是他胖啊,不是全部人都抱得動,主要是他爲了提供更多的功能封裝進了太多東西,因此選擇上仍是須要因地制宜。若是單純只是打包 js(多頁應用每每是這種需求),徹底可使用 rollup,browserify 這種小而美的實現,由於他們只作一件事——打包js。而若是須要將圖片,樣式,字體等全部靜態資源所有打包,webpack 毫無疑問是首選。這也是爲何愈來愈多的流行庫和框架開始從 webpack 轉向使用 rollup 進行打包,由於他們只須要打包 js,webpack 好多強大功能根本用不到。連 rollup 官網也坦言若是你在構建一個庫,rollup 絕對是首選,但若是是構建一個應用,那麼請選 webpack。

結論

我看好多人說 gulp 和 webpack 不是一類東西,我不這麼以爲,雖說二者的出發點確實是不同的,gulp 走的是流式處理路線,webpack 走的是模塊處理路線,可是二者所要達成的目標倒是同樣的,那就是促進前端領域的自動化和工程化管理。webpack 發展到如今,已經很是強大了,強大到在構建方面 gulp 能作的事 webpack 基本上均可以勝任,gulp 作不了的 webpack 也能搞。一樣的那些開發工做中痛苦又耗時的任務,gulp 和 webpack 都能解決,只是解決思路有天壤之別。

下表是從各個角度對 gulp 和 webpack 作的對比:

Gulp Webpack
定位 基於流的自動化構建工具 一個萬能模塊打包器
目標 自動化和優化開發工做流,爲通用 website 開發而生 通用模塊打包加載器,爲移動端大型 SPA 應用而生
學習難度 易於學習,易於使用,api總共只有5個方法 有大量新的概念和api,不過好在有詳盡的官方文檔
適用場景 基於流的做業方式適合多頁面應用開發 一切皆模塊的特色適合單頁面應用開發
做業方式 對輸入(gulp.src)的 js,ts,scss,less 等源文件依次執行打包(bundle)、編譯(compile)、壓縮、重命名等處理後輸出(gulp.dest)到指定目錄中去,爲了構建而打包 對入口文件(entry)遞歸解析生成依賴關係圖,而後將全部依賴打包在一塊兒,在打包以前會將全部依賴轉譯成可打包的 js 模塊,爲了打包而構建
使用方式 常規 js 開發,編寫一系列構建任務(task)。 編輯各類 JSON 配置項
優勢 適合多頁面開發,易於學習,易於使用,接口優雅。 能夠打包一切資源,適配各類模塊系統
缺點 在單頁面應用方面輸出乏力,並且對流行的單頁技術有些難以處理(好比 Vue 單文件組件,使用 gulp 處理就會很困難,而 webpack 一個 loader 就能輕鬆搞定) 不適合多頁應用開發,靈活度高但同時配置很繁瑣複雜。「打包一切」 這個優勢對於 HTTP/1.1 尤爲重要,由於全部資源打包在一塊兒能明顯減小瀏覽器訪問頁面時的資源請求數量,從而減小應用程序必須等待的時間。但這個優勢可能會隨着 HTTP/2 的流行而變得不那麼突出,由於 HTTP/2 的多路複用能夠有效解決客戶端並行請求時的瓶頸問題。
結論 瀏覽器多頁應用(MPA)首選方案 瀏覽器單頁應用(SPA)首選方案

gulp 爲什麼不吸收百家之長,把 webpack 的東西集成進來,反正都是開源的
騰訊那麼牛逼,你說他怎麼不把阿里巴巴集成進來。集成應該是沒可能,由於 gulp 和 webpack 的定位不同。因此,沒有放之天下而皆準的解決方案,只有具體問題具體分析選擇適合的解決方案才能正確地解決問題。gulp 和 webpack 只是咱們解決問題的工具,不要被工具束縛了手腳不能前進。

扯了這麼多,到底誰會被拍死在沙灘上
能夠看出來,這兩個工具其實各有優缺,都有用武之地。合理地配合使用,取長補短,才能發揮最大的威力,因此這倆基友並非互斥的,而是互補的,誰也不會被拍死在沙灘上。

臨末,送你們一個福利 😘

相關文章
相關標籤/搜索