轉自Gulp vs Gruntjavascript
1. Grunt -> Gulp
早些年提到構建工具,不免會讓人聯想到歷史比較悠久的Make
,Ant
,以及後來爲了更方便的構建結構相似的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任務運行器。java
2. Grunt的特色
- Grunt有一個完善的社區,插件豐富
- 它簡單易學,你能夠隨便安裝插件並配置它們
- 你不須要多先進的理念,也不須要任何經驗
完善
– Grunt的插件數據: 根據社區的結果顯示,共計3,439個插件,其中49個官方插件。node
易用
– Grunt的插件豐富: 許多常見的任務都有現成的Grunt插件,並且有衆多第三方插件,如:CoffeeScript
,Handlebars
,Jade
,JsHint
,Less
,RequireJS
,Sass
,Styles
。並且經過參考文檔進行配置即可以使用。nginx
3. Gulp和Grunt的異同點
- 易於使用:採用代碼優於配置策略,Gulp讓簡單的事情繼續簡單,複雜的任務變得可管理。
- 高效:經過利用Node.js強大的流,不須要往磁盤寫中間文件,能夠更快地完成構建。
- 高質量:Gulp嚴格的插件指導方針,確保插件簡單而且按你指望的方式工做。
- 易於學習:經過把API降到最少,你能在很短的時間內學會Gulp。構建工做就像你設想的同樣:是一系列流管道。
易用
Gulp相比Grunt更簡潔,並且遵循代碼優於配置策略,維護Gulp更像是寫代碼。gulp
高效
Gulp相比Grunt更有設計感,核心設計基於Unix流的概念,經過管道鏈接,不須要寫中間文件。promise
高質量
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流程的不一樣
- 使用Grunt的I/O過程當中會產生一些中間態的臨時文件,一些任務生成臨時文件,其它任務可能會基於臨時文件再作處理並生成最終的構建後文件。
- 而使用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對象有兩個主要的屬性: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;
11. Gulp的API
gulp.task
gulp.run
gulp.watch
gulp.src
gulp.dest
gulp.task
在orchestrator中,解決上述任務依賴的方式有三種:
- 在任務定義的function中返回一個數據流,當該數據流的end事件觸發時,任務結束。
- 在任務定義的function中返回一個promise對象,當該promise對象resolve時,任務結束。
- 在任務定義的function中傳入callback變量,當callback()執行時,任務結束。
Gulp腳本中可使用這三種方法來實現任務依賴,不過因爲Gulp中的任務大可能是數據流操做,所以以第一種方法爲主。
12. Gulp的插件開發
全部的Gulp.js插件基本都是through
(後面再也不使用transform這個詞)streams,便是消費者(接收gulp.src()
傳遞出來的數據,而後進行處理加工處理),又是生產者(將加工後的數據傳遞出去)。Gulp.js的使用和插件的開發都很簡單,固然裏面還有不少細節,拋磚引玉,具體請看Gulp.js的官方文檔。