天天看點

教你寫gulp plugin

  前端開發近兩年工程化大幅飙升。随着

Nodejs

大放異彩,靜态檔案處理不再需要其他語言輔助。主要的兩大工具即為基于檔案的

grunt

,基于流的

gulp

。簡單來說,如果需要的隻是檔案處理,

gulp

絕對首選。如果是其他依賴于檔案的任務管理,例如測試(

karma

mocha

),推薦使用

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擷取檔案内容
           
教你寫gulp plugin

 使用:

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的相關原理

轉載于:https://www.cnblogs.com/pingfan1990/p/4809128.html