Gulp头脑——Gulp高等技能

本文翻译自Getting gulpy — Advanced tips for using gulp.js

感受过gulp.js带来的高兴事后,你须要的不仅仅是它的鲜明,而是切切实实的实例。这篇文章议论了一些运用gulp.js经常踩的坑,以及一些越发高等和定制化的插件和流的运用技能。

基本使命

gulp的基本设置具有异常友爱的语法,让你能够异常随意马虎的对文件举行转换:

gulp.task('scripts', function() {
    return gulp.src('./src/**/*.js')
        .pipe(uglify())
        .pipe(concat('all.min.js'))
        .pipe(gulp.dest('build/'));
});

这类体式格局能够敷衍绝大多数状况,但假如你须要更多的定制,很快就会碰到麻烦了。这篇将引见这个中的一些状况并供应解决方案。

流不兼容?

运用gulp时,你能够会堕入“流不兼容”的题目。这重要是因为通例流和Vinyl文件对象有差别,或是运用了仅支撑buffer(不支撑流)库的gulp插件与通例流不兼容。

比方说,你不能直接将通例流与gulp和(或)gulp插件相连。我们建立一个可读流,并尝试运用gulp-uglifygulp-rename来举行转换,将末了取得的内容交给gulp.dest()。下面就是个毛病的例子:

var uglify = require('gulp-uglify'),
    rename = require('gulp-rename');
gulp.task('bundle', function() {
    return fs.createReadStream('app.js')
        .pipe(uglify())
        .pipe(rename('bundle.min.js'))
        .pipe(gulp.dest('dist/'));
});

为何我们不能将可读流和一个gulp插件直接相连?gulp岂非不就是一个基于流的构建体系吗?是的,但上面的例子无视了一个现实,gulp插件希冀的输入是Vinyl文件对象。你不能直接将一个可读流与一个以Vinyl文件对象作为输入的函数(插件)相连

Vinyl文件对象

gulp运用了vinyl-fs,它完成了gulp.src()gulp.dest()要领。vinyl-fs运用vinyl文件对象——一种“假造文件花样”。假如我们须要将gulp和(或)gulp插件与通例的可读流一同运用,我们就须要先把可读流转换为vinyl。

运用vinyl-source-stream是个不错的挑选,以下:

var source = require('vinyl-source-stream'),
    marked = require('gulp-marked');
fs.createReadStream('*.md')
    .pipe(source())
    .pipe(marked())
    .pipe(gulp.dest('dist/'));

别的一个例子起首经由过程browserify封装并终究将其转换为一个vinyl流:

var browserify = require('browserify'),
    uglify = require('gulp-uglify'),
    source = require('vinyl-source-stream');
gulp.task('bundle', function() {
    return browserify('./src/app.js')
        .bundle()
        .pipe(source(‘bundle.min.js))
        .pipe(uglify())
        .pipe(gulp.dest('dist/'));
});

哎呦不错哦。注重我们不再须要运用gulp-rename了,因为vinyl-source-stream建立了一个具有指定文件名的vinyl文件实例(如许gulp.dest要领将运用这个文件名)

gulp.dest

这个gulp要领建立了一个可写流,它真的很随意马虎。它从新运用可读流中的文件名,然后在必要时建立文件夹(运用mkdirp)。在写入操纵完成后,你能够继续运用这个流(比方:你须要运用gzip紧缩数据并写入到其他文件)

流和buffer

既然你有兴致运用gulp,这篇文章假定你已相识了流的基本知识。无论是buffer照样流,vinyl的假造文件都能包括在内。运用通例可读流时,你能够监听data事宜来检测数据碎片的到来:

fs.createReadStream('/usr/share/dict/words').on('data', function(chunk) {
    console.log('Read %d bytes of data', chunk.length);
});
> Read 65536 bytes of data
> Read 65536 bytes of data
> Read 65536 bytes of data
> Read 65536 bytes of data
> ...

差别的是,运用gulp.src()会将转换成buffer的vinyl文件对象从新写入到流中。也就是说,你取得的不再是数据碎片,而是将内容转换成buffer后的(假造)文件。vinyl文件花样具有一个属性来示意内里是buffer照样流,gulp默许运用buffer:

gulp.src('/usr/share/dict/words').on('data', function(file) {
    console.log('Read %d bytes of data', file.contents.length);
});
> Read 2493109 bytes of data

这个例子说清楚明了在文件被完整到场到流之前数据会被转换成buffer。

Gulp默许运用buffer

只管越发引荐运用流中的数据,但许多插件的底层库运用的是buffer。有时候必需运用buffer,因为转换须要完整的文件内容。比方文本替代和正则表达式的情况。假如运用数据碎片,将会面对婚配失利的风险。一样,像UglifyJSTraceur Compiler须要输入完整的文件内容(最少须要语法完整的JavaScript字符串)

这就是为何gulp默许运用转换成buffer的流,因为这更好处置惩罚。

运用转换成buffer的流也有瑕玷,处置惩罚大文件时将异常低效。文件必需完整读取,然后才被到场到流中。那末题目来了,文件的尺寸多大才会下降机能?关于一般的文本文件,比方JavaScript、CSS、模板等等,这些运用buffer开支异常小。

在任何状况下,假如将buffer选项设为false,你能够通知gulp流中通报的内容终究是什么。以下所示:

gulp.src('/usr/share/dict/words', {buffer: false}).on('data', function(file) {
    var stream = file.contents;
    stream.on('data', function(chunk) {
        console.log('Read %d bytes of data', chunk.length);
    });
});
> Read 65536 bytes of data
> Read 65536 bytes of data
> Read 65536 bytes of data
> Read 65536 bytes of data
> ...

从流到buffer

因为所需的输入(输出)流和gulp插件不尽相同,你能够须要将流转换成buffer(反之亦然)。之前已有过引见,大多数插件运用buffer(只管他们的一部分也支撑流)。比方gulp-uglifygulp-traceur。你能够经由过程gulp-buffer来转换成buffer:

var source = require('vinyl-source-stream'),
    buffer = require('gulp-buffer'),
    uglify = require('gulp-uglify');
fs.createReadStream('./src/app.js')
    .pipe(source('app.min.js'))
    .pipe(buffer())
    .pipe(uglify())
    .pipe(gulp.dest('dist/'));

或许另一个例子:

var buffer = require('gulp-buffer'),
    traceur = require('gulp-traceur');
gulp.src('app.js', {buffer: false})
    .pipe(buffer())
    .pipe(traceur())
    .pipe(gulp.dest('dist/'));

将buffer转换为流

你也能够运用gulp-streamifygulp-stream将一个运用buffer的插件的输出转化为一个可读流。如许处置惩罚以后,跟在运用buffer的插件背面的(只能)运用流的插件也能一般工作了。

var wrap = require('gulp-wrap'),
    streamify = require('gulp-streamify'),
    uglify = require('gulp-uglify'),
    gzip = require('gulp-gzip');
gulp.src('app.js', {buffer: false})
    .pipe(wrap('(function(){<%= contents %>}());'))
    .pipe(streamify(uglify()))
    .pipe(gulp.dest('build'))
    .pipe(gzip())
    .pipe(gulp.dest('build'));

不是一切事都须要插件

虽然已有许多运用且随意马虎的插件,许多使命以及转换能够不运用插件而随意马虎完成。插件会带来一些题目,你须要依靠一个分外的npm模块,一个插件接口和(反应迟钝?)的维护者,等等。假如一个使命能够不运用插件而运用原生模块就可以随意马虎完成,绝大多数状况下,都发起不要运用插件。能够明白上面所说的观点,并能够在所处的状况下做出准确的决议,这点异常重要。下面来看一些例子:

vinyl-source-stream

之前的例子中,我们已直接运用了browserify,而不是运用(现已到场黑名单)gulp-browserify插件。这里的关键是运用vinyl-source-stream(或相似的库)举行加工,来将通例的可读流输入运用vinyl的插件。

文本转换

另一个例子就是基于字符串的变更。这里有一个异常基本的插件,直接运用了vinyl的buffer:

function modify(modifier) {
    return through2.obj(function(file, encoding, done) {
        var content = modifier(String(file.contents));
        file.contents = new Buffer(content);
        this.push(file);
        done();
    });
}

你能够像如许运用这个插件:

gulp.task('modify', function() {
    return gulp.src('app.js')
        .pipe(modify(version))
        .pipe(modify(swapStuff))
        .pipe(gulp.dest('build'));
});
function version(data) {
    return data.replace(/__VERSION__/, pkg.version);
}
function swapStuff(data) {
    return data.replace(/(\w+)\s(\w+)/, '$2, $1');
}

这个插件并没有完成,而且也不能处置惩罚流(完整版本)。但是,这个例子申明,能够很随意马虎地经由过程一些基本函数来建立新的变更。through2库供应了异常优异的Node流封装,而且许可像上面那样运用转换函数。

使命流程

假如你须要去运转一些定制化或动态的使命,相识gulp所运用的Orchestrator模块会很有协助。gulp.add要领实在就是Orchestrator.add要领(现实上一切的要领都是从Orchestrator继续而来的)。但为何你须要这个?
* 你不想“私有使命”(比方:不暴露给命令行东西)弄乱gulp使命列表。
* 你须要更多的动态的和(或)可重用的子使命。

末了的思索

请注重,gulp(或grunt)并不老是当前情境下的最好东西。比方说,假如你须要拼接并运用uglify紧缩一系列的JavaScript文件,又或许你须要编译一些SASS文件,你能够须要斟酌运用makefile或npm run,经由过程命令行来完成。削减依靠,削减设置,才是正解。

浏览经由过程npm run来完成使命自动化来相识更多信息。你须要明白经由过程一系列的“自定义构建”后须要取得什么,而哪一个东西最合适。

不过,我以为gulp是一个巨大的构建体系,我很喜欢运用它,它展示了Node.js中流的壮大。

愿望这些能够帮到你!假如你有任何反应或其他发起,请在批评中通知我,或许加我的twitter:@webprolific

小广告:更多内容欢迎来我的博客,配合讨论

    原文作者:天镶
    原文地址: https://segmentfault.com/a/1190000000711469
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞