學習前端自動化構建工具Gulp

司想君發表於2016-03-02

Gulp是什麼?

gulp是前端開發過程中一種基於的程式碼構建工具,是自動化專案的構建利器;她不僅能對網站資源進行優化,而且能幫我們完成前端開發過程中的很多重複的任務,使用它,可以大大提高我們的開發效率。

gulp是基於NodeJs的自動任務執行器,她能自動化的完成JavaScript、coffee、sass、less、html/img、css等檔案的測試、檢查、合併、壓縮、格式化、瀏覽器自動重新整理、部署檔案生成,並監聽檔案改動後重復這些步驟。在實現上,她借鑑了Unix作業系統的管道思想,前一級的輸出,直接變成後一級的輸入,使得在操作上非常簡單。

什麼是流?

,簡單來說就是建立在物件導向基礎上的一種抽象的資料處理工具。在流中,定義了一些處理資料的基本操作,如讀取資料,寫入資料等,程式設計師是對流進行所有操作的,而不用關心流的另一頭資料的真正流向。流不但可以處理檔案,還可以處理動態記憶體、網路資料等多種資料形式。

gulp正是通過流和程式碼優於配置的策略來儘量簡化任務編寫的工作。當使用流時,gulp去除了中間檔案,只將最後的輸出寫入磁碟,整個過程因此變得更快。

Gulp的特點

  • 易於使用:通過程式碼優於配置的策略,gulp 讓簡單的任務簡單,複雜的任務可管理。

  • 構建快捷:利用 Node.js 流的威力,你可以快速構建專案並減少頻繁的 IO 操作。

  • 易於學習:通過最少的 API,掌握 gulp 毫不費力,構建工作盡在掌握:如同一系列流管道。

  • 外掛高質:gulp 嚴格的外掛指南確保外掛如你期望的那樣簡潔高質得工作。

安裝

首先全域性安裝gulp:

npm install -g gulp

全域性安裝gulp後,還需要在每個要使用gulp的專案中都單獨安裝一次。把目錄切換到你的專案資料夾中,然後在命令列中執行:

npm install gulp

如果想在安裝的時候把gulp寫進專案package.json檔案的依賴中,則可以加上--save-dev

npm install --save-dev gulp

這樣就完成了gulp的安裝,接下來就可以在專案中應用gulp了。

gulp的使用

一、建立gulpfile.js檔案

gulp也需要一個檔案作為它的主檔案,在gulp中這個檔案叫做gulpfile.js。新建一個檔名為gulpfile.js的檔案,然後放到你的專案目錄中。之後要做的事情就是在gulpfile.js檔案中定義我們的任務了。下面是一個最簡單的gulpfile.js檔案內容示例,它定義了一個預設的任務。

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

二、執行gulp任務

要執行gulp任務,只需切換到存放gulpfile.js檔案的目錄(windows平臺請使用cmd或者Power Shell等工具),然後在命令列中執行gulp命令就行了,gulp後面可以加上要執行的任務名,例如gulp task1,如果沒有指定任務名,則會執行任務名為default的預設任務。

工作方式

在介紹gulp API之前,我們首先來說一下gulp.js工作方式。在gulp中,使用的是Nodejs中的stream(流),首先獲取到需要的stream,然後可以通過stream的pipe()方法把流匯入到你想要的地方,比如gulp的外掛中,經過外掛處理後的流又可以繼續匯入到其他外掛中,當然也可以把流寫入到檔案中。所以gulp是以stream為媒介的,它不需要頻繁的生成臨時檔案,這也是我們應用gulp的一個原因。

gulp的使用流程一般是:首先通過gulp.src()方法獲取到想要處理的檔案流,然後把檔案流通過pipe方法匯入到gulp的外掛中,最後把經過外掛處理後的流再通過pipe方法匯入到gulp.dest()中,gulp.dest()方法則把流中的內容寫入到檔案中。例如:

var gulp = require('gulp');
gulp.src('script/jquery.js')         // 獲取流的api
    .pipe(gulp.dest('dist/foo.js')); // 寫放檔案的api

接下來我們將為大家講解gulp api,包括gulp.src(),gulp.task(),gulp.dest(),gulp.watch(),gulp.run()

src

gulp.src()方法正是用來獲取流的,但要注意這個流裡的內容不是原始的檔案流,而是一個虛擬檔案物件流(Vinyl files),這個虛擬檔案物件中儲存著原始檔案的路徑、檔名、內容等資訊。其語法為:

gulp.src(globs[, options]);

globs引數是檔案匹配模式(類似正規表示式),用來匹配檔案路徑(包括檔名),當然這裡也可以直接指定某個具體的檔案路徑。當有多個匹配模式時,該引數可以為一個陣列;型別為StringArray
當有多種匹配模式時可以使用陣列:

//使用陣列的方式來匹配多種檔案
gulp.src(['js/*.js','css/*.css','*.html'])

options為可選引數。以下為options的選項引數:

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

options.read

型別: Boolean 預設值: true

如果該項被設定為 false, 那麼 file.contents 會返回空值(null),也就是並不會去讀取檔案。

options.base

型別: String , 設定輸出路徑以某個路徑的某個組成部分為基礎向後拼接。

如, 請想像一下在一個路徑為 client/js/somedir 的目錄中,有一個檔案叫 somefile.js

gulp.src('client/js/**/*.js') // 匹配 'client/js/somedir/somefile.js' 現在 `base` 的值為 `client/js/`
  .pipe(minify())
  .pipe(gulp.dest('build'));  // 寫入 'build/somedir/somefile.js' 將`client/js/`替換為build
 
gulp.src('client/js/**/*.js', { base: 'client' }) // base 的值為 'client'
  .pipe(minify())
  .pipe(gulp.dest('build'));  // 寫入 'build/js/somedir/somefile.js' 將`client`替換為build

dest

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

gulp.dest(path[,options])

path為寫入檔案的路徑;

options為一個可選的引數物件,以下為選項引數:

options.cwd

型別: String 預設值: process.cwd()

輸出目錄的 cwd 引數,只在所給的輸出目錄是相對路徑時候有效。

options.mode

型別: String 預設值: 0777

八進位制許可權字元,用以定義所有在輸出目錄中所建立的目錄的許可權。

var gulp = require('gulp');
gulp.src('script/jquery.js')        // 獲取流
    .pipe(gulp.dest('dist/foo.js')); // 寫放檔案

下面再說說生成的檔案路徑與我們給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.dest()把檔案流寫入檔案後,檔案流仍然可以繼續使用。

watch

gulp.watch()用來監視檔案的變化,當檔案發生變化後,我們可以利用它來執行相應的任務,例如檔案壓縮等。其語法為:

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

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

globopts引數與第一種用法相同;

cb引數為一個函式。每當監視的檔案發生變化時,就會呼叫這個函式,並且會給它傳入一個物件,該物件包含了檔案變化的一些資訊,type屬性為變化的型別,可以是added,changed,deletedpath屬性為發生變化的檔案的路徑。

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

task

gulp.task方法用來定義任務,內部使用的是Orchestrator(用於排序、執行任務和最大併發依賴關係的模組),其語法為:

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

name 為任務名;

deps 是當前定義的任務需要依賴的其他任務,為一個陣列。當前定義的任務會在所有依賴的任務執行完畢後才開始執行。如果沒有依賴,則可省略這個引數;

fn 為任務函式,我們把任務要執行的程式碼都寫在裡面。該引數也是可選的。

當你定義一個簡單的任務時,需要傳入任務名字執行函式兩個屬性。

gulp.task('greet', function () {
   console.log('Hello world!');
});

執行gulp greet的結果就是在控制檯上列印出“Hello world”。

  你也可以定義一個在gulp開始執行時候預設執行的任務,並將這個任務命名為“default”:

gulp.task('default', function () {
   // Your default task
});

前面已經介紹了gulp.task的語法,但是當有多個任務時,需要知道怎麼來控制任務的執行順序。

  可以通過任務依賴來實現。例如我想要執行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是一個非同步執行的任務
  exec(function(){
    console.log('one is finish');
    cb();  //執行回撥,表示這個非同步任務已經完成
  },5000);
});
 
//這時two任務會在one任務中的非同步操作完成後再執行
gulp.task('two',['one'],function(){
  console.log('two is finish');
});

第二:定義任務時返回一個流物件。適用於任務就是操作gulp.src獲取到的流的情況。

gulp.task('one',function(cb){
  var stream = gulp.src('client/**/*.js')
      .pipe(exec()) //exec()中有某些非同步操作
      .pipe(gulp.dest('build'));
    return stream;
});
 
gulp.task('two',['one'],function(){
  console.log('two is done');
});

第三:返回一個promise物件,例如

var Q = require('q');
gulp.task('one', function() {
  var deferred = Q.defer();
 
  // 執行非同步的操作
  setTimeout(function() {
    deferred.resolve();
  }, 1);
  return deferred.promise;
});
 
gulp.task('two',['one'],function(){
  console.log('two is done');
});

run

gulp.run()表示要執行的任務。可能會使用單個引數的形式傳遞多個任務。如下程式碼:

gulp.task('end',function(){
gulp.run('task1','task3','task2');
});

注意:任務是儘可能多的並行執行的,並且可能不會按照指定的順序執行。

外掛安裝

在我們編寫gulp程式碼時候,需要用到一些gulp的外掛,可以通過npm install --save-dev 外掛名稱 來安裝。如下程式碼來安裝自動載入外掛:

npm install --save-dev gulp-load-plugins

要使用gulp的外掛,首先得用require來把外掛載入進來。

var gulp=require('gulp'),
    plugins=require('gulp-load-plugins')(),
    uglify = require('gulp-uglify'),
    minifyHtml = require('gulp-minify-html'),
    rename = require('gulp-rename');

gulp的外掛有很多種,我們將在本節內容中為同學們講解幾個外掛的用法。如還想了解更多外掛,請查閱相關資料。

自動載入

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.rename和plugins.rubySass來代替了,也就是原始外掛名去掉gulp-字首,之後再轉換為駝峰命名。

重新命名

gulp-rename外掛用來重新命名檔案流中的檔案。用gulp.dest()方法寫入檔案時,檔名使用的是檔案流中的檔名,如果要想改變檔名,那可以在之前用gulp-rename外掛來改變檔案流中的檔名。

var gulp = require('gulp'),
    rename = require('gulp-rename'),
    uglify = require("gulp-uglify");
 
gulp.task('rename', function () {
    gulp.src('src/1.js')
    .pipe(uglify())           //壓縮
    .pipe(rename('1.min.js')) //會將1.js重新命名為1.min.js
    .pipe(gulp.dest('js'));
});

js檔案壓縮

gulp-uglify外掛用來壓縮js檔案。

var gulp = require('gulp'),
    uglify = require("gulp-uglify");
 
gulp.task('minify-js', function () {
    gulp.src('src/*.js')          // 要壓縮的js檔案
    .pipe(uglify())              //使用uglify進行壓縮
    .pipe(gulp.dest('dist/js')); //壓縮後的路徑
});

css檔案壓縮

gulp-minify-css外掛用來壓縮css檔案。

var gulp = require('gulp'),
    minifyCss = require("gulp-minify-css");
 
gulp.task('minify-css', function () {
    gulp.src('src/*.css') // 要壓縮的css檔案
    .pipe(minifyCss())    //壓縮css
    .pipe(gulp.dest('dist/css'));
});

html檔案壓縮

gulp-minify-html外掛用來壓縮html檔案。

var gulp = require('gulp'),
    minifyHtml = require("gulp-minify-html");
 
gulp.task('minify-html', function () {
    gulp.src('src/*.html') // 要壓縮的html檔案
    .pipe(minifyHtml())    //壓縮
    .pipe(gulp.dest('dist/html'));
});

js程式碼檢查

使用gulp-jshint外掛,用來檢查js程式碼。

var gulp = require('gulp'),
    jshint = require("gulp-jshint");
 
gulp.task('jsLint', function () {
    gulp.src('src/*.js')
    .pipe(jshint())
    .pipe(jshint.reporter()); // 輸出檢查結果
});

檔案合併

使用gulp-concat外掛,用來把多個檔案合併為一個檔案,我們可以用它來合併jscss檔案等。

var gulp = require('gulp'),
    concat = require("gulp-concat");
gulp.task('concat', function () {
    gulp.src('src/*.js')     //要合併的檔案
    .pipe(concat('all.js'))  // 合併匹配到的js檔案並命名為 "all.js"
    .pipe(gulp.dest('dist/js'));
});

圖片壓縮

可以使用gulp-imagemin外掛來壓縮jpg、png、gif等圖片。

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-livereload外掛,當程式碼變化時,它可以幫我們自動重新整理頁面。

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的基本用法都已經講解完了,最重要的還是在專案中多多使用才能儘快掌握。本文來源於筆者學習匯智網課堂的學習筆記。原課程作者:草莓。

相關文章