grunt vs gulp

虽然gulp已经出来很久了,但是一直没有去使用过。得益于最近项目需要,就尝试了一下,以下从几个要点讲一下grunt和gulp使用的区别,侧重讲一下在使用gulp过程中发现的问题。而两种工具孰优孰劣由读者自己判断。

1. 书写方式

grunt 运用配置的思想来写打包脚本,一切皆配置,所以会出现比较多的配置项,诸如option,src,dest等等。而且不同的插件可能会有自己扩展字段,导致认知成本的提高,运用的时候要搞懂各种插件的配置规则。
gulp 是用代码方式来写打包脚本,并且代码采用流式的写法,只抽象出了gulp.src, gulp.pipe, gulp.dest, gulp.watch 接口,运用相当简单。经尝试,使用gulp的代码量能比grunt少一半左右。


2. 任务划分

grunt 中每个任务对应一个最外层配置的key, 大任务可以包含小任务,以一种树形结构存在。举个栗子:

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相当有用。

gulp 中没有子任务的概念,对于上面的需求,只能通过注册两个task来完成

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,特殊情况下可以用这种方法解决。

3. 运行效率

grunt 采用串行的方式执行任务,比如我们注册了这样一个任务:
grunt.register('default', ['concat', 'uglify', 'release'])
grunt是按书写的顺序首先执行cancat,然后是uglify,最后才是release,一派和谐的气氛,谁也不招惹谁。而我们知道某些操作时可以同步执行的,比如cssmin和uglifyjs。这时grunt无法通过简单地更改配置来达到并行执行的效果,通常的做法是手动写异步task,举个栗子:

grunt.registerTask('cssmin', 'async cssmin task', function() {
var done = this.async();
cssmin(done);
});

在cssmin操作完成后传入done方法告知程序,但这需要插件支持。

gulp 基于并行执行任务的思想,通过一个pipe方法,以数据流的方式处理打包任务,我们来看这段代码:

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本身就是并发执行的,我们并不需要多什么多余多工作,只需

gulp.task('default', ['uglify', 'cssmin']);

gulp该怎么快就怎么来,并不会等到uglify再执行cssmin。
是不是觉得gulp秒杀grunt几条街了呢?且慢,坑还在后面...
首先我们需要问一个问题,为什么要用并发?
为了快?那什么时候可以快,什么时候又不能快?
假设我们有这样一个任务:

gulp.task('jsmin', ['clean', 'concat']);

需要先将文件夹清空,再进行合并压缩,根据gulp的并发执行的方式,两个任务会同时执行,虽然从指令上看是先执行了clean再执行concat,然而clean还没结束,concat就执行了,导致代码合并了一些未被清理的文件,这显然不是我们想要的结果。
那这个问题有没有什么解决方案呢?
gulp官方API给出了这样的方法:

  • 给出一个提示,来告知 task 什么时候执行完毕
  • 并且再给出一个提示,来告知一个 task 依赖另一个 task 的完成

官方举了这个例子:
让我们先假定你有两个 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');
})

4. 其他要交代的

  1. gulp真的只有src, pipe, dest, watch, run这几个API吗? 不,由于gulp继承了Orchestrator(<4.0),所以具备了另外一些API,包括start等。当然这些API是官方不推荐使用的。会导致代码的复杂度提升,所以并没有出现在官方文档中。
  2. 不建议将多个操作写在同个task中,这样程序并不知道任务及时结束,如:
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);
});
  1. 尽量减少task的数量,很多任务其实可以在一个task中用多个pipe来执行,只需要我们在打包等时候规划好文件夹及任务流。

对了,gulp4.0会带给我们很多惊喜(wtf!),虽然它还是迟迟未发布... 暂时不想去踩坑。读者可自行Google。

Grunt vs Gulp的更多相关文章

  1. 前端构建工具的用法—grunt、gulp、browserify、webpack

    随着前端项目的飞速发展,项目越来越大.文件越来越多,前端工程化的工具也越来越多.下面介绍目前最流行的四种构建工具——grunt.gulp.browserify.webpack 所有的构建工具都是基于N ...

  2. gulp系列文章一 fis vs grunt vs gulp,为什么要是gulp呢?

    gulp是最近火起来的前端构建工具,大有赶超grunt之势,它和grunt这种构建工具比较像. grunt是写一个Gruntfile.js来写配置代码,gulp则是写一个gulpfile.js来写配置 ...

  3. 前端开发自动化工作流工具,JavaScript自动化构建工具grunt、gulp、webpack介绍

    前端开发自动化工作流工具,JavaScript自动化构建工具grunt.gulp.webpack介绍 前端自动化,这样的一个名词听起来非常的有吸引力,向往力.当今时代,前端工程师需要维护的代码变得及为 ...

  4. JavaScript构建(编绎)系统大比拼:Grunt vs. Gulp vs. NPM

    Nicolas Bevacqua进行了一个比较JavaScript构建(编绎)系统的任务.他对三巨头: Grunt, Gulp and NPM进行了比较,并讨论了每种的优缺点. By Nicolas ...

  5. Grunt和Gulp构建工具在Visual Studio 2015中的高效的应用

    Grunt和Gulp构建工具在Visual Studio 2015中的高效的应用 Grunt和Gulp是Javascript世界里的用来做自动压缩.Typescript编译.代码质量lint工具.cs ...

  6. JavaScript自动化构建工具入门----grunt、gulp、webpack

    蛮荒时代的程序员: 做项目的时候,会有大量的js 大量的css   需要合并压缩,大量时间需要用到合并压缩 在前端开发中会出现很多重复性无意义的劳动  自动化时代的程序员: 希望一切都可以自动完成  ...

  7. 谈谈Grunt,NPM,Gulp

    随着前端工程化的趋势,产生了越来越多的构建工具,而其中比较优秀的就是grunt,npm,gulp,今天我来说说这三者间的区别以及他们的优缺点. 相信一般前端开发者选择构建工具的时候,更多的是看个人习惯 ...

  8. Grunt、Gulp和Webpack对比

    1.Grunt处理Sass转换成CSS过程 2.Gulp处理Sass转换成CSS过程 3.Webpack执行原理 4.区别 (1)grunt和gulp都类似于webapck的中的scripts,属于任 ...

  9. ASP.NET5之客户端开发:Grunt和Gulp构建工具在Visual Studio 2015中的高效的应用

    Grunt和Gulp是Javascript世界里的用来做自动压缩.Typescript编译.代码质量lint工具.css预处理器的构建工具,它帮助开发者处理客户端开发中的一些烦操重复性的工作.Grun ...

随机推荐

  1. NYOJ题目840吃花生

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAskAAAKdCAIAAABeSGNbAAAgAElEQVR4nO3dPXKkuvv28f8mnHshjn

  2. cascade 介绍与用法 ( oracle)

    级联删除,比如你删除某个表的时候后面加这个关键字,会在删除这个表的同时删除和该表有关系的其他对象 1.级联删除表中的信息,当表A中的字段引用了表B中的字段时,一旦删除B中该字段的信息,表A的信息也自动 ...

  3. mysql中select五种子句和统计函数

    select 五种子句顺序 where 条件 group by 分组 having 把结果进行再次筛选 order by  排序 limit  取出条目 统计函数  max(列名)  求最大 min( ...

  4. mysql 5.6.33 重置密码后报错

    [root@ Desktop]# mysql -uroot -p Enter password: Welcome to the MySQL monitor. Commands end with ; o ...

  5. SpringMVC @RequestBody问题:Unrecognized field , not marked as ignorable

    http://blog.csdn.net/isea533/article/details/33397735

  6. 【PHP Cookie&&Session】

    大部分的人都知道Cookie,但是可能不了解Session,现在对这两者进行解释. 问题的提出: 有些网站会提示用户在一定的时间之内免登陆,这是用的什么技术?答案是Cookie技术. 有些购物网站会提 ...

  7. 算法系列:CSAPP 推荐

    转载自:https://book.douban.com/review/6093947/ 如果你觉得这本书过于厚重担心看不下来的话,不妨跟着coursera的Hardware/Software Inte ...

  8. RMAN恢复目录

    是否使用RMAN恢复目录(Recovery Catalog 你可能从其他人或书上听过RMAN恢复目录(也有可能是其他名字,RMAN Recovery Catalog的翻译较多较杂,以下简称恢复目录), ...

  9. phpcms v9中调用栏目及调用多个子栏目中的文章列表

    调用一个指定栏目列表:            {pc:content action="lists" catid="6" order="id DESC& ...

  10. hdu 3709 数位dp

    数位dp,有了进一步的了解,模板也可以优化一下了 题意:找出区间内平衡数的个数,所谓的平衡数,就是以这个数字的某一位为支点,另外两边的数字大小乘以力矩之和相等,即为平衡数例如4139,以3为支点4*2 ...