1. 程式人生 > >精通gulp的關鍵:檔案路徑匹配模式globs

精通gulp的關鍵:檔案路徑匹配模式globs

簡單說來,gulp的api非常簡單。只有4個,他們分別是gulp.src, gulp.dest, gulp.task, gulp.watch 因此,想要簡單的使用gulp很容易,但是想要將gulp使用到得心應手的地步並不是一件簡單的事情。 而其中最關鍵的地方在於,對nodejs檔案路徑匹配模式globs的理解。

gulp的4個api中, gulp.task建立一個任務, gulp.src表示建立的任務是針對檔案目錄中的那些位置 gulp.dest表示目標檔案被處理完畢之後會在哪個位置重新生成, gulp.watch表示監聽那些位置檔案的變化

除了task,其他三個api都與檔案路徑密切相關,而gulp.src與gulp.watch更是直接以golbs為第一個引數, gulp的各個外掛需要知道自己處理的是那些位置的檔案,而golbs則負責指定需要被處理檔案的位置

每個人對前端的理解有所差異,造成了前端的專案結構也是各不一樣,因此golbs的配置也就顯得更加重要 下面就一起來了解一下,golbs的匹配規則。

粗略讀一遍可能並不能掌握,所以建議大家收藏起來,留著以後慢慢看

* 匹配檔案中0個或者多個字元,但是不會匹配路徑中的分隔符,除非路徑分隔符出現在末尾 例如下面的寫法

// 匹配./style目錄下所有的js檔案
./style/*.js

// 匹配./style目錄下所有的檔案
./style/*.*

// 只要層級相同,可以匹配任意目錄下的任意js檔案 比如./style/a/b.js
./style/*/*.js

** 匹配檔案路徑中的0個或者多個層級的目錄,需要單獨出現,如果出現在末尾,也可匹配檔案


// 匹配style目錄及其所有子目錄下的所有js檔案,如能匹配
// ./style/a.js
// ./style/lib/res.js
// ./style/mudules/b/a.js
./style/**/*.js

// 匹配style目錄下的所有目錄和檔案,比如能匹配
// ./style/a.js
// ./style/bb
// ./style/images/c.png
./style/**/*

? 匹配一個字元,不會匹配路徑分隔符

// 能匹配檔名只有一個字元的js檔案,如a.js, b.js ,但不能匹配檔名為2個字元及其以上的js的檔案
?.js

[...] 由多個規則組成的陣列,可以匹配陣列中符合任意一個子項的檔案,當子項中第一個字元為!或者^時,表示不匹配該規則

// 匹配style目錄下的a0.js, a1.js, a2.js, a3.js
'./style/a[0-3].js'

// 除開node_modules目錄之外,匹配專案根目錄下的所有html檔案
['./**/*.html', '!node_modules/**']

{...} 展開模式,根據裡面的內容展開為多個規則,能匹配所有展開之後的規則 將上面的例子擴充套件一下,可以如下寫

// 除開build,simple,images,node_modules目錄,匹配根目錄下所有的html與php檔案
['./**/*.{html, php}', '!{build, simple, images, node_modules}/**']

!(pattern|pattern|pattern) 每一個規則用pattern表示,這裡指排除符合這幾個模式的所有檔案

// 匹配排除檔名為一個字元的js檔案,以及排除jquery.js之後的所有js檔案
'./style/!(?|jquery).js'

// 排除build與node_modules目錄,並排除其他目錄下以下劃線_開頭的html與php檔案,匹配其餘的html與php檔案
// 這種比較複雜的規則在實際開發中會常常用到,需要加深瞭解
['./**/!(_)*.{html, php}', '!{build, node_modules}/**']

?(pattern|pattern|pattern) 匹配括號中給定的任一模式0次或者1次

// 匹配style目錄下的a.js, a2.js, b.js
// 不能組合
// 匹配0次或者1次
'./style/?(a|a2|b).js'

@(pattern|pattern|pattern) 匹配多個模式中的任一個

// 匹配style目錄下的a.js, b.js, c.js
// 不能組合
// 匹配一次,不能為空,注意與?的區別
'./style/@(a|b|c).js'

+(pattern|pattern|pattern) 匹配括號中給定任一模式1次或者多次,這幾個模式可以組合在一起匹配

// 可以匹配style目錄下的a.js, a2.js, b.js
// 也可以匹配他們的組合 ab.js, aa2.js, a2b.js等
// 至少匹配一次,為空不匹配
'./style/+(a|a2|b).js'

*(pattern|pattern|pattern) 匹配括號中給定任一模式0次或者多次,這幾個模式可以組合在一起匹配

// 可以匹配style目錄下的a.js, b.js, c.js
// 也可以匹配他們的組合 ab.js, bc.js, ac.js
// 匹配0次或者多次
'./style/*(a|b|c).js'

幾乎能用到的規則就都在上面了,在實際使用的時候,我們需要根據自己的實際情況來使用 只要掌握了globs模式,距離精通gulp也不太遠了。

API

gulp.src(globs[, options])

徹底瞭解了globs,那麼回過頭來學習gulp的這幾個api就變得非常簡單。 gulp.src指定gulp任務的目標檔案位置 它的引數中,第一個引數globs為必選,中括號表示可選引數,options為一個配置json物件 globs用於指定目標檔案的位置,在上面已經做過史上最詳細的介紹,這裡對option做一個簡單的介紹即可

gulp.src('./**/!(_).{php, html}', {
    buffer: true,
    read: true,
    base: './'
})

options.buffer

型別: Boolean 預設:true 如果該項被設定為false,那麼將會以stream方式返回file.contents,而不是檔案buffer的形式。 這在處理一些大檔案的時候非常有用。 另外需要注意點是,gulp外掛可能不支援stream

options.read

型別: Boolean 預設: true 如果被設定為false,那麼file.contents會返回空值(null) 也就是並不會去讀取檔案

options.base

型別: String 預設值: 第一個引數目錄中,glob匹配模式之前的路徑 理解base至關重要,它將影響到檔案處理完畢後,新檔案生成的目錄 我們需要結合gulp.dest來理解它。

比如在一個路徑為static/scripts/libs的目錄中,有一個js元件叫做dialog.js

// 匹配static/scripts/libs/dialog.js,並將base解析成 static/scripts
gulp.src('static/scripts/**/*.js')
  .pipe(minify())

  // 處理完畢的檔案,將會用build替換掉base,即為 build/libs/dialog.js
  .pipe(gulp.dest('build'));

// 手動設定base的值
gulp.src('static/scripts/**/*.js', { base: 'static' })
  .pipe(minify())

  // 使用build替換base,結果為 build/scripts/libs/dialog.js
  .pipe(gulp.dest('build'));

gulp.dest(path[, options])

能被pipe進來,並且將會寫檔案。會重新輸出所有資料。 也就是說會新建處理完畢之後的檔案。 我們可以將他pipe到多個資料夾。如果資料夾不存在,則會自動新建。 關於使用方法,可以結合上面的例子進行理解。

指定的路徑用於替換gulp.src引數options中的base,結果就是新檔案的位置

gulp.dest的options沒怎麼使用過,感覺沒上面用,就略過了。

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

定義個gulp任務

gulp.task('somename', function() {
  // dosomething
})

name 型別:string 任務的名字,如果你需要在命令列中執行你的某些任務,那麼不要在任務名字中使用空格

deps 型別: array 一個包含任務列表的陣列,這些任務會在你當前任務執行之前完成

gulp.task('mytask', ['array', 'of', 'task', 'names'], function() {
  // dosomething
})

請一定要確保你所依賴的任務列表中的任務都使用了正確的非同步執行方式:使用一個 callback,或者返回一個 promise 或 stream。

fn 該函式定義任務所需要執行的一些操作。通常來說,它會是這樣中形式

gulp.src().pipe(someplugin())

非同步任務支援 如果fn能夠做到以下其中一點,就可以非同步執行了

接受一個callback

// 在 shell 中執行一個命令
var exec = require('child_process').exec;
gulp.task('jekyll', function(cb) {
  // 編譯 Jekyll
  exec('jekyll build', function(err) {
    if (err) return cb(err); // 返回 error
    cb(); // 完成 task
  });
});

返回一個stream

gulp.task('somename', function() {
  var stream = gulp.src('client/**/*.js')
    .pipe(minify())
    .pipe(gulp.dest('build'));
  return stream;
});

或者返回一個promise

var Q = require('q');

gulp.task('somename', function() {
  var deferred = Q.defer();

  // 執行非同步的操作
  setTimeout(function() {
    deferred.resolve();
  }, 1);

  return deferred.promise;
});

預設的,task 將以最大的併發數執行,也就是說,gulp 會一次性執行所有的 task 並且不做任何等待。如果你想要建立一個序列化的 task 佇列,並以特定的順序執行,你需要做兩件事:1. 給出一個提示,來告知 task 什麼時候執行完畢 2.並且再給出一個提示,來告知一個 task 依賴另一個 task 的完成。

對於這個例子,讓我們先假定你有兩個 task,"one" 和 "two",並且你希望它們按照這個順序執行:

a 在 "one" 中,你加入一個提示,來告知什麼時候它會完成:可以再完成時候返回一個 callback,或者返回一個 promise 或 stream,這樣系統會去等待它完成。

b 在 "two" 中,你需要新增一個提示來告訴系統它需要依賴第一個 task 完成。

因此,這個例子的實際程式碼將會是這樣:

var gulp = require('gulp');

// 返回一個 callback,因此係統可以知道它什麼時候完成
gulp.task('one', function(cb) {
    // 做一些事 -- 非同步的或者其他的
    // 如果 err 不是 null 或 undefined,則會停止執行,且注意,這樣代表執行失敗了
    cb(err);
});

// 定義一個所依賴的 task 必須在這個 task 執行之前完成
gulp.task('two', ['one'], function() {
    // 'one' 完成後
});

gulp.task('default', ['one', 'two']);

gulp.watch(glob [, options], tasks) 或 gulp.watch(glob [, options, cb])

監視檔案,並且可以在檔案發生改動時候做一些事情。它總會返回一個 EventEmitter 來發射(emit) change 事件。

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

glob與options略過。

tasks 型別: 陣列 需要在檔案變動後執行的一個或者多個通過 gulp.task() 建立的 task 的名字,

var watcher = gulp.watch('js/**/*.js', ['uglify','reload']);
watcher.on('change', function(event) {
  console.log('File ' + event.path + ' was ' + event.type + ', running tasks...');
});

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

glob與options略過

cb(event) 型別: Function 每次變動需要執行的回撥函式

gulp.watch('js/**/*.js', function(event) {
  console.log('File ' + event.path + ' was ' + event.type + ', running tasks...');
});

callback 會被傳入一個名為 event 的物件。這個物件描述了所監控到的變動:

event.type 型別: String 發生變動的行為型別: added, changed, deleted

event.path 型別: String 觸發該事件的檔案路徑

好啦,gulp的api與最關鍵的globs都介紹完了,基本上gulp也算是掌握完畢了。 接下來我們要做的事情就是逐步去了解完成各種任務的外掛了。很多外掛都比較簡單, 在npm上都能夠找到使用方法,所以也就沒什麼好擔心的了。