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插件短小得多!

    相关文章

      网友评论

      • 梦林:@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