教你写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获取文件内容
使用:
- 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实战 理解gulp的相关原理
教你写gulp plugin的更多相关文章
- Flutter实战:手把手教你写Flutter Plugin
前言 如果你对移动端有所关注,那么你一定会听说过Flutter.得益于Google,Flutter一经推出便得受到了广泛关注.很多开发者跃跃欲试,国内部分大厂,诸如美团.闲鱼等团队已经开始了Flutt ...
- Android开发之手把手教你写ButterKnife框架(二)
欢迎转载,转载请标明出处: http://blog.csdn.net/johnny901114/article/details/52664112 本文出自:[余志强的博客] 上一篇博客Android开 ...
- 半个小时教你写一个装(bi)逼(she)之地图搜租房
半个小时教你写一个装(bi)逼(she)之地图搜租房 首先需要一个Python3环境,怎么准备我就不多说了,实在不会的出门右转看一下廖雪峰老师的博客. HTML部分 代码来自:高德API+Python ...
- 手把手教你写DI_0_DI是什么?
DI是什么? Dependency Injection 常常简称为:DI. 它是实现控制反转(Inversion of Control – IoC)的一个模式. fowler 大大大神 "几 ...
- 手把手教你写Sublime中的Snippet
手把手教你写Sublime中的Snippet Sublime Text号称最性感的编辑器, 并且越来越多人使用, 美观, 高效 关于如何使用Sublime text可以参考我的另一篇文章, 相信你会喜 ...
- 看过《大湿教我写.net通用权限框架(1)之菜单导航篇》之后发生的事(续)——主界面
引言 在UML系列学习中的小插曲:看过<大湿教我写.net通用权限框架(1)之菜单导航篇>之后发生的事 在上篇中只拿登录界面练练手,不把主界面抠出来,实在难受,严重的强迫症啊.之前一直在总 ...
- 手把手教你写LKM rookit! 之 第一个lkm程序及模块隐藏(一)
唉,一开始在纠结起个什么名字,感觉名字常常的很装逼,于是起了个这<手把手教你写LKM rookit> 我觉得: 你们觉得:...... 开始之前,我们先来理解一句话:一切的操作都是系统调用 ...
- 手把手教你写电商爬虫-第三课 实战尚妆网AJAX请求处理和内容提取
版权声明:本文为博主原创文章,未经博主允许不得转载. 系列教程: 手把手教你写电商爬虫-第一课 找个软柿子捏捏 手把手教你写电商爬虫-第二课 实战尚妆网分页商品采集爬虫 看完两篇,相信大家已经从开始的 ...
- 手把手教你写电商爬虫-第四课 淘宝网商品爬虫自动JS渲染
版权声明:本文为博主原创文章,未经博主允许不得转载. 系列教程: 手把手教你写电商爬虫-第一课 找个软柿子捏捏 手把手教你写电商爬虫-第二课 实战尚妆网分页商品采集爬虫 手把手教你写电商爬虫-第三课 ...
随机推荐
- 【Linux】理解分区
http://blog.csdn.net/aaronychen/article/details/2270048 主分区逻辑分区设置 http://forum.ubuntu.org.cn/viewtop ...
- tooltips插件
摘要: 继‘带箭头提示框’,本文将分享几款带箭头提示框. qtipqTip是一种先进的提示插件,基于jQuery框架.以用户友好,而且功能丰富,qTip为您提供不一般的功能,如圆角和语音气泡提示,并且 ...
- python 函数结果缓存一段时间的装饰器
把函数结果缓存一段时间,比如读取一个mongodb,mongodb中的内容又在发生变化,如果从部署后,自始至终只去读一次那就感触不到变化了,如果每次调用一个函数就去读取那太频繁了耽误响应时间也加大了c ...
- python内存泄漏,python垃圾手动回收,1
部署的舆情系统,内存变大,找原因. 一个小例子. def func(): local_list = list(range(10000000)) func() time.sleep(200) 能够观察到 ...
- [OpenCV] IplImage and Operation
IplImage 一.资源 In this chapter, APIs will make U crazy. Good luck! Next, Review Linear Algebra. Ref: ...
- c#中如何退出程序后自动重新启动程序
//触发退出程序事件 private void button1_Click(object sender, EventArgs e) { Application.E ...
- SQLServer------远程调用失败
1.情况 出现 2.解决方法 打开“控制面板” -> “卸载程序” -> 找到 “Microsoft SQL Server 2016) ExpressLocalDB”将其卸载 -> ...
- JAVA简单内存泄露分析及解决
一.问题产生 项目采用Tomcat6.0为服务器,数据库为mysql5.1,数据库持久层为hibernate3.0,以springMVC3.0为框架,项目开发完成后,上线前夕进行稳定性拷机,测试 ...
- c#事件Unity与.Net对比
今天在看Unity3d的书,发现上面的调用事件比较特殊,比如说按钮事件 该方法写在OnGUI方法中if(GUILayout.Button("按钮1")) { //执行事件的处理 } ...
- spring+hibernate 下载
http://www.cnblogs.com/haogj/archive/2012/07/28/nhibernate.html-------原文 http://www.springframework. ...