教你寫gulp plugin


  前端開發近兩年工程化大幅飆升。隨着Nodejs大放異彩,靜態文件處理不再需要其他語言輔助。主要的兩大工具即為基於文件的grunt,基於流的gulp。簡單來說,如果需要的只是文件處理,gulp絕對首選。如果是其他依賴於文件的任務管理,例如測試(karmamocha),推薦使用grunt

  gulp常用api:

gulp.src(globs[,options])

gulp.dest(path[,options])

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

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

gulp.start(["param"]);

一、gulp plugin開發依賴

  就插件開發難度而言,gulp遠低於grunt。如果你只關注如何處理文件,而不關注細節,那么需要依賴Nodejs Transform stream的實現。可以使用官方推薦的through2,但推薦使用through-gulp。后者是基於前者,為gulp插件編寫精簡優化重寫而來。千萬不要使用through,這個包時間久遠,長時間沒有維護,而且部分mock實現的功能,到nodejs 0.10.x已經原生支持。如果只是想學習如何編寫gulp插件,through-gulp更適合。 

  through-gulp: https://github.com/bornkiller/through-gulp
  through2: https://github.com/rvagg/through2.git
  through: https://github.com/dominictarr/through

二、利用through-gulp開發gulp plugin

 依賴API

var through = require('through-gulp');
var stream = through(transformFunction, flushFunction);

 結構 

// PLUGIN_NAME: sample 
var through = require('through-gulp');
 
function sample() {
  //通過through創建流stream
  var stream = through(function(file, encoding,callback) {
  
    //進程文件判斷
    if (file.isNull()) {
 
    }
    if (file.isBuffer()) {
 
    }
    if (file.isStream()) {
 
    }
    // just pipe data next, or just do nothing to process file later in flushFunction 
    // never forget callback to indicate that the file has been processed. 
      this.push(file);
      callback();
    },function(callback) {
      // just pipe data next, just callback to indicate that the stream's over 
      this.push(something);
      callback();
    });
 
  //返回這個流文件
  return stream;
};
 
// exporting the plugin  
module.exports = sample;

 這里

through(function(file, encoding,callback){})發現file是一個對象,含有如下許多屬性,但是我們常用的通常是file.path獲取文件路徑,file.contents獲取文件內容

 
        

 使用:

var gulp = require('gulp');
var sample = require('sample');
gulp.task('sample', function() {
  return gulp.src(['source file'])
    .pipe(sample())
    .pipe(gulp.dest('file destiny'))
});

  從以上我們可以看到,through-gulp插件寫法,其實就是讀取轉換流,存儲流,導出流的一個過程(一個文件一個文件的過去),如果我們不需要導出流進行鏈式寫法,其實直接module.exports = sample就可以直接單向使用。

  下面來看一下簡單的 gulp-pf-replace插件,理解原理:

//Gulp默認使用buffer

var through = require("through-gulp");  //引入gulp插件模塊
var fs = require("fs");
var http = require("http");
var request = require("request");
var path = require("path");
var source = require('vinyl-source-stream'); //常規流轉換為gulp支持的Vinyl文件格式
var gutil = require('gulp-util'); 
 //gulp多功能的插件,可以替換擴展名,log顏色日志,模板
 
var chalk = require('chalk'); //設置顏色
chalk.blue('Hello world!');

// 類型判斷
function isType(type){
    return function(o){
        return Object.prototype.toString.crall(o) === '[object ' + type + ']';
    }
}

var isString = isType("String");
var isObject = isType("Object");
var isArray = isType("Array");

gutil.log('stuff happened', 'Really it did', gutil.colors.magenta('123'));

var i=0;
//gulp插件原理就是一個流進入,流處理完出來
function replace(modReplace) {
	
  //通過through創建流stream
  var stream = through(function(file, encoding,callback) {
	//file為對象,含有path,clone,pipe,inspect,history,isNull,isDirectory 等,常用的是path
	//console.log(isObject(file));
	
    //進程文件判斷
    if (file.isNull()) {
		throw "NO Files,Please Check Files!"
    }
	
	//buffer對象可以操作
    if (file.isBuffer()) {
		//拿到單個文件buffer
		var content = modReplace(file.contents.toString("utf-8"));
		
		//console.log(contents);
		file.contents = new Buffer(content,"utf-8");
		//可以通過buffer.toString("utf-8")轉換成字符串
		//contents = file.contents.toString("utf-8")
    }
	
	//stream流是不能操作的,可以通過fs.readFileSync
    if (file.isStream()) {
		//同步讀取
		 var content = modReplace(fs.readFileSync(file.path).toString("utf-8"));
		 file.contents = new Buffer(content,"utf-8");
    }
	
    // just pipe data next, or just do nothing to process file later in flushFunction 
    // never forget callback to indicate that the file has been processed. 
      this.push(file);
      callback();
	  i++;
    },function(callback) {
		gutil.log( gutil.colors.red(i) ,  gutil.colors.green("已經處理完畢!"));
      // just pipe data next, just callback to indicate that the stream's over 
     // this.push(something);
      callback();
    });
	
  //返回這個流文件
  return stream;
};
 
// 導出插件 
module.exports = replace;

  使用:

gulp.task("pfDefault",function(){
    return gulp.src("./tianzun/*.+(html|htm)",{buffer: true})
            .pipe(pfDefault(ypReplace))
            .pipe(gulp.dest("./out"))
       .on("finish",function(){
          console.log("處理完成")
       }) });
//替換方法 function ypReplace(data){ return data.replace(/helloword/,"123") }

  上面注解比較多,應該大多數人看得懂,這里我就不再做解釋。

三、利用through2開發gulp plugin

  結構如下:

// through2 是一個對 node 的 transform streams 簡單封裝
var through = require('through2');
var gutil = require('gulp-util');
var PluginError = gutil.PluginError;

// 常量
const PLUGIN_NAME = 'gulp-prefixer';

function prefixStream(prefixText) {
  var stream = through();
  stream.write(prefixText);
  return stream;
}

// 插件級別函數 (處理文件)
function gulpPrefixer(prefixText) {

  if (!prefixText) {
    throw new PluginError(PLUGIN_NAME, 'Missing prefix text!');
  }
  prefixText = new Buffer(prefixText); // 預先分配

  // 創建一個讓每個文件通過的 stream 通道
  return through.obj(function(file, enc, cb) {
    if (file.isNull()) {
      // 返回空文件
      cb(null, file);
    }
    if (file.isBuffer()) {
      file.contents = Buffer.concat([prefixText, file.contents]);
    }
    if (file.isStream()) {
      file.contents = file.contents.pipe(prefixStream(prefixText));
    }

    cb(null, file);

  });

};

// 暴露(export)插件主函數
module.exports = gulpPrefixer;

 

推薦閱讀:

  Gulp思維——Gulp高級技巧  理解gulp底層處理是buffer、還是Vinyl文件格式流

  編寫gulp指導

  through-gulp插件

   從零單排之gulp實戰 理解gulp的相關原理


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM