?
This document uses PHP Chinese website manual Release
Gulp與Grunt一樣,也是一個(gè)自動(dòng)任務(wù)運(yùn)行器。它充分借鑒了Unix操作系統(tǒng)的管道(pipe)思想,很多人認(rèn)為,在操作上,它要比Grunt簡(jiǎn)單。
安裝
gulpfile.js
gulp模塊的方法
src()
dest()
task()
watch()
gulp-load-plugins模塊
gulp-livereload模塊
參考鏈接
Gulp需要全局安裝,然后再在項(xiàng)目的開發(fā)目錄中安裝為本地模塊。先進(jìn)入項(xiàng)目目錄,運(yùn)行下面的命令。
npm install -g gulp npm install --save-dev gulp
除了安裝gulp以外,不同的任務(wù)還需要安裝不同的gulp插件模塊。舉例來(lái)說,下面代碼安裝了gulp-uglify模塊。
$ npm install --save-dev gulp-uglify
項(xiàng)目根目錄中的gulpfile.js,是Gulp的配置文件。下面就是一個(gè)典型的gulpfile.js文件。
var gulp = require('gulp'); var uglify = require('gulp-uglify'); gulp.task('minify', function () { gulp.src('js/app.js') .pipe(uglify()) .pipe(gulp.dest('build')) });
上面代碼中,gulpfile.js加載gulp和gulp-uglify模塊之后,使用gulp模塊的task方法指定任務(wù)minify。task方法有兩個(gè)參數(shù),第一個(gè)是任務(wù)名,第二個(gè)是任務(wù)函數(shù)。在任務(wù)函數(shù)中,使用gulp模塊的src方法,指定所要處理的文件,然后使用pipe方法,將上一步的輸出轉(zhuǎn)為當(dāng)前的輸入,進(jìn)行鏈?zhǔn)教幚怼?/p>
task方法的回調(diào)函數(shù)使用了兩次pipe方法,也就是說做了兩種處理。第一種處理是使用gulp-uglify模塊,壓縮源碼;第二種處理是使用gulp模塊的dest方法,將上一步的輸出寫入本地文件,這里是build.js(代碼中省略了后綴名js)。
執(zhí)行minify任務(wù)時(shí),就在項(xiàng)目目錄中執(zhí)行下面命令就可以了。
$ gulp minify
從上面的例子中可以看到,gulp充分使用了“管道”思想,就是一個(gè)數(shù)據(jù)流(stream):src方法讀入文件產(chǎn)生數(shù)據(jù)流,dest方法將數(shù)據(jù)流寫入文件,中間是一些中間步驟,每一步都對(duì)數(shù)據(jù)流進(jìn)行一些處理。
下面是另一個(gè)數(shù)據(jù)流的例子。
gulp.task('js', function () { return gulp.src('js/*.js') .pipe(jshint()) .pipe(uglify()) .pipe(concat('app.js')) .pipe(gulp.dest('build')); });
上面代碼使用pipe命令,分別進(jìn)行jshint、uglify、concat三步處理。
gulp模塊的src方法,用于產(chǎn)生數(shù)據(jù)流。它的參數(shù)表示所要處理的文件,這些指定的文件會(huì)轉(zhuǎn)換成數(shù)據(jù)流。參數(shù)的寫法一般有以下幾種形式。
js/app.js:指定確切的文件名。
js/*.js:某個(gè)目錄所有后綴名為js的文件。
js/**/*.js:某個(gè)目錄及其所有子目錄中的所有后綴名為js的文件。
!js/app.js:除了js/app.js以外的所有文件。
*.+(js|css):匹配項(xiàng)目根目錄下,所有后綴名為js或css的文件。
src方法的參數(shù)還可以是一個(gè)數(shù)組,用來(lái)指定多個(gè)成員。
gulp.src(['js/**/*.js', '!js/**/*.min.js'])
dest方法將管道的輸出寫入文件,同時(shí)將這些輸出繼續(xù)輸出,所以可以依次調(diào)用多次dest方法,將輸出寫入多個(gè)目錄。如果有目錄不存在,將會(huì)被新建。
gulp.src('./client/templates/*.jade') .pipe(jade()) .pipe(gulp.dest('./build/templates')) .pipe(minify()) .pipe(gulp.dest('./build/minified_templates'));
dest方法還可以接受第二個(gè)參數(shù),表示配置對(duì)象。
gulp.dest('build', { cwd: './app', mode: '0644' })
配置對(duì)象有兩個(gè)字段。cwd字段指定寫入路徑的基準(zhǔn)目錄,默認(rèn)是當(dāng)前目錄;mode字段指定寫入文件的權(quán)限,默認(rèn)是0777。
task方法用于定義具體的任務(wù)。它的第一個(gè)參數(shù)是任務(wù)名,第二個(gè)參數(shù)是任務(wù)函數(shù)。下面是一個(gè)非常簡(jiǎn)單的任務(wù)函數(shù)。
gulp.task('greet', function () { console.log('Hello world!'); });
task方法還可以指定按順序運(yùn)行的一組任務(wù)。
gulp.task('build', ['css', 'js', 'imgs']);
上面代碼先指定build任務(wù),它由css、js、imgs三個(gè)任務(wù)所組成,task方法會(huì)并發(fā)執(zhí)行這三個(gè)任務(wù)。注意,由于每個(gè)任務(wù)都是異步調(diào)用,所以沒有辦法保證js任務(wù)的開始運(yùn)行的時(shí)間,正是css任務(wù)運(yùn)行結(jié)束。
如果希望各個(gè)任務(wù)嚴(yán)格按次序運(yùn)行,可以把前一個(gè)任務(wù)寫成后一個(gè)任務(wù)的依賴模塊。
gulp.task('css', ['greet'], function () { // Deal with CSS here });
上面代碼表明,css任務(wù)依賴greet任務(wù),所以css一定會(huì)在greet運(yùn)行完成后再運(yùn)行。
task方法的回調(diào)函數(shù),還可以接受一個(gè)函數(shù)作為參數(shù),這對(duì)執(zhí)行異步任務(wù)非常有用。
// 執(zhí)行shell命令 var exec = require('child_process').exec; gulp.task('jekyll', function(cb) { // build Jekyll exec('jekyll build', function(err) { if (err) return cb(err); // return error cb(); // finished task }); });
如果一個(gè)任務(wù)的名字為default,就表明它是“默認(rèn)任務(wù)”,在命令行直接輸入gulp命令,就會(huì)運(yùn)行該任務(wù)。
gulp.task('default', function () { // Your default task }); // 或者 gulp.task('default', ['styles', 'jshint', 'watch']);
執(zhí)行的時(shí)候,直接使用gulp,就會(huì)運(yùn)行styles、jshint、watch三個(gè)任務(wù)。
watch方法用于指定需要監(jiān)視的文件。一旦這些文件發(fā)生變動(dòng),就運(yùn)行指定任務(wù)。
gulp.task('watch', function () { gulp.watch('templates/*.tmpl.html', ['build']); });
上面代碼指定,一旦templates目錄中的模板文件發(fā)生變化,就運(yùn)行build任務(wù)。
watch方法也可以用回調(diào)函數(shù),代替指定的任務(wù)。
gulp.watch('templates/*.tmpl.html', function (event) { console.log('Event type: ' + event.type); console.log('Event path: ' + event.path); });
另一種寫法是watch方法所監(jiān)控的文件發(fā)生變化時(shí)(修改、增加、刪除文件),會(huì)觸發(fā)change事件。可以對(duì)change事件指定回調(diào)函數(shù)。
var watcher = gulp.watch('templates/*.tmpl.html', ['build']); watcher.on('change', function (event) { console.log('Event type: ' + event.type); console.log('Event path: ' + event.path); });
除了change事件,watch方法還可能觸發(fā)以下事件。
end:回調(diào)函數(shù)運(yùn)行完畢時(shí)觸發(fā)。
error:發(fā)生錯(cuò)誤時(shí)觸發(fā)。
ready:當(dāng)開始監(jiān)聽文件時(shí)觸發(fā)。
nomatch:沒有匹配的監(jiān)聽文件時(shí)觸發(fā)。
watcher對(duì)象還包含其他一些方法。
watcher.end():停止watcher對(duì)象,不會(huì)再調(diào)用任務(wù)或回調(diào)函數(shù)。
watcher.files():返回watcher對(duì)象監(jiān)視的文件。
watcher.add(glob):增加所要監(jiān)視的文件,它還可以附件第二個(gè)參數(shù),表示回調(diào)函數(shù)。
watcher.remove(filepath):從watcher對(duì)象中移走一個(gè)監(jiān)視的文件。
一般情況下,gulpfile.js中的模塊需要一個(gè)個(gè)加載。
var gulp = require('gulp'), jshint = require('gulp-jshint'), uglify = require('gulp-uglify'), concat = require('gulp-concat'); gulp.task('js', function () { return gulp.src('js/*.js') .pipe(jshint()) .pipe(jshint.reporter('default')) .pipe(uglify()) .pipe(concat('app.js')) .pipe(gulp.dest('build')); });
上面代碼中,除了gulp模塊以外,還加載另外三個(gè)模塊。
這種一一加載的寫法,比較麻煩。使用gulp-load-plugins模塊,可以加載package.json文件中所有的gulp模塊。上面的代碼用gulp-load-plugins模塊改寫,就是下面這樣。
var gulp = require('gulp'), gulpLoadPlugins = require('gulp-load-plugins'), plugins = gulpLoadPlugins(); gulp.task('js', function () { return gulp.src('js/*.js') .pipe(plugins.jshint()) .pipe(plugins.jshint.reporter('default')) .pipe(plugins.uglify()) .pipe(plugins.concat('app.js')) .pipe(gulp.dest('build')); });
上面代碼假設(shè)package.json文件包含以下內(nèi)容。
{ "devDependencies": { "gulp-concat": "~2.2.0", "gulp-uglify": "~0.2.1", "gulp-jshint": "~1.5.1", "gulp": "~3.5.6" } }
gulp-livereload模塊用于自動(dòng)刷新瀏覽器,反映出源碼的最新變化。它除了模塊以外,還需要在瀏覽器中安裝插件,用來(lái)配合源碼變化。
var gulp = require('gulp'), less = require('gulp-less'), livereload = require('gulp-livereload'), watch = require('gulp-watch'); gulp.task('less', function() { gulp.src('less/*.less') .pipe(watch()) .pipe(less()) .pipe(gulp.dest('css')) .pipe(livereload()); });
上面代碼監(jiān)視less文件,一旦編譯完成,就自動(dòng)刷新瀏覽器。
Callum Macrae, Building With Gulp