Gulp挑战Grunt,背后的哲学

作者: 梦林 | 来源:发表于2014-07-12 23:57 被阅读6100次

[按:网上介绍Gulp和Grunt安装使用的文章很多,甚少比较二者的思路,连官方文档都语焉不详。我在此做一个粗陋的对比,希望能提纲挈领,加深读者对这两个工具的理解。]

做过点儿正经开发的同学都知道,构建工具必不可少。C时代的Make、Java的Ant、Ruby的Rake……没有这些工具,一遍遍地点选输入,准烦死你。

在前端和Node JS的开发中,最普及的构建工具就是Grunt。它的功能说来简单,就是管理一系列的Task。大部分的Task都是第三方的插件,安装好对应的NPM包,再loadNpmTasks就可以用了。

Grunt的配置文件Gruntfile,主要包含两部分:

  • 配置每个Task,包括文件从哪里,到哪里去,还有一些处理的选项

  • 自己写一些简单的Task,把第三方插件提供的Task组合起来

别看这两个事儿,轻轻松松几百行出来了。每个Task的配置,各有各的规矩,还牵扯到插件间的配合。反正我从seed库开始做新项目的时候,基本不敢改原来的Gruntfile,很多用不上的功能也搁那儿。留意了一下很多开源项目的Gruntfile,也都臃肿杂乱,好不到哪儿去。

Gruntfile维护起来那么困难,有几个原因:

  • 配置和运行分离
    程序员都知道,变量的声明和使用挨在一起,最方便理解和修改。但Gruntfile里,配置Task和调用它们的地方离得很远,极大地增加了心智负担。

  • 每个插件做的事太多
    每个Task的结果必须写到磁盘文件,另一个Task再读,损害性能倒是小事,更麻烦的是让整个过程变复杂了。
    就像一个个小作坊,来料加工又返回给客户,这中间的沟通成本、出错机会都大大增加。

  • 配置项过多
    做事多了,配置项自然也多。至少输入和输出的位置得配吧。每个插件的配置规则还不尽相同。用每个插件,都得去学习一番。

Gulp应运而生。

恐怕没几个IT人不知道Unix管道的概念。前一级的输出,直接变成后一级的输入。把简单的工具组合起来,优雅地解决复杂的问题。听起来那么熟悉呢?是的,Gulp就把这种思维用在构建过程中。

Gulp基于Node JS的一个机制,叫做stream,有点类似C++中的stream。在Node中,文件访问、输入输出、HTTP连接,都是stream。Gulp的每个插件从stream中读取输入,做一些处理,再输出到stream中。

每个插件不是拿来独立使用的。相反,它专注于完成单一职责。只有把合适的插件组合起来,才能完成具体的Task。引用官方的例子,看看一个典型的Task长什么样(略有删减):

var paths = {
  scripts: ['client/js/**/*.coffee', '!client/external/**/*.coffee']
};

gulp.task('scripts', ['clean'], function() { // 可以依赖于其它task
  return gulp.src(paths.scripts) // 指定输入
      .pipe(coffee()) // 环节一
      .pipe(uglify()) // 环节二
      .pipe(concat('all.min.js')) // 环节三
      .pipe(gulp.dest('build/js')); // 指定输出
});

配置呢?不需要了。是不是行云流水,一气呵成?

那我们再回头来看看前面Grunt的几个问题,Gulp是怎么解决的:

  • 配置和运行分离
    code over configuration,直接就在调用的地方配置。

  • 每个插件做的事太多
    单一职责,依靠组合来发挥作用。就像一条自动化生产线,上一道工序的产出直接交给下一步,效率不要太高。

  • 配置项过多
    既然大家都遵循同一个协议,很多配置就不需要了。

放大了看,Gulp像是一个非常贴近领域模型的DSL,而Grunt更像万能的XML。哪个好用,无需多说。在我们制作DSL时,也有参考意义。

最后,举一个Grunt很别扭,Gulp却能优雅解决的例子。

做前端开发会用到一个功能叫usemin。我们HTML中会引用到很多css和js文件。发布时,这些文件要合并、压缩、混淆,最后生成一两个文件。为了让修改过的代码绕过浏览器的缓存机制,要根据文件内容hash出文件名。html文件里就要引用这些新的文件名。

比较一下grunt-usemingulp-usemin各自README的长度,就能看出区别。

grunt.registerTask('build', [
  'useminPrepare', // 准备
  'concat',
  'cssmin',
  'uglify',
  'filerev',
  'usemin' // 执行
]);

grunt-usemin分成两步:

  • 先从html文件中收集需要处理的js和css,传给后续的一堆任务
    它本身并不知道在实际中会调用哪些其它Task,只能用一些hack,支持固定的几个Task。而上面的每个Task,都有自己的配置项。要把这些配置项都列出来,实在太长了。

  • 真正执行,更新html文件里的js和css引用。

gulp-usemin就干净得多,没有丝毫多余的东西:

gulp.task('usemin', function() {
  gulp.src('./*.html')
    .pipe(usemin({
      css: [minifyCss(), 'concat'],
      html: [minifyHtml({empty: true})],
      js: [uglify(), rev()]
    }))
    .pipe(gulp.dest('build/'));
});

usemin不需要有minifyCssminifyHtmluglifyrev这几个插件的任何知识,只要把对应的内容从stream丢出去就好。在用这些插件组装task时才需要关心。

当前,Gulp的社区还远不如Grunt成熟,有些功能的插件,Gulp可能就没有。这其实不算很大的劣势,只要足够好用,追上来很快。而且,写一个Gulp插件要比相应的Grunt插件短小得多!

相关文章

  • Gulp挑战Grunt,背后的哲学

    [按:网上介绍Gulp和Grunt安装使用的文章很多,甚少比较二者的思路,连官方文档都语焉不详。我在此做一个粗陋的...

  • gulp_安装和使用

    gulp前端构建工具,其功能和grunt一样但运行起来比grunt快。gulp和grunt的区别是,gul...

  • gulp API介绍

    1. gulp.src(globs[, options]) Grunt.js和Gulp.js工作方式 Grunt主...

  • 构建工具/grunt/gulp/parcel/webpack

    构建工具/grunt/gulp/parcel/webpack 1. Grunt 1.1 Grunt介绍 中文主页 ...

  • gulp安装

    grunt: http://yujiangshui.com/grunt-basic-tutorial/ gulp安...

  • (web前端) 工程化高频面试题

    1.webpack与grunt、gulp的不同? Grunt、Gulp是基于任务运行的工具: 它们会自动执行指定的...

  • 自动化构建02

    常用的自动化构建工具 Grunt Gulp FIS Grunt Grunt标记任务失败 Grunt 配置方法 G...

  • gulp webpack browserify

    Gulp应该和Grunt比较,他们的区别我就不说了,说说用处吧。Gulp / Grunt 是一种工具,能够优化前端...

  • gulp

    gulp前端构建工具,功能和grunt一样,但运行起来比grunt快区别:gulp: 快,书写跟nodejs一样...

  • 使用Harp快速开发前端页面

    有了node以后,像gulp,grunt等一些工具可以给我们的前端开发带来很大的便利,但是gulp和grunt对于...

网友评论

  • 梦林:@888888888888888 我是这么看的。

    首先Grunt的很多配置是指定每个插件的输入和输出文件,Gulp不需要。这有点类似于反复写临时文件和使用Unix管道的区别。

    然后Grunt每个插件的配置是独立的,初看很难明白和任务有何关系,而Gulp是在使用插件时指定,更像是调用时的参数,顺序看下来,很容易理解整个逻辑。
  • c41beec52c37:@jacobbubu 代码量的确是这样的,该配置的还是要配置
    所以我觉得这文章有失偏颇
  • wizcabbit:gulp的社区支持问题,有grunt-gulp或者gulp-grunt在gulp和grunt之间互相调用,而且现在有的好工具上来就支持gulp,很高兴方向已经在变化了
  • 梦林:@jyu213 文件打包本身慢,和构建工具似乎没什么关系。
  • 03be9f1978d2:gulp 的流式写法很赞。
    只是在多文件打包的时候,很慢..很慢。
  • 梦林:@jacobbubu

    我不懂Node,插件自然也只会看,但不妨碍我对Gulp赞叹有加,并且拿现有的插件来拼装。话说写Grunt插件的人也不多吧。

    性能的话,反正不是业务代码,对大部分项目其实关系不大。

    我最恼火Grunt的就是那一大堆配置,太烦了。这两天我自己设计DSL时效仿Gulp,感觉那叫舒坦。
  • jacobbubu:Grunt 由于其同步语法,可以给一些初通 Javascript 的开发者来使用,哪怕是自己写 Plugin 也不是很难。而 Gulp 则因为基于 Node.js 的 Stream,对 Plugin 的开发者要有较高的要求。
    不过对于专业 Noder 来说,Gulp 还能收获性能。一方面是因为其异步的特性,没有先后依赖的 tasks 是天生一起执行的;另一方面,由于 gulp.src 的缺省行为是把文档载入到 vinyl 对象的 buffer (fs.contents) ,因此向后传递过程中无需再次访问磁盘(或其他 IO 数据源),因此性能很好,尤其对针对大量细小的文件的操作,例如:编译拼接 jade 或者 stylus 的文件,这点要比 Grunt 快很多倍。所以,给非 Noder 用,Grunt 不错;给 Noder,就用 Gulp。另外, Gulp 插件的代码量未必就小,这两个还是在一个量级上的。

本文标题:Gulp挑战Grunt,背后的哲学

本文链接:https://www.haomeiwen.com/subject/mffstttx.html