精华 关于gulp
发布于 10 年前 作者 soliury 11665 次浏览 最后一次编辑是 8 年前 来自 分享

关于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一个挨一个地执行,需要做到以下两点:

  1. 在第一个task中标注何时完成task,你可以通过一个callback来标注,如同上面的callback的例子。也可以通过返回一个promise来标注,后续的task就会等返回的promise完成以后再去执行。通常情况下是直接返回一个stream,当stream end后之后的task才执行。
  2. 在第二个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对象。包含:

  1. event.type:change的类型,有addeddeletedchanged
  2. event.path:发生改变的文件的路径。
20 回复

发现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,也没给我创建文件,也没删除文件,不懂他什么意思,能讲讲不

@shadow88sky

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 那这段有啥意义,没搞懂~

@shadow88sky

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']); 

@shadow88sky

实际上上面那段其实运行会出错误。上面那种方式只是适合要删除一个文件,但是删除前需要做一些操作,但是不会dist,我之后贴得那种方式,就是先用vinylPath来记录下要删除的文件的地址,然后,在pipe end的时候,删除。懂否?

@soliury 秒懂了 嘿嘿 谢谢

@shadow88sky ok,我之后就会放出一个删除文件的文章,你这个问题提醒了我,正在努力写这篇文章中。。。。。稍等片刻。

@soliury 恩 不错不错。 不过这有何意义,只不过把原来的文件删了,另外给你建了个文件。 最好结合项目实例讲解下更好~

@shadow88sky 你最初的用法是用错了, 它官方的文档写错了,我会从实际例子出发来讲解的。

@soliury 借贵宝地打个广告,哈哈

欢迎关注我的公众号【node全栈】 node全栈.png

回到顶部