前端開發近兩年工程化大幅飆升。隨着Nodejs
大放異彩,靜態文件處理再也不須要其餘語言輔助。主要的兩大工具即爲基於文件的grunt
,基於流的gulp
。簡單來講,若是須要的只是文件處理,gulp
絕對首選。若是是其餘依賴於文件的任務管理,例如測試(karma
,mocha
),推薦使用grunt
。html
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遠低於grunt。若是你只關注如何處理文件,而不關注細節,那麼須要依賴Nodejs Transform stream的實現。可使用官方推薦的through2,但推薦使用through-gulp。後者是基於前者,爲gulp插件編寫精簡優化重寫而來。千萬不要使用through,這個包時間久遠,長時間沒有維護,並且部分mock實現的功能,到nodejs 0.10.x已經原生支持。若是隻是想學習如何編寫gulp插件,through-gulp更適合。 node
through-gulp: https://github.com/bornkiller/through-gulp
through2: https://github.com/rvagg/through2.git
through: https://github.com/dominictarr/throughgit
依賴APIgithub
var through = require('through-gulp'); var stream = through(transformFunction, flushFunction);
結構 npm
// 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;
這裏gulp
through(function(file, encoding,callback){})發現file是一個對象,含有以下許多屬性,可是咱們經常使用的一般是file.path獲取文件路徑,file.contents獲取文件內容
使用:api
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就能夠直接單向使用。app
下面來看一下簡單的 gulp-pf-replace插件,理解原理:dom
//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 是一個對 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實戰 理解gulp的相關原理