gulp和grunt的區別


1. Grunt -> Gulp

早些年提到構建工具,難免會讓人聯想到歷史比較悠久的MakeAnt,以及后來為了更方便的構建結構類似的Java項目而出現的Maven。Node催生了一批自動化工具,像Bower,Yeoman,Grunt等。而如今前端提到構建工具會自然想起Grunt。Java世界里的Maven提供了強大的包依賴管理和構建生命周期管理。

在JavaScript的世界里,Grunt.js是基於Node.js的自動化任務運行器。2013年02月18日,Grunt v0.4.0 發布。Fractal公司積極參與了數個流行Node.js模塊的開發,它去年發布了一個新的構建系統Gulp,希望能夠取其精華,並取代Grunt,成為最流行的JavaScript任務運行器。

2. Grunt的特點

  • Grunt有一個完善的社區,插件豐富
  • 它簡單易學,你可以隨便安裝插件並配置它們
  • 你不需要多先進的理念,也不需要任何經驗

完善 – Grunt的插件數據: 根據社區的結果顯示,共計3,439個插件,其中49個官方插件。

易用 – Grunt的插件豐富: 許多常見的任務都有現成的Grunt插件,而且有眾多第三方插件,如:CoffeeScriptHandlebarsJadeJsHintLessRequireJSSassStyles。而且通過參考文檔進行配置便可以使用。

3. Gulp和Grunt的異同點

  • 易於使用:采用代碼優於配置策略,Gulp讓簡單的事情繼續簡單,復雜的任務變得可管理。
  • 高效:通過利用Node.js強大的流,不需要往磁盤寫中間文件,可以更快地完成構建。
  • 高質量:Gulp嚴格的插件指導方針,確保插件簡單並且按你期望的方式工作。
  • 易於學習:通過把API降到最少,你能在很短的時間內學會Gulp。構建工作就像你設想的一樣:是一系列流管道。

易用 Gulp相比Grunt更簡潔,而且遵循代碼優於配置策略,維護Gulp更像是寫代碼。

高效 Gulp相比Grunt更有設計感,核心設計基於Unix流的概念,通過管道連接,不需要寫中間文件。

高質量 Gulp的每個插件只完成一個功能,這也是Unix的設計原則之一,各個功能通過流進行整合並完成復雜的任務。例如:Grunt的imagemin插件不僅壓縮圖片,同時還包括緩存功能。他表示,在Gulp中,緩存是另一個插件,可以被別的插件使用,這樣就促進了插件的可重用性。目前官方列出的有673個插件。

易學 Gulp的核心API只有5個,掌握了5個API就學會了Gulp,之后便可以通過管道流組合自己想要的任務。

4. Yeoman Team Talks

Yeoman團隊去年12月份時在Github上也專門提過一個issue,討論是否使用Gulp取代Grunt:他們提到Gulp是一個新的基於流的管道式構建系統,需要很少的配置並且更快。

5. Gruntfile.js

module.exports = function(grunt) { grunt.initConfig({ concat: { 'dist/all.js': ['src/*.js'] }, uglify: { 'dist/all.min.js': ['dist/all.js'] }, jshint: { files: ['gruntfile.js', 'src/*.js'] }, watch: { files: ['gruntfile.js', 'src/*.js'], tasks: ['jshint', 'concat', 'uglify'] } }); // Load Our Plugins grunt.loadNpmTasks('grunt-contrib-jshint'); grunt.loadNpmTasks('grunt-contrib-concat'); grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-contrib-watch'); // Register Default Task grunt.registerTask('default', ['jshint', 'concat', 'uglify']); }; 

6. Gulpfile.js

var gulp = require('gulp'); var jshint = require('gulp-jshint'); var concat = require('gulp-concat'); var rename = require('gulp-rename'); var uglify = require('gulp-uglify'); // Lint JS gulp.task('lint', function() { return gulp.src('src/*.js') .pipe(jshint()) .pipe(jshint.reporter('default')); }); // Concat & Minify JS gulp.task('minify', function(){ return gulp.src('src/*.js') .pipe(concat('all.js')) .pipe(gulp.dest('dist')) .pipe(rename('all.min.js')) .pipe(uglify()) .pipe(gulp.dest('dist')); }); // Watch Our Files gulp.task('watch', function() { gulp.watch('src/*.js', ['lint', 'minify']); }); // Default gulp.task('default', ['lint', 'minify', 'watch']); 

7. 差異和不同

  • 流:Gulp是一個基於流的構建系統,使用代碼優於配置的策略。
  • 插件:Gulp的插件更純粹,單一的功能,並堅持一個插件只做一件事。
  • 代碼優於配置:維護Gulp更像是寫代碼,而且Gulp遵循CommonJS規范,因此跟寫Node程序沒有差別。
  • 沒有產生中間文件

8. I/O流程的不同

  1. 使用Grunt的I/O過程中會產生一些中間態的臨時文件,一些任務生成臨時文件,其它任務可能會基於臨時文件再做處理並生成最終的構建后文件。
  2. 而使用Gulp的優勢就是利用流的方式進行文件的處理,通過管道將多個任務和操作連接起來,因此只有一次I/O的過程,流程更清晰,更純粹。

9. Gulp的核心:流

Gulp通過流和代碼優於配置策略來盡量簡化任務編寫的工作。這看起來有點“像jQuery”的方法,把動作串起來創建構建任務。早在Unix的初期,流就已經存在了。流在Node.js生態系統中也扮演了重要的角色,類似於*nix將幾乎所有設備抽象為文件一樣,Node將幾乎所有IO操作都抽象成了Stream的操作。因此用Gulp編寫任務也可看作是用Node.js編寫任務。當使用流時,Gulp去除了中間文件,只將最后的輸出寫入磁盤,整個過程因此變得更快。

Doug McIlroy, then head of the Bell Labs CSRC (Computing Sciences Research Center), and inventor of the Unix pipe, summarized the Unix philosophy as follows:

This is the Unix philosophy: Write programs that do one thing and do it well. Write programs to work together. Write programs to handle text streams, because that is a universal interface.

基於流的模塊特點:

  • Write modules that do one thing and do it well.
  • Write modules that work together.
  • Write modules that handle events and streams.

Unix管道示例:

tput setaf 88 ; whoami | figlet | tr _ … | tr \ \` | tr \| ¡ | tr / √ 

10. 為什么應該使用流?

Node中的I/O操作是異步的,因此磁盤的讀寫和網絡操作都需要傳遞回調函數。

var http = require('http'); var fs = require('fs'); var server = http.createServer(function (req, res) { fs.readFile(__dirname + '/data.txt', function (err, data) { res.end(data); }); }); server.listen(8000); 

這個Node.js應用很簡單,估計所有學習過Node的人都做過這樣的練習,可以說是Node的Hello World了。這段代碼沒有任何問題,你使用node可以正常的運行起來,使用瀏覽器或者其他的http客戶端都可以正常的訪問運行程序主機的8000端口讀取主機上的data.txt文件。但是這種方式隱含了一個潛在的問題,node會把整個data.txt文件都緩存到內存中以便響應客戶端的請求(request),隨着客戶端請求的增加內存的消耗將是非常驚人的,而且客戶端需要等待很長傳輸時間才能得到結果。讓我們再看一看另外一種方式,使用流:

var http = require('http'); var fs = require('fs'); var server = http.createServer(function (req, res) { var stream = fs.createReadStream(__dirname + '/data.txt'); stream.pipe(res); }); server.listen(8000); 

這里面有一個非常大的變化就是使用createReadStream這個fs的方法創建了stream這個變量,並由這個變量的pip方法來響應客戶端的請求。使用stream這個變量就可以讓node讀取data.txt一定量的時候就開始向客戶端發送響應的內容,而無需服務緩存以及客戶端的等待。

Node中Stream的種類

  • Readable(可讀)
  • Writeable(可寫)
  • Duplex(雙工)
  • Transform(運算雙工)

流可以是可讀(Readable)或可寫(Writable),或者兼具兩者(Duplex,雙工)的。所有流都是 EventEmitter,但它們也具有其它自定義方法和屬性,取決於它們是 Readable、Writable 或 Duplex。

Depends on

  • Liftoff
  • Through2
  • Vinyl, Vinyl-fs
  • Orchestrator

Liftoff模塊解決的問題是全局安裝一個CLI工具,但支持多個項目多個配置文件,並且當前目錄沒有配置文件時,可以就近向上級目錄找到已有的配置文件,或者在項目目錄外執行命令行時可以指定配置文件的目錄。所以Gulp基於liftoff可以實現,多個項目多個Gulpfile,並且可以執行gulp時指定配置文件路徑。

Through2是為Node的streams2.Transform的小型封裝,來避免subclassing的煩惱。可以更簡單的通過一個函數來創建一個流,而不用再繁瑣的設置原型鏈的_transform_flush,以及再擴充的Transform類中調用構造函數,以便緩沖設定能夠正確初始化。

Vinyl是用來描述文件的一個非常簡單的元信息對象,Vinyl對象有兩個主要的屬性:pathcontent。因為一個文件不僅可能是你硬盤上的一些內容,還可能是你托管在S3,FTP,甚至DropBox上的一些內容,所以Vinyl可以描述所有這些來源的文件。它提供了一種簡潔的描述文件的方式,但如果你需要訪問本地文件系統上的一個文件,還需要通過一個所謂的Vinyl Adapter,它會暴漏一些方法:如.src(globs).dest(folder),和watch(globs, fn)。globs是路徑模式匹配。

Orchestrator其實是一個基於Node的模塊,負責任務依賴關系定義,處理和執行,很像我們目前所用的AMD模塊加載器,而且默認是最大限度的並行加載的方式。

事實上,gulp中的任務運行系統並不是自己實現的,而是直接使用了orchestrator。在gulp的源代碼中可以發現,gulp繼承了orchestrator,而gulp.task僅僅只是orchestrator.add的別名而已:

//gulp source code var util = require('util'); var Orchestrator = require('orchestrator'); function Gulp() { Orchestrator.call(this); } util.inherits(Gulp, Orchestrator); Gulp.prototype.task = Gulp.prototype.add; 

11. Gulp的API

  • gulp.task
  • gulp.run
  • gulp.watch
  • gulp.src
  • gulp.dest

gulp.task

在orchestrator中,解決上述任務依賴的方式有三種:

  1. 在任務定義的function中返回一個數據流,當該數據流的end事件觸發時,任務結束。
  2. 在任務定義的function中返回一個promise對象,當該promise對象resolve時,任務結束。
  3. 在任務定義的function中傳入callback變量,當callback()執行時,任務結束。

Gulp腳本中可以使用這三種方法來實現任務依賴,不過由於Gulp中的任務大多是數據流操作,因此以第一種方法為主。

12. Gulp的插件開發

所有的Gulp.js插件基本都是through(后面不再使用transform這個詞)streams,即是消費者(接收gulp.src()傳遞出來的數據,然后進行處理加工處理),又是生產者(將加工后的數據傳遞出去)。Gulp.js的使用和插件的開發都很簡單,當然里面還有很多細節,拋磚引玉,具體請看Gulp.js的官方文檔。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM