虽然gulp已经出来好久了,可是一直没有去使用过。得益于最近项目须要,就尝试了一下,如下从几个要点讲一下grunt和gulp使用的区别,侧重讲一下在使用gulp过程当中发现的问题。而两种工具孰优孰劣由读者本身判断。css
grunt 运用配置的思想来写打包脚本,一切皆配置,因此会出现比较多的配置项,诸如option,src,dest等等。并且不一样的插件可能会有本身扩展字段,致使认知成本的提升,运用的时候要搞懂各类插件的配置规则。
gulp 是用代码方式来写打包脚本,而且代码采用流式的写法,只抽象出了gulp.src, gulp.pipe, gulp.dest, gulp.watch 接口,运用至关简单。经尝试,使用gulp的代码量能比grunt少一半左右。html
grunt 中每一个任务对应一个最外层配置的key, 大任务能够包含小任务,以一种树形结构存在。举个栗子:node
uglify: {
one: { src: 'src/a.js', dest: 'dest/a.min.js' }, two: { src: 'tmp/b.js', dest: 'dist/b.min.js' } }
将uglify划分子任务的好处是,咱们在封装不一样的task时能够分别对'uglify:one'或'uglify:two'进行调用,这对于某些须要在不一样时间点调用到uglify的task至关有用。jquery
gulp 中没有子任务的概念,对于上面的需求,只能经过注册两个task来完成git
gulp.task('uglify:one', function(){ gulp.src('src/a.js') .pipe(uglify()) .dest('dest/a.min.js') }); gulp.task('uglify:two', function(){ gulp.src('tmp/b.js') .pipe(uglify()) .dest('dist/b.min.js') });
固然这种需求每每能够经过调整打包策略来优化,并不须要分解子task,特殊状况下能够用这种方法解决。github
grunt 采用串行的方式执行任务,好比咱们注册了这样一个任务:grunt.register('default', ['concat', 'uglify', 'release'])
grunt是按书写的顺序首先执行cancat,而后是uglify,最后才是release,一派和谐的气氛,谁也不招惹谁。而咱们知道某些操做时能够同步执行的,好比cssmin和uglifyjs。这时grunt没法经过简单地更改配置来达到并行执行的效果,一般的作法是手动写异步task,举个栗子:gulp
grunt.registerTask('cssmin', 'async cssmin task', function() { var done = this.async(); cssmin(done); });
在cssmin操做完成后传入done方法告知程序,但这须要插件支持。bootstrap
gulp 基于并行执行任务的思想,经过一个pipe方法,以数据流的方式处理打包任务,咱们来看这段代码:api
gulp.task('jsmin', function () { gulp.src(['build/js/**/*.js']) .pipe(concat('app.min.js')) .pipe(uglify() .pipe(gulp.dest('dist/js/')); });
程序首先将build/js
下的js文件压缩为app.min.js
, 再进行uglify操做,最后放置于dist/js下。这一系列工做就在一个task中完成,中间没有产生任何临时文件。若是用grunt,咱们须要怎样写这个任务?那必须是有两个task配置,一个concat,一个uglify,中间还必须产生一个临时文件。从这个角度来讲,gulp快在中间文件的产生只生成于内存,不会产生多余的io操做。
再来看看前面的问题,如何并行执行uglify和cssmin?其实gulp自己就是并发执行的,咱们并不须要多什么多余多工做,只需promise
gulp.task('default', ['uglify', 'cssmin']);
gulp该怎么快就怎么来,并不会等到uglify再执行cssmin。
是否是以为gulp秒杀grunt几条街了呢?且慢,坑还在后面...
首先咱们须要问一个问题,为何要用并发?
为了快?那何时能够快,何时又不能快?
假设咱们有这样一个任务:
gulp.task('jsmin', ['clean', 'concat']);
须要先将文件夹清空,再进行合并压缩,根据gulp的并发执行的方式,两个任务会同时执行,虽然从指令上看是先执行了clean再执行concat,然而clean还没结束,concat就执行了,致使代码合并了一些未被清理的文件,这显然不是咱们想要的结果。
那这个问题有没有什么解决方案呢?
gulp官方API给出了这样的方法:
官方举了这个例子:
让咱们先假定你有两个 task,"one" 和 "two",而且你但愿它们按照这个顺序执行:
1. 在 "one" 中,你加入一个提示,来告知何时它会完成:能够再完成时候返回一个 callback,或者返回一个 promise 或 stream,这样系统会去等待它完成。
2. 在 "two" 中,你须要添加一个提示来告诉系统它须要依赖第一个 task 完成。
所以,这个例子的实际代码将会是这样:
var gulp = require('gulp'); // 返回一个 callback,所以系统能够知道它何时完成 gulp.task('one', function(cb) { // 作一些事 -- 异步的或者其余的 cb(err); // 若是 err 不是 null 或 undefined,则会中止执行,且注意,这样表明执行失败了 }); // 定义一个所依赖的 task 必须在这个 task 执行以前完成 gulp.task('two', ['one'], function() { // 'one' 完成后 }); gulp.task('default', ['one', 'two']);
task one执行完毕后须要调用cb方法来告知task two我已经执行完成了,你能够干你的事了。
那在咱们实际运用中,一般是这样的:
gulp.task('clean', function (cb) { gulp.src(['tmp']) .pipe(clean()); });
这个时候clean结束的cb要写在哪呢?是这样吗?
gulp.task('clean', function (cb) { gulp.src(['tmp']) .pipe(clean()); cb(); });
对于理解什么叫异步的人来讲这种方法确定是不行的,clean还没完成,cb已经执行了。好在!!!
好在咱们能够利用gulp中的时间监听来作结束判断:
gulp.task('clean', function (cb) { gulp.src(['tmp']) .pipe(clean()), .on('end', cb); }); gulp.task('concat', [clean], function(){ gulp.src('blabla') .pipe('blabla') .dest('blabla'); });
因为gulp是用node实现的,因此必然绑定了数据流的监听事件,咱们经过监听stream event end来达到这个目的。
而不得不吐槽的是经过在task后面写[]依赖的方式也并不优雅,一般能够经过其余插件来达到顺序执行的效果,写法如同grunt,可是每一个task的end事件的监听也是少不了的。
若是你的任务很少的时候,直接在回调后面执行concat也是能够的:
gulp.task('clean', function(){}) gulp.task('concat', function(){}) gulp.task('clean-concat', ['clean'], function(){ gulp.start('concat'); })
gulp.task('test', function(cb) { gulp.src('bootstrap/js/*.js') .pipe(gulp.dest('public/bootstrap')) .on('end', cb); gulp.src('jquery.cookie/jquery.cookie.js') .pipe(gulp.dest('public/jquery')) .on('end', cb); });
对了,gulp4.0会带给咱们不少惊喜(wtf!),虽然它仍是迟迟未发布... 暂时不想去踩坑。读者可自行Google。