前端开发近两年工程化大幅飙升。随着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/,"")
}

  上面注解比较多,应该大多数人看得懂,这里我就不再做解释。

三、利用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的相关原理

教你写gulp plugin的更多相关文章

  1. Flutter实战:手把手教你写Flutter Plugin

    前言 如果你对移动端有所关注,那么你一定会听说过Flutter.得益于Google,Flutter一经推出便得受到了广泛关注.很多开发者跃跃欲试,国内部分大厂,诸如美团.闲鱼等团队已经开始了Flutt ...

  2. Android开发之手把手教你写ButterKnife框架(二)

    欢迎转载,转载请标明出处: http://blog.csdn.net/johnny901114/article/details/52664112 本文出自:[余志强的博客] 上一篇博客Android开 ...

  3. 半个小时教你写一个装(bi)逼(she)之地图搜租房

    半个小时教你写一个装(bi)逼(she)之地图搜租房 首先需要一个Python3环境,怎么准备我就不多说了,实在不会的出门右转看一下廖雪峰老师的博客. HTML部分 代码来自:高德API+Python ...

  4. 手把手教你写DI_0_DI是什么?

    DI是什么? Dependency Injection 常常简称为:DI. 它是实现控制反转(Inversion of Control – IoC)的一个模式. fowler 大大大神 "几 ...

  5. 手把手教你写Sublime中的Snippet

    手把手教你写Sublime中的Snippet Sublime Text号称最性感的编辑器, 并且越来越多人使用, 美观, 高效 关于如何使用Sublime text可以参考我的另一篇文章, 相信你会喜 ...

  6. 看过《大湿教我写.net通用权限框架(1)之菜单导航篇》之后发生的事(续)——主界面

    引言 在UML系列学习中的小插曲:看过<大湿教我写.net通用权限框架(1)之菜单导航篇>之后发生的事 在上篇中只拿登录界面练练手,不把主界面抠出来,实在难受,严重的强迫症啊.之前一直在总 ...

  7. 手把手教你写LKM rookit! 之 第一个lkm程序及模块隐藏(一)

    唉,一开始在纠结起个什么名字,感觉名字常常的很装逼,于是起了个这<手把手教你写LKM rookit> 我觉得: 你们觉得:...... 开始之前,我们先来理解一句话:一切的操作都是系统调用 ...

  8. 手把手教你写电商爬虫-第三课 实战尚妆网AJAX请求处理和内容提取

    版权声明:本文为博主原创文章,未经博主允许不得转载. 系列教程: 手把手教你写电商爬虫-第一课 找个软柿子捏捏 手把手教你写电商爬虫-第二课 实战尚妆网分页商品采集爬虫 看完两篇,相信大家已经从开始的 ...

  9. 手把手教你写电商爬虫-第四课 淘宝网商品爬虫自动JS渲染

    版权声明:本文为博主原创文章,未经博主允许不得转载. 系列教程: 手把手教你写电商爬虫-第一课 找个软柿子捏捏 手把手教你写电商爬虫-第二课 实战尚妆网分页商品采集爬虫 手把手教你写电商爬虫-第三课 ...

随机推荐

  1. spring原理机制

    转自:http://blog.csdn.net/nrain2/article/details/45459311 1,关于spring容器: spring容器是Spring的核心,该 容器负责管理spr ...

  2. Can't clobber writable file **************

    最近搭建了新的quick check server, workspace也是新的.但是get latest (unshelve)的时候,出现以下错误: can't clobber writable f ...

  3. mySql慢查询分析原因

    1.分析查询慢的语句,并记录到日志中 查看: http://blog.csdn.net/haiqiao_2010/article/details/25138099

  4. linux中sudo如何读取标准输入作为密码,避免每次都输入密码?

    需求描述: 今天想要在生产环境中,弄自动部署的脚本,但是现在呢,需要sudo权限,每次都要输入.就想看sudo如何能从标准输入读取密码. 操作过程: 1.原来的方法 [deployer@testvm ...

  5. python使用dbutils的PooledDB连接池,操作数据库

    1.使用dbutils的PooledDB连接池,操作数据库. 这样就不需要每次执行sql后都关闭数据库连接,频繁的创建连接,消耗时间 2.如果是使用一个连接一直不关闭,多线程下,插入超长字符串到数据库 ...

  6. WinForm创建自定义控件

    虽然VS为我们提供了很多控件可以使用,但有时候这些控件仍然不能满足我们的要求,比如我们要对部分控件进行一些个性化的定制,例如美化控件,这时候就需要自己绘制控件,或是在原有控件的基础上进行修改 自定义控 ...

  7. 【代码审计】QYKCMS_v4.3.2 任意文件读取漏洞分析

      0x00 环境准备 QYKCMS官网:http://www.qykcms.com/ 网站源码版本:QYKCMS_v4.3.2(企业站主题) 程序源码下载:http://bbs.qingyunke. ...

  8. 【安全开发】IOS安全编码规范

    申明:本文非笔者原创,原文转载自:https://github.com/SecurityPaper/SecurityPaper-web/blob/master/_posts/2.SDL%E8%A7%8 ...

  9. Nginx(十)-- 进程模型及工作原理

    1.nginx进程模型 Nginx是一个master和worker的模型.master主要用来管理worker进程,master就比作老板,worker就是打工仔,master指挥worker来做事情 ...

  10. Kafka producer拦截器(interceptor)

    Producer拦截器(interceptor)是个相当新的功能,它和consumer端interceptor是在Kafka 0.10版本被引入的,主要用于实现clients端的定制化控制逻辑. 对于 ...