1. 程式人生 > >前端構建工具gulp超詳細配置, 使用教程(圖文)

前端構建工具gulp超詳細配置, 使用教程(圖文)

流程

1. 輸入命令(可以使用git bash或者命令控制檯cmd) npm install -g gulp

  • 安裝全域性gulp命令

2. 建立一個專案資料夾, 當前專案資料夾下輸入命令npm init

這裡寫圖片描述 
配置package.json檔案, 這一部分看情況自己決定是否填, 不想填也可以, 直接按回車 
這裡寫圖片描述

當前專案資料夾下輸入命令npm install gulp --save-dev

全域性安裝gulp後,還需要在每個要使用gulp的專案中都單獨安裝一次

開始使用gulp

其實, gulp的使用比webpack要簡單很多.

配置gulpflie.js檔案

在當前專案檔案下建立檔名為gulpfile.js檔案, 作為該專案配置檔案.

//gulpfile.js

var gulp = require('gulp');
gulp.task('default',function(){
    console.log('hello world');
});

其實在專案資料夾下輸入命令gulp時, 就是觸發這個default任務, 因此, 我們定義多個自定義事件, 這樣在輸入gulp時, 就可以直接將我們寫的命令也一起觸發.

gulp API

gulp.src(globs[, options])

globs引數是檔案匹配模式(類似正則表示式),用來匹配檔案路徑(包括檔名),當然這裡也可以直接指定某個具體的檔案路徑。當有多個匹配模式時,該引數可以為一個數組。 
options為可選引數。通常情況下我們不需要用到。

下面我們重點說說Gulp用到的glob的匹配規則以及一些檔案匹配技巧。

名稱 說明
* 匹配檔案路徑中的0個或多個字元,但不會匹配路徑分隔符,除非路徑分隔符出現在末尾
** 匹配路徑中的0個或多個目錄及其子目錄,需要單獨出現,即它左右不能有其他東西了。如果出現在末尾,也能匹配檔案。
? 匹配檔案路徑中的一個字元(不會匹配路徑分隔符)
[…] 匹配方括號中出現的字元中的任意一個,當方括號中第一個字元為^或!時,則表示不匹配方括號中出現的其他字元中的任意一個,類似js正則表示式中的用法
!(pattern|pattern|pattern) 匹配任何與括號中給定的任一模式都不匹配的
?(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)

例子:

//轉換html檔案
gulp.task('html', function(){
    gulp.src('./src/index.html')
        .pipe(connect.reload())
        .pipe(gulp.dest('./dist'));//寫入命令
});

當有多種匹配模式時可以使用陣列
//使用陣列的方式來匹配多種檔案
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

gulp.dest(path[,options])

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.src('app/src/**/*.css') //此時base的值為 app/src

上面我們說的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()把檔案流寫入檔案後,檔案流仍然可以繼續使用。


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

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')
  },5000);
});

//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();  //執行回撥,表示這個非同步任務已經完成
  },5000);
});

//這時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();
  }, 5000);
  return deferred.promise;
});

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

gulp.task()就這些了,主要是要知道當依賴是非同步任務時的處理。


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

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()還有另外一種使用方式:

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

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); //變化的檔案的路徑
}); 

一些常用的gulp外掛

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-renamegulp-ruby-sass這兩個外掛的時候,就可以使用plugins.renameplugins.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是通過你的k檔案來載入外掛的,所以必須要保證你需要自動載入的外掛已經寫入到了package.json檔案裡,並且這些外掛都是已經安裝好了的。

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
});

3. js檔案壓縮

使用 : gulp-uglify 
安裝:npm install --save-dev gulp-uglify 
用來壓縮js檔案,使用的是uglify引擎

var gulp = require('gulp');
var uglify = require('gulp-uglify');
var pump = require('pump');

gulp.task('compress', function (cb) {
  pump([
        gulp.src('lib/*.js'),
        uglify(),
        gulp.dest('dist')
    ],
    cb
  );
});

4. css檔案壓縮

使用 : gulp-clean-css 
安裝:npm install gulp-clean-css --save-dev 
要壓縮css檔案時可以使用該外掛

var gulp = require('gulp');
var cleanCSS = require('gulp-clean-css');

gulp.task('minify-css', function() {
  return gulp.src('styles/*.css')
    .pipe(cleanCSS({compatibility: 'ie8'}))
    .pipe(gulp.dest('dist'));
});

5. html檔案壓縮

使用 : gulp-htmlmin 
安裝:npm i gulp-htmlmin --save-dev 
用來壓縮html檔案

var gulp = require('gulp');
var htmlmin = require('gulp-htmlmin');

gulp.task('minify', function() {
  return gulp.src('src/*.html')
    .pipe(htmlmin({collapseWhitespace: true}))
    .pipe(gulp.dest('dist'));
});

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()); // 輸出檢查結果
});

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'));
});

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'));
});

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的使用比較複雜一點,而且它本身也有很多外掛,建議去它的專案主頁看看文件

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']);
});

11. 伺服器

使用 : gulp-connect外掛, 
安裝 : npm install --save-dev gulp-connect 
Gulp plugin to run a webserver (with LiveReload), 開伺服器,並集成了LiveReload(自動重新整理), 下載這個外掛上面的LiverReload外掛就不用再下載了.

var gulp = require('gulp'),
  connect = require('gulp-connect');

gulp.task('connect', function() {
  connect.server({
    root: 'app',
    livereload: true
  });
});