gulp的API介紹

2020-09-24 16:16 更新

gulp的API介紹

使用 gulp ,主要知道4個(gè)API就能完成大部分需求:gulp.task(),gulp.src(),gulp.dest(),gulp.watch(),所以很快就能掌握,但是這4個(gè)API有幾個(gè)地方需理解透徹才行,我會(huì)在下面一一說明。為了避免出現(xiàn)理解偏差,建議先看一遍下面的文檔。

3.1 gulp.src()

在介紹這個(gè) API 之前我們首先來說一下 Grunt.js 和 Gulp.js 工作方式的一個(gè)區(qū)別。Grunt 主要是以文件為媒介來運(yùn)行它的工作流的,比如在 Grunt 中執(zhí)行完一項(xiàng)任務(wù)后,會(huì)把結(jié)果寫入到一個(gè)臨時(shí)文件中,然后可以在這個(gè)臨時(shí)文件內(nèi)容的基礎(chǔ)上執(zhí)行其它任務(wù),執(zhí)行完成后又把結(jié)果寫入到臨時(shí)文件中,然后又以這個(gè)為基礎(chǔ)繼續(xù)執(zhí)行其它任務(wù)...就這樣反復(fù)下去。而在 Gulp 中,使用的是 Nodejs 中的 stream(流),首先獲取到需要的 stream,然后可以通過 stream 的pipe()方法把流導(dǎo)入到你想要的地方,比如 Gulp 的插件中,經(jīng)過插件處理后的流又可以繼續(xù)導(dǎo)入到其他插件中,當(dāng)然也可以把流寫入到文件中。所以 Gulp 是以 stream 為媒介的,它不需要頻繁的生成臨時(shí)文件,這也是 Gulp 的速度比 Grunt 快的一個(gè)原因。再回到正題上來,gulp.src()方法正是用來獲取流的,但要注意這個(gè)流里的內(nèi)容不是原始的文件流,而是一個(gè)虛擬文件對(duì)象流(Vinyl files),這個(gè)虛擬文件對(duì)象中存儲(chǔ)著原始文件的路徑、文件名、內(nèi)容等信息,這個(gè)我們暫時(shí)不用去深入理解,你只需簡單的理解可以用這個(gè)方法來讀取你需要操作的文件就行了。其語法為:

gulp.src(globs[, options])

globs參數(shù)是文件匹配模式(類似正則表達(dá)式),用來匹配文件路徑(包括文件名),當(dāng)然這里也可以直接指定某個(gè)具體的文件路徑。當(dāng)有多個(gè)匹配模式時(shí),該參數(shù)可以為一個(gè)數(shù)組。
options為可選參數(shù)。通常情況下我們不需要用到。

下面我們重點(diǎn)說說 Gulp 用到的 glob 的匹配規(guī)則以及一些文件匹配技巧。
Gulp內(nèi)部使用了 node-glob 模塊來實(shí)現(xiàn)其文件匹配功能。我們可以使用下面這些特殊的字符來匹配我們想要的文件:

  • * 匹配文件路徑中的0個(gè)或多個(gè)字符,但不會(huì)匹配路徑分隔符,除非路徑分隔符出現(xiàn)在末尾
  • ** 匹配路徑中的0個(gè)或多個(gè)目錄及其子目錄,需要單獨(dú)出現(xiàn),即它左右不能有其他東西了。如果出現(xiàn)在末尾,也能匹配文件。
  • ? 匹配文件路徑中的一個(gè)字符(不會(huì)匹配路徑分隔符)
  • [...] 匹配方括號(hào)中出現(xiàn)的字符中的任意一個(gè),當(dāng)方括號(hào)中第一個(gè)字符為^!時(shí),則表示不匹配方括號(hào)中出現(xiàn)的其他字符中的任意一個(gè),類似js正則表達(dá)式中的用法
  • !(pattern|pattern|pattern) 匹配任何與括號(hào)中給定的任一模式都不匹配的
  • ?(pattern|pattern|pattern) 匹配括號(hào)中給定的任一模式0次或1次,類似于js正則中的(pattern|pattern|pattern)?
  • +(pattern|pattern|pattern) 匹配括號(hào)中給定的任一模式至少1次,類似于js正則中的(pattern|pattern|pattern)+
  • *(pattern|pattern|pattern) 匹配括號(hào)中給定的任一模式0次或多次,類似于js正則中的(pattern|pattern|pattern)*
  • @(pattern|pattern|pattern) 匹配括號(hào)中給定的任一模式1次,類似于js正則中的(pattern|pattern|pattern)

下面以一系列例子來加深理解

  • * 能匹配 a.js,x.y,abc,abc/,但不能匹配a/b.js
  • *.* 能匹配 a.js,style.css,a.b,x.y
  • */*/*.js 能匹配 a/b/c.js,x/y/z.js,不能匹配a/b.js,a/b/c/d.js
  • ** 能匹配 abc,a/b.js,a/b/c.js,x/y/z,x/y/z/a.b,能用來匹配所有的目錄和文件
  • **/*.js 能匹配 foo.js,a/foo.js,a/b/foo.js,a/b/c/foo.js
  • a/**/z 能匹配 a/z,a/b/z,a/b/c/z,a/d/g/h/j/k/z
  • a/**b/z 能匹配 a/b/z,a/sb/z,但不能匹配a/x/sb/z,因?yàn)橹挥袉?code>**單獨(dú)出現(xiàn)才能匹配多級(jí)目錄
  • ?.js 能匹配 a.js,b.js,c.js
  • a?? 能匹配 a.b,abc,但不能匹配ab/,因?yàn)樗粫?huì)匹配路徑分隔符
  • [xyz].js 只能匹配 x.js,y.js,z.js,不會(huì)匹配xy.js,xyz.js等,整個(gè)中括號(hào)只代表一個(gè)字符
  • [^xyz].js 能匹配 a.js,b.js,c.js等,不能匹配x.js,y.js,z.js

當(dāng)有多種匹配模式時(shí)可以使用數(shù)組

//使用數(shù)組的方式來匹配多種文件
gulp.src(['js/*.js','css/*.css','*.html'])

使用數(shù)組的方式還有一個(gè)好處就是可以很方便的使用排除模式,在數(shù)組中的單個(gè)匹配模式前加上!即是排除模式,它會(huì)在匹配的結(jié)果中排除這個(gè)匹配,要注意一點(diǎn)的是不能在數(shù)組中的第一個(gè)元素中使用排除模式

gulp.src([*.js,'!b*.js']) //匹配所有js文件,但排除掉以b開頭的js文件
gulp.src(['!b*.js',*.js]) //不會(huì)排除任何文件,因?yàn)榕懦J讲荒艹霈F(xiàn)在數(shù)組的第一個(gè)元素中

此外,還可以使用展開模式。展開模式以花括號(hào)作為定界符,根據(jù)它里面的內(nèi)容,會(huì)展開為多個(gè)模式,最后匹配的結(jié)果為所有展開的模式相加起來得到的結(jié)果。展開的例子如下:

  • a{b,c}d 會(huì)展開為 abd,acd
  • a{b,}c 會(huì)展開為 abc,ac
  • a{0..3}d 會(huì)展開為 a0d,a1d,a2d,a3d
  • a{b,c{d,e}f}g 會(huì)展開為 abg,acdfg,acefg
  • a{b,c}d{e,f}g 會(huì)展開為 abdeg,acdeg,abdeg,abdfg

3.2 gulp.dest()

gulp.dest()方法是用來寫文件的,其語法為:

gulp.dest(path[,options])

path為寫入文件的路徑
options為一個(gè)可選的參數(shù)對(duì)象,通常我們不需要用到

要想使用好gulp.dest()這個(gè)方法,就要理解給它傳入的路徑參數(shù)與最終生成的文件的關(guān)系。
gulp的 使用流程一般是這樣子的:首先通過gulp.src()方法獲取到我們想要處理的文件流,然后把文件流通過pipe方法導(dǎo)入到gulp的插件中,最后把經(jīng)過插件處理后的流再通過 pipe 方法導(dǎo)入到gulp.dest()中,gulp.dest()方法則把流中的內(nèi)容寫入到文件中,這里首先需要弄清楚的一點(diǎn)是,我們給gulp.dest()傳入的路徑參數(shù),只能用來指定要生成的文件的目錄,而不能指定生成文件的文件名,它生成文件的文件名使用的是導(dǎo)入到它的文件流自身的文件名,所以生成的文件名是由導(dǎo)入到它的文件流決定的,即使我們給它傳入一個(gè)帶有文件名的路徑參數(shù),然后它也會(huì)把這個(gè)文件名當(dāng)做是目錄名,例如:

var gulp = require('gulp');
gulp.src('script/jquery.js')
    .pipe(gulp.dest('dist/foo.js'));
//最終生成的文件路徑為 dist/foo.js/jquery.js,而不是dist/foo.js

要想改變文件名,可以使用插件 gulp-rename

下面說說生成的文件路徑與我們給gulp.dest()方法傳入的路徑參數(shù)之間的關(guān)系。
gulp.dest(path)生成的文件路徑是我們傳入的path參數(shù)后面再加上gulp.src()中有通配符開始出現(xiàn)的那部分路徑。例如:

var gulp = reruire('gulp');
//有通配符開始出現(xiàn)的那部分路徑為 **/*.js
gulp.src('script/**/*.js')
    .pipe(gulp.dest('dist')); //最后生成的文件路徑為 dist/**/*.js
//如果 **/*.js 匹配到的文件為 jquery/jquery.js ,則生成的文件路徑為 dist/jquery/jquery.js

再舉更多一點(diǎn)的例子

gulp.src('script/avalon/avalon.js') //沒有通配符出現(xiàn)的情況
    .pipe(gulp.dest('dist')); //最后生成的文件路徑為 dist/avalon.js

//有通配符開始出現(xiàn)的那部分路徑為 **/underscore.js
gulp.src('script/**/underscore.js')
    //假設(shè)匹配到的文件為script/util/underscore.js
    .pipe(gulp.dest('dist')); //則最后生成的文件路徑為 dist/util/underscore.js

gulp.src('script/*') //有通配符出現(xiàn)的那部分路徑為 *
    //假設(shè)匹配到的文件為script/zepto.js    
    .pipe(gulp.dest('dist')); //則最后生成的文件路徑為 dist/zepto.js

通過指定gulp.src()方法配置參數(shù)中的base屬性,我們可以更靈活的來改變gulp.dest()生成的文件路徑。
當(dāng)我們沒有在gulp.src()方法中配置base屬性時(shí),base的默認(rèn)值為通配符開始出現(xiàn)之前那部分路徑,例如:

gulp.src('app/src/**/*.css') //此時(shí)base的值為 app/src

上面我們說的gulp.dest()所生成的文件路徑的規(guī)則,其實(shí)也可以理解成,用我們給gulp.dest()傳入的路徑替換掉gulp.src()中的base路徑,最終得到生成文件的路徑。

gulp.src('app/src/**/*.css') //此時(shí)base的值為app/src,也就是說它的base路徑為app/src
     //設(shè)該模式匹配到了文件 app/src/css/normal.css
    .pipe(gulp.dest('dist')) //用dist替換掉base路徑,最終得到 dist/css/normal.css

所以改變base路徑后,gulp.dest()生成的文件路徑也會(huì)改變

gulp.src(script/lib/*.js) //沒有配置base參數(shù),此時(shí)默認(rèn)的base路徑為script/lib
    //假設(shè)匹配到的文件為script/lib/jquery.js
    .pipe(gulp.dest('build')) //生成的文件路徑為 build/jquery.js

gulp.src(script/lib/*.js, {base:'script'}) //配置了base參數(shù),此時(shí)base路徑為script
    //假設(shè)匹配到的文件為script/lib/jquery.js
    .pipe(gulp.dest('build')) //此時(shí)生成的文件路徑為 build/lib/jquery.js    

gulp.dest()把文件流寫入文件后,文件流仍然可以繼續(xù)使用。

3.3 gulp.task()

gulp.task方法用來定義任務(wù),內(nèi)部使用的是 Orchestrator,其語法為:

gulp.task(name[, deps], fn)

name 為任務(wù)名
deps 是當(dāng)前定義的任務(wù)需要依賴的其他任務(wù),為一個(gè)數(shù)組。當(dāng)前定義的任務(wù)會(huì)在所有依賴的任務(wù)執(zhí)行完畢后才開始執(zhí)行。如果沒有依賴,則可省略這個(gè)參數(shù)
fn 為任務(wù)函數(shù),我們把任務(wù)要執(zhí)行的代碼都寫在里面。該參數(shù)也是可選的。

gulp.task('mytask', ['array', 'of', 'task', 'names'], function() { //定義一個(gè)有依賴的任務(wù)
  // Do something
});

gulp.task()這個(gè)API沒什么好講的,但需要知道執(zhí)行多個(gè)任務(wù)時(shí)怎么來控制任務(wù)執(zhí)行的順序。
 gulp 中執(zhí)行多個(gè)任務(wù),可以通過任務(wù)依賴來實(shí)現(xiàn)。例如我想要執(zhí)行one,two,three這三個(gè)任務(wù),那我們就可以定義一個(gè)空的任務(wù),然后把那三個(gè)任務(wù)當(dāng)做這個(gè)空的任務(wù)的依賴就行了:

//只要執(zhí)行default任務(wù),就相當(dāng)于把one,two,three這三個(gè)任務(wù)執(zhí)行了
gulp.task('default',['one','two','three']);

如果任務(wù)相互之間沒有依賴,任務(wù)會(huì)按你書寫的順序來執(zhí)行,如果有依賴的話則會(huì)先執(zhí)行依賴的任務(wù)。
但是如果某個(gè)任務(wù)所依賴的任務(wù)是異步的,就要注意了,gulp 并不會(huì)等待那個(gè)所依賴的異步任務(wù)完成,而是會(huì)接著執(zhí)行后續(xù)的任務(wù)。例如:

gulp.task('one',function(){
  //one是一個(gè)異步執(zhí)行的任務(wù)
  setTimeout(function(){
    console.log('one is done')
  },5000);
});

//two任務(wù)雖然依賴于one任務(wù),但并不會(huì)等到one任務(wù)中的異步操作完成后再執(zhí)行
gulp.task('two',['one'],function(){
  console.log('two is done');
});

上面的例子中我們執(zhí)行 two 任務(wù)時(shí),會(huì)先執(zhí)行 one 任務(wù),但不會(huì)去等待 one 任務(wù)中的異步操作完成后再執(zhí)行 two 任務(wù),而是緊接著執(zhí)行 two 任務(wù)。所以 two 任務(wù)會(huì)在 one 任務(wù)中的異步操作完成之前就執(zhí)行了。

那如果我們想等待異步任務(wù)中的異步操作完成后再執(zhí)行后續(xù)的任務(wù),該怎么做呢?
有三種方法可以實(shí)現(xiàn):
第一:在異步操作完成后執(zhí)行一個(gè)回調(diào)函數(shù)來通知 gulp 這個(gè)異步任務(wù)已經(jīng)完成,這個(gè)回調(diào)函數(shù)就是任務(wù)函數(shù)的第一個(gè)參數(shù)。

gulp.task('one',function(cb){ //cb為任務(wù)函數(shù)提供的回調(diào),用來通知任務(wù)已經(jīng)完成
  //one是一個(gè)異步執(zhí)行的任務(wù)
  setTimeout(function(){
    console.log('one is done');
    cb();  //執(zhí)行回調(diào),表示這個(gè)異步任務(wù)已經(jīng)完成
  },5000);
});

//這時(shí)two任務(wù)會(huì)在one任務(wù)中的異步操作完成后再執(zhí)行
gulp.task('two',['one'],function(){
  console.log('two is done');
});

第二:定義任務(wù)時(shí)返回一個(gè)流對(duì)象。適用于任務(wù)就是操作 gulp.src 獲取到的流的情況。

gulp.task('one',function(cb){
  var stream = gulp.src('client/**/*.js')
      .pipe(dosomething()) //dosomething()中有某些異步操作
      .pipe(gulp.dest('build'));
    return stream;
});

gulp.task('two',['one'],function(){
  console.log('two is done');
});

第三:返回一個(gè) promise 對(duì)象,例如

var Q = require('q'); //一個(gè)著名的異步處理的庫 https://github.com/kriskowal/q
gulp.task('one',function(cb){
  var deferred = Q.defer();
  // 做一些異步操作
  setTimeout(function() {
     deferred.resolve();
  }, 5000);
  return deferred.promise;
});

gulp.task('two',['one'],function(){
  console.log('two is done');
});

gulp.task()就這些了,主要是要知道當(dāng)依賴是異步任務(wù)時(shí)的處理。

3.4 gulp.watch()

gulp.watch()用來監(jiān)視文件的變化,當(dāng)文件發(fā)生變化后,我們可以利用它來執(zhí)行相應(yīng)的任務(wù),例如文件壓縮等。其語法為

gulp.watch(glob[, opts], tasks)

glob 為要監(jiān)視的文件匹配模式,規(guī)則和用法與gulp.src()方法中的glob相同。
opts 為一個(gè)可選的配置對(duì)象,通常不需要用到
tasks 為文件變化后要執(zhí)行的任務(wù),為一個(gè)數(shù)組

gulp.task('uglify',function(){
  //do something
});
gulp.task('reload',function(){
  //do something
});
gulp.watch('js/**/*.js', ['uglify','reload']);

gulp.watch()還有另外一種使用方式:

gulp.watch(glob[, opts, cb])

globopts參數(shù)與第一種用法相同
cb參數(shù)為一個(gè)函數(shù)。每當(dāng)監(jiān)視的文件發(fā)生變化時(shí),就會(huì)調(diào)用這個(gè)函數(shù),并且會(huì)給它傳入一個(gè)對(duì)象,該對(duì)象包含了文件變化的一些信息,type屬性為變化的類型,可以是added,changed,deleted;path屬性為發(fā)生變化的文件的路徑

gulp.watch('js/**/*.js', function(event){
    console.log(event.type); //變化類型 added為新增,deleted為刪除,changed為改變 
    console.log(event.path); //變化的文件的路徑
}); 


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)