关于gulp
前缀
gulp是继grunt之后的后起之秀,主张代码优于配置,api简单方便,基于流进行操作,快捷方便。grunt之前使用不多,但是第一次看配置gruntFile时还是挺费劲的,代码并不直观。直到后来投入gulp的怀抱,那个配置起来岂止一个爽字了得。
我用gulp也不算久,想起去年十一月份出来实习,最先接触grunt,然后偶然看到别人推荐gulp,然后去了解一下,发现gulp在github上star数量超过grunt,然后当时果断转到gulp了。
好歹是用过一些gulp,所以就厚着脸皮写一些,权当笔记,也做分享之用。
PS:前段时间我准备写一个系列的文章(有关gulp+angular+browserify+sass),这篇文章算是第一篇,写得若不好,还请指正,这一系列文章之后会放到ngFasg.lingyong.me。
gulp简单介绍
一句话来概括,gulp就是一个基于流,代码优于控制的构建工具。网上都这么说,用我的话来说,第一,快,比grunt快,第二,配置起来简单,阅读无障碍,第三,很流行,看github star数量就知道了。
简单地API
gulp的API很简单,常用的也就几个:watch
, task
, dest
, src
。
gulp.src(globs[, options])
读取符合条件的文件,然后返回Vinyl files类型的流用于gulp插件进行进一步处理。
globs String是一种简洁的匹配文件的方式,详情可以查看node-glob。
这里就有个疑问了,什么是Vinyl files,说白了Vinyl就是一种虚拟文件格式,它能很方便的匹配文件然后读取出来(以stream或者buffer的格式),为每个文件标注path,content,有利于gulp向文件流pipe plugin。
options
参数是用来传给node-glob。
options.buffer:是否返回buffer,默认是true。
options.read:设置为false,返回的file.content为null,就是不会读取该文件。默认为true。
options.base:指glob开始匹配的前面的任何东西,听起来有点拗口。来点例子:
gulp.src('client/js/**/*.js') // Matches 'client/js/somedir/somefile.js' and resolves base to client/js/
.pipe(minify())
.pipe(gulp.dest('build')); // Writes 'build/somedir/somefile.js'
gulp.src('client/js/**/*.js', { base: 'client' })
.pipe(minify())
.pipe(gulp.dest('build')); // Writes 'build/js/somedir/somefile.js'
更加详细的文档可以查看glob2base。从以上的例子可以看出,默认的base为glob开始匹配前的任何东西,也就是client/js
,但是一旦把base改成client
时,js/**/*.js
作为匹配的开始,所以gulp.dest
的时候就会把js/
加上。
gulp.dest(path[, options])
可以被pipe,而且会写入文件,当然,是可以写入多个文件的,当dest的路径不存在时会自动创建。
而这里的path是和file base相关的,就是上面讲到的option.base,所以实际文件的路径由path+base+global.start结合而成,然后再计算出绝对地址。
options.cwd:输出文件夹的cwd
,默认为:process.cwd()。
options.mode:创建文件夹时赋予的权限,默认为:0777
。
gulp.task(name[, deps], fn)
定义一个task,采用的是orchestrator,一个用来执行控制一系列task和dependencies的库。
例如:
gulp.task('mytask', ['array', 'of', 'task', 'names'], function() {
// Do stuff
});
首先注册一个task名字为mytask
,然后他有四个依赖,也就是说再执行mytask之前得执行这四个依赖的任务。
fn
里就是书写任务内容的地方啦。
支持callback
// run a command in a shell
var exec = require('child_process').exec;
gulp.task('jekyll', function(cb) {
// build Jekyll
exec('jekyll build', function(err) {
if (err) return cb(err); // return error
cb(); // finished task
});
});
还能返回stream
gulp.task('somename', function() {
var stream = gulp.src('client/**/*.js')
.pipe(minify())
.pipe(gulp.dest('build'));
return stream;
});
还能返回promise
var Q = require('q');
gulp.task('somename', function() {
var deferred = Q.defer();
// do async stuff
setTimeout(function() {
deferred.resolve();
}, 1);
return deferred.promise;
});
但是这里需要注意的是,因为task采用的是orchestrator,这个库将最大化地并发执行任务,也就是说它会立即载入多个task,然后等着,一个个去完成。
所以为了让多个task一个挨一个地执行,需要做到以下两点:
- 在第一个task中标注何时完成task,你可以通过一个callback来标注,如同上面的callback的例子。也可以通过返回一个promise来标注,后续的task就会等返回的promise完成以后再去执行。通常情况下是直接返回一个stream,当stream end后之后的task才执行。
- 在第二个task中标注依赖第一个task。
例如:
var gulp = require('gulp');
// takes in a callback so the engine knows when it'll be done
gulp.task('one', function(cb) {
// do stuff -- async or otherwise
cb(err); // if err is not null and not undefined, the run will stop, and note that it failed
});
// identifies a dependent task must be complete before this one begins
gulp.task('two', ['one'], function() {
// task 'one' is done now
});
gulp.task('default', ['one', 'two']);
gulp.watch
watch文件的变化,然后做一定的事情,可以是task或者是一个callback。
gulp.watch(glob[, opts], tasks)
直接来例子更加简单:
var watcher = gulp.watch('js/**/*.js', ['uglify','reload']);
watcher.on('change', function(event) {
console.log('File ' + event.path + ' was ' + event.type + ', running tasks...');
});
gulp.watch(glob[, opts, cb])
还是直接上例子:
gulp.watch('js/**/*.js', function(event) {
console.log('File ' + event.path + ' was ' + event.type + ', running tasks...');
});
callback里会带上event
对象。包含:
event.type
:change的类型,有added
、deleted
、changed
。event.path
:发生改变的文件的路径。
发现gulp在github上star数量超过gulp,然后当时果断转到gulp了。 写错了吧
@gangdiedao 谢谢了,去公司了,马上改正。确实是写错了 自豪地采用 CNodeJS ionic
赞一个(ps:我也是大四学生党,加qq互相学习呗)
@soliury 开始工作了?
@gangdiedao ok,改了。
@CocaCola183 微信上看到你了
@jobn123 是啊,不过最近又得换工作了。
@soliury 有个问题想请教下 https://github.com/gulpjs/gulp/blob/master/docs/recipes/delete-files-folder.md这里面的第二种删除文件Delete files in a pipeline,我试了下,没啥用啊0.0,也没给我创建文件,也没删除文件,不懂他什么意思,能讲讲不
var gulp = require('gulp');
var stripDebug = require('gulp-strip-debug'); // only as an example
var del = require('del');
var vinylPaths = require('vinyl-paths');
gulp.task('clean:tmp', function () {
return gulp.src('tmp/*')
.pipe(stripDebug())
.pipe(gulp.dest('dist'))
.pipe(vinylPaths(del));
});
gulp.task('default', ['clean:tmp']);
你确定你是按照这个来做的?
@soliury 是的 非常确定,它只给我建了个dist文件夹
var gulp = require('gulp');
var stripDebug = require('gulp-strip-debug'); // only as an example
var del = require('del');
var vinylPaths = require('vinyl-paths');
gulp.task('clean', function () {
gulp.src('1.js')
.pipe(stripDebug())
.pipe(gulp.dest('dist'))
.pipe(vinylPaths(del));
});
gulp.task('default', ['clean']);
@shadow88sky 这样做确实不行,这样其实是删除了gulp.dest 的文件。
@soliury 那这段有啥意义,没搞懂~
var gulp = require('gulp');
var stripDebug = require('gulp-strip-debug'); // only as an example
var del = require('del');
var vinylPaths = require('vinyl-paths');
gulp.task('clean', function () {
var vp=vinylPaths();
gulp.src('1.js')
.pipe(vp)
.pipe(stripDebug())
.pipe(gulp.dest('dist'))
.on('end',function(){
del(vp.paths);
})
});
gulp.task('default', ['clean']);
实际上上面那段其实运行会出错误。上面那种方式只是适合要删除一个文件,但是删除前需要做一些操作,但是不会dist,我之后贴得那种方式,就是先用vinylPath来记录下要删除的文件的地址,然后,在pipe end的时候,删除。懂否?
@soliury 秒懂了 嘿嘿 谢谢
@shadow88sky ok,我之后就会放出一个删除文件的文章,你这个问题提醒了我,正在努力写这篇文章中。。。。。稍等片刻。
@soliury 恩 不错不错。 不过这有何意义,只不过把原来的文件删了,另外给你建了个文件。 最好结合项目实例讲解下更好~
@shadow88sky 你最初的用法是用错了, 它官方的文档写错了,我会从实际例子出发来讲解的。
@soliury 借贵宝地打个广告,哈哈
欢迎关注我的公众号【node全栈】
@i5ting 已关注