gulp-filter过滤、恢复过滤等文件操作

2019-06-063669次阅读gulp

算是在简单的gulp构建实例+代理服务器基础上的延伸,方便成一体使用。

gulp.task('temp',() => {
    return gulp.src('src/temp/**/*.*')
    .pipe($.if(process.env.production !== undefined, $.smushit()))//压缩图片非常慢,请耐心等待
.pipe(gulp.dest('build/temp'))
    .pipe($.connect.reload());
});

gulp.task('source',() => {
    return gulp.src('src/temp/source/*.*')//.mp3资源
.pipe(gulp.dest('build/temp/source'))
    .pipe($.connect.reload());
});

gulp.task('build',['clean'],function(callback){
    process.env.production = true;
    runSequence(['sass','html','image','temp','js','source'],'watch');
});

当执行build任务时,temp任务gulp.src('src/temp/**/*.*')执行压缩图片操作,偏偏temp子目录中还有一个source目录下全部是mp3资源是无法执行压缩图片操作,结果报错中止任务流完成。为了赶项目进度,临时"曲线救国"新建了一个source任务gulp.src('src/temp/source/*.*')执行文件复制。这不得空了来优化一下,顺便溯源gulp stream(流)做一下简单的整理记录。

gulp.src默认排除

gulp默认支持glob格式,排除语法为!。

gulp.src(['css/**/!(ignore.css)*.css'])
gulp.src(['!css/ignnore.css','css/*.css'])
gulp.src('src/temp/source/*.{png,jpg}')
gulp.src('src/[^_]*.html')

这确实能够排除一些文件,但是还不算强大,不能满足一些复杂的场景。例如开篇那样希望执行压缩图片任务时能跳过mp3资源,同时还要把mp3资源输出到build目录下。还有假如当有*.min.js文件,在压缩js时不想对它进行压缩,但同时又想将其输出到build目录下。所以单纯采用gulp.src默认排除这种方式只能像开篇那样"曲线救国"新建了一个任务出来。

Stream

Gulp是一个有关Stream(数据流)的构建系统。这句话的意思是,Gulp本身使用了Node的Stream。Stream有一个很基本的操作叫做管道(pipe)。Stream是水流,而管道可以从一个流的输出口,接到另一个流的输入口,从而控制流向。

Node的Stream有一个方法pipe(),也就是管道操作对应的方法。它一般这样用:

src.pipe(dst)

其中src和dst都是stream,分别代表源和目标。也就是说,流src的输出,将作为输入转到流dst。此外,这个方法返回目标流(比如这里.pipe(dst)返回dst),因此可以链式调用:

a.pipe(b).pipe(c).pipe(d)

在现在的Node里,Stream被分为4类,分别是Readable(只读)、Writable(只写)、Duplex(双向)、 Transform(转换)。其中Duplex就是指可读可写,而Transform也是Duplex,只不过输出是由输入计算得到的,因此算作Duplex的特例。

Vinyl文件系统

虽然Gulp使用的是Stream,但却不是普通的Node Stream,实际上,Gulp(以及Gulp插件)用的应该叫做Vinyl File Object Stream

这里的Vinyl,是一种虚拟文件格式。Vinyl主要用两个属性来描述文件,它们分别是路径(path)及内容(contents)。具体来说,Vinyl并不神秘,它仍然是JavaScript Object。Vinyl官方给了这样的示例:

var File = require('vinyl');
var coffeeFile = new File({
  cwd: "/",
  base: "/test/",
  path: "/test/file.coffee",
  contents: new Buffer("test = 123")
});

从这段代码可以看出,Vinyl是Object,path和contents也正是这个Object的属性。

Vinyl的意义

Gulp为什么不使用普通的Node Stream呢?请看这段代码:

gulp.task("css", function(){
    gulp.src("./stylesheets/src/**/*.css")
        .pipe(gulp.dest("./stylesheets/dest"));
});

虽然这段代码没有用到任何Gulp插件,但包含了我们最为熟悉的gulp.src()和gulp.dest()。这段代码是有效果的,就是将一个目录下的全部.css文件,都复制到了另一个目录。这其中还有一个很重要的特性,那就是所有原目录下的文件树,包含子目录、文件名等,都原封不动地保留了下来。

普通的Node Stream只传输String或Buffer类型,也就是只关注“内容”。但Gulp不只用到了文件的内容,而且还用到了这个文件的相关信息(比如路径)。因此,Gulp的Stream是Object风格的,也就是Vinyl File Object了。到这里,你也知道了为什么有contents、path这样的多个属性了。

vinyl-fs

Gulp并没有直接使用vinyl,而是用了一个叫做vinyl-fs的模块(和vinyl一样,都是npm)。vinyl-fs相当于vinyl的文件系统适配器,它提供三个方法:.src()、.dest()和.watch(),其中.src()将生成Vinyl File Object,而.dest()将使用Vinyl File Object,进行写入操作。

在Gulp源码index.js中,可以看到这样的对应关系:

var vfs = require('vinyl-fs');
// ...
Gulp.prototype.src = vfs.src;
Gulp.prototype.dest = vfs.dest;
// ...

也就是说,gulp.src()和gulp.dest()直接来源于vinyl-fs。

类型

Vinyl File Object的contents可以有三种类型:Stream、Buffer(二进制数据)、Null(就是JavaScript里的null)。需要注意的是,各类Gulp插件虽然操作的都是Vinyl File Object,但可能会要求不同的类型。

在使用Gulp过程中,可能会碰到incompatible streams的问题,这个问题的原因一般都是Stream与Buffer的类型差异。Stream如前文介绍,特性是可以把数据分成小块,一段一段地传输,Buffer则是整个文件作为一个整体传输。可以想到,不同的Gulp插件做的事情不同,因此可能不支持某一种类型。例如,gulp-uglify这种需要对JavaScript代码做语法分析的,就必须保证代码的完整性,因此,gulp-uglify只支持Buffer类型的Vinyl File Object

gulp.src()方法默认会返回Buffer类型,如果想要Stream类型,可以这样指明:

gulp.src("*.js", {buffer: false})

在Gulp的插件编写指南中,也可以找到Using buffers及Dealing with streams这样两种类型的参考。

Stream转换

为了让Gulp可以更多地利用当前Node生态体系的Stream,出现了许多Stream转换模块。下面介绍一些比较常用的。

vinyl-source-stream

vinyl-source-stream可以把普通的Node Stream转换为Vinyl File Object Stream。这样,相当于就可以把普通Node Stream连接到Gulp体系内。具体用法是:

var fs = require("fs");
var source = require('vinyl-source-stream');
var gulp = require('gulp');
var nodeStream = fs.createReadStream("komari.txt");
nodeStream
    .pipe(source("hotaru.txt"))
    .pipe(gulp.dest("./"));

这段代码中的Stream管道,作为起始的并不是gulp.src(),而是普通的Node Stream。但经过vinyl-source-stream的转换后,就可以用gulp.dest()进行输出。其中source([filename])就是调用转换,我们知道Vinyl至少要有contents和path,而这里的原Node Stream只提供了contents,因此还要指定一个filename作为path。

vinyl-source-stream中的stream,指的是生成的Vinyl File Object,其contents类型是Stream。类似的,还有vinyl-source-buffer,它的作用相同,只是生成的contents类型是Buffer

vinyl-buffer

vinyl-buffer接收Vinyl File Object作为输入,然后判断其contents类型,如果是Stream就转换为Buffer

很多常用的Gulp插件如gulp-sourcemaps、gulp-uglify,都只支持Buffer类型,因此vinyl-buffer可以在需要的时候派上用场。

解决方案

可以采用gulp插件来实现更为强大的排除。主要有以下几个插件:

  • gulp-ignore,支持通过boolean,stat对象,函数来判断是否排除与包含。判断函数接受一个Vinyl文件对象,返回一个boolean值。

  • gulp-filter,支持glob模式、函数过滤,以及过滤后恢复

  • gulp-if,支持条件判断(支持函数条件)来控制相关流程。

gulp-filter

filter(pattern, [options])

Returns a transform stream with a .restore property.

返回具有.restore属性的转换流。

pattern

Type: string Array Function

Accepts a string/array with globbing patterns which are run through multimatch.

接受带有通过multimatch运行的globbing模式的字符串/数组。

If you supply a function, you'll get a vinyl file object as the first argument and you're expected to return a boolean of whether to include the file:

如果提供一个函数,您将得到一个vinyl file object 作为第一个参数,并且期望返回是否包括该文件的布尔值:

filter(file => /unicorns/.test(file.path));

安装gulp-filter

npm i gulp-filter --save-dev

修改后的代码

const condition = file => !/\.mp3/.test(file.path);
//const condition = function(file){
//if(/\.mp3/.test(file.path)){
//return false;
//}
//return true;
// };
const f = $.filter(condition,{restore: true});
gulp.task('temp', () => {
return gulp.src('src/temp/**/*.*')
.pipe(f)
.pipe($.if(process.env.production !== undefined, $.smushit()))//压缩图片非常慢,请耐心等待.pipe(f.restore)
.pipe(gulp.dest('build/temp'))
.pipe($.connect.reload());
});
gulp.task('build',['clean'],function(callback){
    process.env.production = true;
    runSequence(['sass','html','image','temp','js','source'],'watch');
});

参考:

https://www.cnblogs.com/linx/p/6638070.html

https://segmentfault.com/a/1190000003770541

上一篇: Node获取npm命令行参数  下一篇: max-height实现任意高度元素的动画  

gulp-filter过滤、恢复过滤等文件操作相关文章