轉自Gulp vs Grunt前端
早些年提到構建工具,不免會讓人聯想到歷史比較悠久的Make
,Ant
,以及後來爲了更方便的構建結構相似的Java項目而出現的Maven
。Node催生了一批自動化工具,像Bower,Yeoman,Grunt等。而現在前端提到構建工具會天然想起Grunt
。Java世界裏的Maven提供了強大的包依賴管理和構建生命週期管理。node
在JavaScript的世界裏,Grunt.js是基於Node.js的自動化任務運行器。2013年02月18日,Grunt v0.4.0 發佈。Fractal公司積極參與了數個流行Node.js模塊的開發,它去年發佈了一個新的構建系統Gulp,但願可以取其精華,並取代Grunt,成爲最流行的JavaScript任務運行器。gulp
完善
– Grunt的插件數據: 根據社區的結果顯示,共計3,439個插件,其中49個官方插件。promise
易用
– Grunt的插件豐富: 許多常見的任務都有現成的Grunt插件,並且有衆多第三方插件,如:CoffeeScript
,Handlebars
,Jade
,JsHint
,Less
,RequireJS
,Sass
,Styles
。並且經過參考文檔進行配置即可以使用。瀏覽器
易用
Gulp相比Grunt更簡潔,並且遵循代碼優於配置策略,維護Gulp更像是寫代碼。緩存
高效
Gulp相比Grunt更有設計感,核心設計基於Unix流的概念,經過管道鏈接,不須要寫中間文件。網絡
高質量
Gulp的每一個插件只完成一個功能,這也是Unix的設計原則之一,各個功能經過流進行整合並完成複雜的任務。例如:Grunt的imagemin
插件不只壓縮圖片,同時還包括緩存功能。他表示,在Gulp中,緩存是另外一個插件,能夠被別的插件使用,這樣就促進了插件的可重用性。目前官方列出的有673個插件。異步
易學
Gulp的核心API只有5個,掌握了5個API就學會了Gulp,以後即可以經過管道流組合本身想要的任務。svg
Yeoman團隊去年12月份時在Github上也專門提過一個issue,討論是否使用Gulp取代Grunt:他們提到Gulp是一個新的基於流的管道式構建系統,須要不多的配置而且更快。函數
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']); };
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']);
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.
基於流的模塊特色:
Unix管道示例:
tput setaf 88 ; whoami | figlet | tr _ … | tr \ \` | tr \| ¡ | tr / √
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)或可寫(Writable),或者兼具二者(Duplex,雙工)的。全部流都是 EventEmitter,但它們也具備其它自定義方法和屬性,取決於它們是 Readable、Writable 或 Duplex。
Liftoff
Through2
Vinyl
, Vinyl-fs
Orchestrator
Liftoff
模塊解決的問題是全局安裝一個CLI工具,但支持多個項目多個配置文件,而且當前目錄沒有配置文件時,能夠就近向上級目錄找到已有的配置文件,或者在項目目錄外執行命令行時能夠指定配置文件的目錄。因此Gulp基於liftoff
能夠實現,多個項目多個Gulpfile,而且能夠執行gulp
時指定配置文件路徑。
Through2
是爲Node的streams2.Transform
的小型封裝,來避免subclassing
的煩惱。能夠更簡單的經過一個函數來建立一個流,而不用再繁瑣的設置原型鏈的_transform
,_flush
,以及再擴充的Transform類中調用構造函數,以便緩衝設定可以正確初始化。
Vinyl
是用來描述文件的一個很是簡單的元信息對象,Vinyl對象有兩個主要的屬性:path
和content
。由於一個文件不只多是你硬盤上的一些內容,還多是你託管在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;
gulp.task
gulp.run
gulp.watch
gulp.src
gulp.dest
gulp.task
在orchestrator中,解決上述任務依賴的方式有三種:
Gulp腳本中可使用這三種方法來實現任務依賴,不過因爲Gulp中的任務大可能是數據流操做,所以以第一種方法爲主。
全部的Gulp.js插件基本都是through
(後面再也不使用transform這個詞)streams,便是消費者(接收gulp.src()
傳遞出來的數據,而後進行處理加工處理),又是生產者(將加工後的數據傳遞出去)。Gulp.js的使用和插件的開發都很簡單,固然裏面還有不少細節,拋磚引玉,具體請看Gulp.js的官方文檔。