原文位址:http://www.cnblogs.com/2050/p/4198792.html
gulpjs是一個前端建構工具,與gruntjs相比,gulpjs無需寫一大堆繁雜的配置參數,API也非常簡單,學習起來很容易,而且gulpjs使用的是nodejs中stream來讀取和操作資料,其速度更快。如果你還沒有使用過前端建構工具,或者覺得gruntjs太難用的話,那就嘗試一下gulp吧。
本文導航:
- gulp的安裝
- 開始使用gulp
- gulp的API介紹
- 一些常用的gulp插件
1、gulp的安裝
首先確定你已經正确安裝了nodejs環境。然後以全局方式安裝gulp:
全局安裝gulp後,還需要在每個要使用gulp的項目中都單獨安裝一次。把目錄切換到你的項目檔案夾中,然後在指令行中執行:
如果想在安裝的時候把gulp寫進項目package.json檔案的依賴中,則可以加上--save-dev:
這樣就完成了gulp的安裝。至于為什麼在全局安裝gulp後,還需要在項目中本地安裝一次,有興趣的可以看下stackoverflow上有人做出的回答:why-do-we-need-to-install-gulp-globally-and-locally、what-is-the-point-of-double-install-in-gulp。大體就是為了版本的靈活性,但如果沒了解那也不必太去糾結這個問題,隻需要知道通常我們是要這樣做就行了。
2、開始使用gulp
2.1 建立gulpfile.js檔案
就像gruntjs需要一個
Gruntfile.js
檔案一樣,gulp也需要一個檔案作為它的主檔案,在gulp中這個檔案叫做
gulpfile.js
。建立一個檔案名為
gulpfile.js
的檔案,然後放到你的項目目錄中。之後要做的事情就是在
gulpfile.js
檔案中定義我們的任務了。下面是一個最簡單的
gulpfile.js
檔案内容示例,它定義了一個預設的任務。
var gulp = require('gulp');
gulp.task('default',function(){
console.log('hello world');
});
此時我們的目錄結構是這樣子的:
├── gulpfile.js
├── node_modules
│ └── gulp
└── package.json
2.2 運作gulp任務
要運作gulp任務,隻需切換到存放
gulpfile.js
檔案的目錄(windows平台請使用cmd或者Power Shell等工具),然後在指令行中執行
gulp
指令就行了,
gulp
後面可以加上要執行的任務名,例如
gulp task1
,如果沒有指定任務名,則會執行任務名為
default
的預設任務。
3、gulp的API介紹
使用gulp,僅需知道4個API即可:
gulp.task()
,
gulp.src()
,
gulp.dest()
,
gulp.watch()
,是以很容易就能掌握,但有幾個地方需了解透徹才行,我會在下面一一說明。為了避免出現了解偏差,建議先看一遍官方文檔。
3.1 gulp.src()
在介紹這個API之前我們首先來說一下Grunt.js和Gulp.js工作方式的一個差別。Grunt主要是以檔案為媒介來運作它的工作流的,比如在Grunt中執行完一項任務後,會把結果寫入到一個臨時檔案中,然後可以在這個臨時檔案内容的基礎上執行其它任務,執行完成後又把結果寫入到臨時檔案中,然後又以這個為基礎繼續執行其它任務...就這樣反複下去。而在Gulp中,使用的是Nodejs中的stream(流),首先擷取到需要的stream,然後可以通過stream的
pipe()
方法把流導入到你想要的地方,比如Gulp的插件中,經過插件處理後的流又可以繼續導入到其他插件中,當然也可以把流寫入到檔案中。是以Gulp是以stream為媒介的,它不需要頻繁的生成臨時檔案,這也是Gulp的速度比Grunt快的一個原因。再回到正題上來,
gulp.src()
方法正是用來擷取流的,但要注意這個流裡的内容不是原始的檔案流,而是一個虛拟檔案對象流(Vinyl files),這個虛拟檔案對象中存儲着原始檔案的路徑、檔案名、内容等資訊,這個我們暫時不用去深入了解,你隻需簡單的了解可以用這個方法來讀取你需要操作的檔案就行了。其文法為:
globs參數是檔案比對模式(類似正規表達式),用來比對檔案路徑(包括檔案名),當然這裡也可以直接指定某個具體的檔案路徑。當有多個比對模式時,該參數可以為一個數組。
options為可選參數。通常情況下我們不需要用到。
下面我們重點說說Gulp用到的glob的比對規則以及一些檔案比對技巧。
Gulp内部使用了node-glob子產品來實作其檔案比對功能。我們可以使用下面這些特殊的字元來比對我們想要的檔案:
-
比對檔案路徑中的0個或多個字元,但不會比對路徑分隔符,除非路徑分隔符出現在末尾*
-
比對路徑中的0個或多個目錄及其子目錄,需要單獨出現,即它左右不能有其他東西了。如果出現在末尾,也能比對檔案。**
-
比對檔案路徑中的一個字元(不會比對路徑分隔符)?
-
比對方括号中出現的字元中的任意一個,當方括号中第一個字元為[...]
或^
時,則表示不比對方括号中出現的其他字元中的任意一個,類似js正規表達式中的用法!
-
比對任何與括号中給定的任一模式都不比對的!(pattern|pattern|pattern)
-
比對括号中給定的任一模式0次或1次,類似于js正則中的(pattern|pattern|pattern)??(pattern|pattern|pattern)
-
比對括号中給定的任一模式至少1次,類似于js正則中的(pattern|pattern|pattern)++(pattern|pattern|pattern)
-
比對括号中給定的任一模式0次或多次,類似于js正則中的(pattern|pattern|pattern)**(pattern|pattern|pattern)
-
比對括号中給定的任一模式1次,類似于js正則中的(pattern|pattern|pattern)@(pattern|pattern|pattern)
下面以一系列例子來加深了解
-
能比對*
,a.js
,x.y
,abc
,但不能比對abc/
a/b.js
-
能比對*.*
,a.js
,style.css
,a.b
x.y
-
能比對**.js
,foo.js
,a/foo.js
,a/b/foo.js
a/b/c/foo.js
-
能比對az
,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
單獨出現才能比對多級目錄**
-
能比對?.js
,a.js
,b.js
c.js
-
能比對a??
,a.b
,但不能比對abc
,因為它不會比對路徑分隔符ab/
-
隻能比對[xyz].js
,x.js
,y.js
,不會比對z.js
,xy.js
等,整個中括号隻代表一個字元xyz.js
-
能比對[^xyz].js
,a.js
,b.js
等,不能比對c.js
,x.js
,y.js
z.js
當有多種比對模式時可以使用數組
//使用數組的方式來比對多種檔案
gulp.src(['js/*.js','css/*.css','*.html'])
使用數組的方式還有一個好處就是可以很友善的使用排除模式,在數組中的單個比對模式前加上
!
即是排除模式,它會在比對的結果中排除這個比對,要注意一點的是不能在數組中的第一個元素中使用排除模式
gulp.src([*.js,'!b*.js']) //比對所有js檔案,但排除掉以b開頭的js檔案
gulp.src(['!b*.js',*.js]) //不會排除任何檔案,因為排除模式不能出現在數組的第一個元素中
此外,還可以使用展開模式。展開模式以花括号作為定界符,根據它裡面的内容,會展開為多個模式,最後比對的結果為所有展開的模式相加起來得到的結果。展開的例子如下:
-
會展開為a{b,c}d
,abd
acd
-
會展開為a{b,}c
,abc
ac
-
會展開為a{0..3}d
,a0d
,a1d
,a2d
a3d
-
會展開為a{b,c{d,e}f}g
,abg
,acdfg
acefg
-
會展開為a{b,c}d{e,f}g
,abdeg
,acdeg
,abdeg
abdfg
3.2 gulp.dest()
gulp.dest()方法是用來寫檔案的,其文法為:
path為寫入檔案的路徑
options為一個可選的參數對象,通常我們不需要用到
要想使用好
gulp.dest()
這個方法,就要了解給它傳入的路徑參數與最終生成的檔案的關系。
gulp的使用流程一般是這樣子的:首先通過
gulp.src()
方法擷取到我們想要處理的檔案流,然後把檔案流通過pipe方法導入到gulp的插件中,最後把經過插件處理後的流再通過pipe方法導入到
gulp.dest()
中,
gulp.dest()
方法則把流中的内容寫入到檔案中,這裡首先需要弄清楚的一點是,我們給
gulp.dest()
傳入的路徑參數,隻能用來指定要生成的檔案的目錄,而不能指定生成檔案的檔案名,它生成檔案的檔案名使用的是導入到它的檔案流自身的檔案名,是以生成的檔案名是由導入到它的檔案流決定的,即使我們給它傳入一個帶有檔案名的路徑參數,然後它也會把這個檔案名當做是目錄名,例如:
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()
方法傳入的路徑參數之間的關系。
gulp.dest(path)
生成的檔案路徑是我們傳入的path參數後面再加上
gulp.src()
中有通配符開始出現的那部分路徑。例如:
var gulp = reruire('gulp');
//有通配符開始出現的那部分路徑為 **/*.js
gulp.src('script/**/*.js')
.pipe(gulp.dest('dist')); //最後生成的檔案路徑為 dist/**/*.js
//如果 **/*.js 比對到的檔案為 jquery/jquery.js ,則生成的檔案路徑為 dist/jquery/jquery.js
再舉更多一點的例子
gulp.src('script/avalon/avalon.js') //沒有通配符出現的情況
.pipe(gulp.dest('dist')); //最後生成的檔案路徑為 dist/avalon.js
//有通配符開始出現的那部分路徑為 **/underscore.js
gulp.src('script/**/underscore.js')
//假設比對到的檔案為script/util/underscore.js
.pipe(gulp.dest('dist')); //則最後生成的檔案路徑為 dist/util/underscore.js
gulp.src('script/*') //有通配符出現的那部分路徑為 *
//假設比對到的檔案為script/zepto.js
.pipe(gulp.dest('dist')); //則最後生成的檔案路徑為 dist/zepto.js
通過指定
gulp.src()
方法配置參數中的
base
屬性,我們可以更靈活的來改變
gulp.dest()
生成的檔案路徑。
當我們沒有在
gulp.src()
方法中配置
base
屬性時,
base
的預設值為通配符開始出現之前那部分路徑,例如:
上面我們說的
gulp.dest()
所生成的檔案路徑的規則,其實也可以了解成,用我們給
gulp.dest()
傳入的路徑替換掉
gulp.src()
中的
base
路徑,最終得到生成檔案的路徑。
gulp.src('app/src/**/*.css') //此時base的值為app/src,也就是說它的base路徑為app/src
//設該模式比對到了檔案 app/src/css/normal.css
.pipe(gulp.dest('dist')) //用dist替換掉base路徑,最終得到 dist/css/normal.css
是以改變base路徑後,
gulp.dest()
生成的檔案路徑也會改變
gulp.src(script/lib/*.js) //沒有配置base參數,此時預設的base路徑為script/lib
//假設比對到的檔案為script/lib/jquery.js
.pipe(gulp.dest('build')) //生成的檔案路徑為 build/jquery.js
gulp.src(script/lib/*.js, {base:'script'}) //配置了base參數,此時base路徑為script
//假設比對到的檔案為script/lib/jquery.js
.pipe(gulp.dest('build')) //此時生成的檔案路徑為 build/lib/jquery.js
用
gulp.dest()
把檔案流寫入檔案後,檔案流仍然可以繼續使用。
3.3 gulp.task()
gulp.task
方法用來定義任務,内部使用的是Orchestrator,其文法為:
name 為任務名
deps 是目前定義的任務需要依賴的其他任務,為一個數組。目前定義的任務會在所有依賴的任務執行完畢後才開始執行。如果沒有依賴,則可省略這個參數
fn 為任務函數,我們把任務要執行的代碼都寫在裡面。該參數也是可選的。
gulp.task('mytask', ['array', 'of', 'task', 'names'], function() { //定義一個有依賴的任務
// Do something
});
gulp.task()
這個API沒什麼好講的,但需要知道執行多個任務時怎麼來控制任務執行的順序。
gulp中執行多個任務,可以通過任務依賴來實作。例如我想要執行
one
,
two
,
three
這三個任務,那我們就可以定義一個空的任務,然後把那三個任務當做這個空的任務的依賴就行了:
//隻要執行default任務,就相當于把one,two,three這三個任務執行了
gulp.task('default',['one','two','three']);
如果任務互相之間沒有依賴,任務會按你書寫的順序來執行,如果有依賴的話則會先執行依賴的任務。
但是如果某個任務所依賴的任務是異步的,就要注意了,gulp并不會等待那個所依賴的異步任務完成,而是會接着執行後續的任務。例如:
gulp.task('one',function(){
//one是一個異步執行的任務
setTimeout(function(){
console.log('one is done')
},);
});
//two任務雖然依賴于one任務,但并不會等到one任務中的異步操作完成後再執行
gulp.task('two',['one'],function(){
console.log('two is done');
});
上面的例子中我們執行two任務時,會先執行one任務,但不會去等待one任務中的異步操作完成後再執行two任務,而是緊接着執行two任務。是以two任務會在one任務中的異步操作完成之前就執行了。
那如果我們想等待異步任務中的異步操作完成後再執行後續的任務,該怎麼做呢?
有三種方法可以實作:
第一:在異步操作完成後執行一個回調函數來通知gulp這個異步任務已經完成,這個回調函數就是任務函數的第一個參數。
gulp.task('one',function(cb){ //cb為任務函數提供的回調,用來通知任務已經完成
//one是一個異步執行的任務
setTimeout(function(){
console.log('one is done');
cb(); //執行回調,表示這個異步任務已經完成
},);
});
//這時two任務會在one任務中的異步操作完成後再執行
gulp.task('two',['one'],function(){
console.log('two is done');
});
第二:定義任務時傳回一個流對象。适用于任務就是操作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');
});
第三:傳回一個promise對象,例如
var Q = require('q'); //一個著名的異步處理的庫 https://github.com/kriskowal/q
gulp.task('one',function(cb){
var deferred = Q.defer();
// 做一些異步操作
setTimeout(function() {
deferred.resolve();
}, );
return deferred.promise;
});
gulp.task('two',['one'],function(){
console.log('two is done');
});
gulp.task()
就這些了,主要是要知道當依賴是異步任務時的處理。
3.4 gulp.watch()
gulp.watch()
用來監視檔案的變化,當檔案發生變化後,我們可以利用它來執行相應的任務,例如檔案壓縮等。其文法為
glob 為要監視的檔案比對模式,規則和用法與
gulp.src()
方法中的
glob
相同。
opts 為一個可選的配置對象,通常不需要用到
tasks 為檔案變化後要執行的任務,為一個數組
gulp.task('uglify',function(){
//do something
});
gulp.task('reload',function(){
//do something
});
gulp.watch('js/**/*.js', ['uglify','reload']);
gulp.watch()
還有另外一種使用方式:
glob和opts參數與第一種用法相同
cb參數為一個函數。每當監視的檔案發生變化時,就會調用這個函數,并且會給它傳入一個對象,該對象包含了檔案變化的一些資訊,
type
屬性為變化的類型,可以是
added
,
changed
,
deleted
;
path
屬性為發生變化的檔案的路徑
gulp.watch('js/**/*.js', function(event){
console.log(event.type); //變化類型 added為新增,deleted為删除,changed為改變
console.log(event.path); //變化的檔案的路徑
});
4、一些常用的gulp插件
gulp的插件數量雖然沒有grunt那麼多,但也可以說是應有盡有了,下面列舉一些常用的插件。
4.1 自動加載插件
使用gulp-load-plugins
安裝:
npm install --save-dev gulp-load-plugins
要使用gulp的插件,首先得用
require
來把插件加載進來,如果我們要使用的插件非常多,那我們的
gulpfile.js
檔案開頭可能就會是這個樣子的:
var gulp = require('gulp'),
//一些gulp插件,abcd這些命名隻是用來舉個例子
a = require('gulp-a'),
b = require('gulp-b'),
c = require('gulp-c'),
d = require('gulp-d'),
e = require('gulp-e'),
f = require('gulp-f'),
g = require('gulp-g'),
//更多的插件...
z = require('gulp-z');
雖然這沒什麼問題,但會使我們的
gulpfile.js
檔案變得很冗長,看上去不那麼舒服。
gulp-load-plugins
插件正是用來解決這個問題。
gulp-load-plugins
這個插件能自動幫你加載
package.json
檔案裡的gulp插件。例如假設你的
package.json
檔案裡的依賴是這樣的:
{
"devDependencies": {
"gulp": "~3.6.0",
"gulp-rename": "~1.2.0",
"gulp-ruby-sass": "~0.4.3",
"gulp-load-plugins": "~0.5.1"
}
}
然後我們可以在
gulpfile.js
中使用
gulp-load-plugins
來幫我們加載插件:
var gulp = require('gulp');
//加載gulp-load-plugins插件,并馬上運作它
var plugins = require('gulp-load-plugins')();
然後我們要使用gulp-rename和gulp-ruby-sass這兩個插件的時候,就可以使用
plugins.rename
和
plugins.rubySass
來代替了,也就是原始插件名去掉
gulp-
字首,之後再轉換為駝峰命名。
實質上
gulp-load-plugins
是為我們做了如下的轉換
plugins.rename = require('gulp-rename');
plugins.rubySass = require('gulp-ruby-sass');
gulp-load-plugins
并不會一開始就加載所有
package.json
裡的gulp插件,而是在我們需要用到某個插件的時候,才去加載那個插件。
最後要提醒的一點是,因為
gulp-load-plugins
是通過你的
package.json
檔案來加載插件的,是以必須要保證你需要自動加載的插件已經寫入到了
package.json
檔案裡,并且這些插件都是已經安裝好了的。
4.2 重命名
使用gulp-rename
安裝:
npm install --save-dev gulp-rename
用來重命名檔案流中的檔案。用
gulp.dest()
方法寫入檔案時,檔案名使用的是檔案流中的檔案名,如果要想改變檔案名,那可以在之前用
gulp-rename
插件來改變檔案流中的檔案名。
var gulp = require('gulp'),
rename = require('gulp-rename'),
uglify = require("gulp-uglify");
gulp.task('rename', function () {
gulp.src('js/jquery.js')
.pipe(uglify()) //壓縮
.pipe(rename('jquery.min.js')) //會将jquery.js重命名為jquery.min.js
.pipe(gulp.dest('js'));
//關于gulp-rename的更多強大的用法請參考https://www.npmjs.com/package/gulp-rename
});
4.3 js檔案壓縮
使用gulp-uglify
安裝:
npm install --save-dev gulp-uglify
用來壓縮js檔案,使用的是uglify引擎
var gulp = require('gulp'),
uglify = require("gulp-uglify");
gulp.task('minify-js', function () {
gulp.src('js/*.js') // 要壓縮的js檔案
.pipe(uglify()) //使用uglify進行壓縮,更多配置請參考:
.pipe(gulp.dest('dist/js')); //壓縮後的路徑
});
4.4 css檔案壓縮
使用gulp-minify-css
安裝:
npm install --save-dev gulp-minify-css
要壓縮css檔案時可以使用該插件
var gulp = require('gulp'),
minifyCss = require("gulp-minify-css");
gulp.task('minify-css', function () {
gulp.src('css/*.css') // 要壓縮的css檔案
.pipe(minifyCss()) //壓縮css
.pipe(gulp.dest('dist/css'));
});
4.5 html檔案壓縮
使用gulp-minify-html
安裝:
npm install --save-dev gulp-minify-html
用來壓縮html檔案
var gulp = require('gulp'),
minifyHtml = require("gulp-minify-html");
gulp.task('minify-html', function () {
gulp.src('html/*.html') // 要壓縮的html檔案
.pipe(minifyHtml()) //壓縮
.pipe(gulp.dest('dist/html'));
});
4.6 js代碼檢查
使用gulp-jshint
安裝:
npm install --save-dev gulp-jshint
用來檢查js代碼
var gulp = require('gulp'),
jshint = require("gulp-jshint");
gulp.task('jsLint', function () {
gulp.src('js/*.js')
.pipe(jshint())
.pipe(jshint.reporter()); // 輸出檢查結果
});
4.7 檔案合并
使用gulp-concat
安裝:
npm install --save-dev gulp-concat
用來把多個檔案合并為一個檔案,我們可以用它來合并js或css檔案等,這樣就能減少頁面的http請求數了
var gulp = require('gulp'),
concat = require("gulp-concat");
gulp.task('concat', function () {
gulp.src('js/*.js') //要合并的檔案
.pipe(concat('all.js')) // 合并比對到的js檔案并命名為 "all.js"
.pipe(gulp.dest('dist/js'));
});
4.8 less和sass的編譯
less使用gulp-less,安裝:
npm install --save-dev gulp-less
var gulp = require('gulp'),
less = require("gulp-less");
gulp.task('compile-less', function () {
gulp.src('less/*.less')
.pipe(less())
.pipe(gulp.dest('dist/css'));
});
sass使用gulp-sass,安裝:
npm install --save-dev gulp-sass
var gulp = require('gulp'),
sass = require("gulp-sass");
gulp.task('compile-sass', function () {
gulp.src('sass/*.sass')
.pipe(sass())
.pipe(gulp.dest('dist/css'));
});
4.9 圖檔壓縮
可以使用gulp-imagemin插件來壓縮jpg、png、gif等圖檔。
安裝:
npm install --save-dev gulp-imagemin
var gulp = require('gulp');
var imagemin = require('gulp-imagemin');
var pngquant = require('imagemin-pngquant'); //png圖檔壓縮插件
gulp.task('default', function () {
return gulp.src('src/images/*')
.pipe(imagemin({
progressive: true,
use: [pngquant()] //使用pngquant來壓縮png圖檔
}))
.pipe(gulp.dest('dist'));
});
gulp-imagemin的使用比較複雜一點,而且它本身也有很多插件,建議去它的項目首頁看看文檔
4.10 自動重新整理
使用gulp-livereload插件,安裝:
npm install --save-dev gulp-livereload
。
當代碼變化時,它可以幫我們自動重新整理頁面
該插件最好配合谷歌浏覽器來使用,且要安裝livereload chrome extension擴充插件,不能下載下傳的請自行FQ。
var gulp = require('gulp'),
less = require('gulp-less'),
livereload = require('gulp-livereload');
gulp.task('less', function() {
gulp.src('less/*.less')
.pipe(less())
.pipe(gulp.dest('css'))
.pipe(livereload());
});
gulp.task('watch', function() {
livereload.listen(); //要在這裡調用listen()方法
gulp.watch('less/*.less', ['less']);
});
如對gulp還有什麼不明白之處,或者本文有什麼遺漏或錯誤,歡迎一起交流和探讨~