• 新一代前端构建利器Gulp介绍
    时间:2014-09-05   作者:nightire   出处:太极客

    Grunt 一直是前端领域构建工具(任务运行器或许更准确一些,因为前端构建只是此类工具的一部分用途)的王者,然而它也不是毫无缺陷的,近期风头正劲的 gulp.js 隐隐有取而代之的态势。那么,究竟是什么使得 gulp.js 备受关注呢?

    Grunt 之殇

    gulp.js 的作者 Eric Schoffstall 在他介绍 gulp.js 的 presentation 中总结了 Grunt 的几点不足之处:

    插件很难遵守单一责任原则。因为 Grunt 的 API 设计缺憾,使得许多插件不得不负责一些和其主要任务无关的事情。比如说要对处理后的文件进行更名操作,你可能使用的是 uglify 插件,也有可能使用的是 concat 插件(取决于工作流的最后一个环节是谁)。

    我的看法:这或许是个问题,对很多人来说 Grunt 插件多少存在“职责不明”和“越俎代庖”的情况。在我看来,这也是 Grunt 一个设计思想:把对文件的操作抽象为一个独立的组件(Files),任何插件都以相同的规则来使用它。遗憾在于,使用它的过程发生在每个插件的独立配置对象里,所以总给人一种“把不该这个插件做的事情丢给它来做”的别扭感觉。
    用插件做一些本来不需要插件来做的事情。因为 Grunt 提供了统一的 CLI 入口,子任务由插件定义,由 CLI 命令来调用执行,因此哪怕是很简单的外部命令(比如说运行 karma start)都得有一个插件来负责封装它,然后再变成 Grunt CLI 命令的参数来运行,多此一举。

    我的看法:举双手双脚赞成!
    试图用配置文件完成所有事,结果就是混乱不堪。规模较大,构建/分发/部署流程较为复杂的项目,其 Gruntfile 有多庞杂相信有经历的人都有所体会。而 gulp.js 奉行的是“写程序而不是写配置”,它走的是一种 node way。

    我的看法:对于 node.js 开发者来说这是好事,符合他们的一贯作风;不过对于那些纯前端工程师来说(数量不小),这似乎没有什么显著的改善。况且近来 Grunt 社区涌现了不少插件来帮助开发者组织/管理/简化臃肿的 Gruntfile,效果都还不错。所以关于这一点,就见仁见智吧。
    落后的流程控制产生了让人头痛的临时文件/文件夹所导致的性能滞后。这是 gulp.js 下刀子的重点,也是本标题里“流式构建”所解决的根本问题。流式构建改变了底层的流程控制,大大提高了构建工作的效率和性能,给用户的直观感觉就是:更快。

    我的看法:关于流式构建,短短几句话无法讲清它的来龙去脉,但是在 node.js 的世界里,streaming 确实是至关重要的。我推荐一份阅读材料:Stream Handbook,读过之后相信心里就有数了。

    作为对比和总结,作者列出了 gulp.js 的五大特点:

    1. 使用 gulp.js,你的构建脚本是代码,而不是配置文件;
    2. 使用标准库(node.js standard library)来编写脚本;
    3. 插件都很简单,只负责完成一件事-基本上都是 20 行左右的函数;
    4. 任务都以最大的并发数来执行;
    5. 输入/输出(I/O)是基于“流式”的。


    gulp.js 之道

    gulp.js 的官方文档都在 Github 上,本文是一个简介,更具体的细节还请自行阅读文档。在这里我就 gulp.js 的安装和使用流程做一个简述,先一起来领略一下 gulp.js 的风采吧。

    第一步:安装命令行工具

    $ npm install -g gulp
    第二步:在你的项目下把 gulp 安装为开发依赖组件(假设你已经创建好了 package.json)

    $ cd <YOUR_PROJECT>
    $ npm install gulp --save-dev
    第三步:在项目的根路径下创建 Gulpfile.js,初始内容为:

    var gulp = require('gulp');

    gulp.task('default', function () {
    });
    第四步:运行!

    $ gulp
    So far so good! 看起来和 Grunt 没差太远吧?的确如此,gulp.js 的学习曲线还是相当平缓的。接下来,为了能够顺利的编写构建脚本,我们来学习几个核心的 API 函数——别担心,gulp.js 的 API 非常简单,我们只需要了解四个就足以应对绝大多数的脚本编写了(而且用过 Grunt 的话,这四个都不是什么新鲜货)。

    1. gulp.task(name[, deps], fn):注册任务
    name 是任务名称;deps 是可选的数组,其中列出需要在本任务运行要执行的任务;fn 是任务体,这是 gulp.js 的核心了,需要花时间吃透它,详情见此。

    2. gulp.src(globs[, options]):指明源文件路径
    用过 Grunt 的话,globs 一定不会陌生,这里没什么变化;options 是可选的,具体请查看 gulp.js API

    3. gulp.dest(path):指明任务处理后的目标输出路径

    4. gulp.watch(glob[, options], tasks)/gulp.watch(glob[, options, cb]):监视文件的变化并运行相应的任务。你没看错,watch 作为核心 API 出现在 gulp.js 里了,具体用法还是要多看文档,不过接下来我们会演示简单的例子。

    范例

    我们练习一个最常见的范例,写一个 node.js 程序时所需要的构建脚本。为此我们要做三件事情(括号内列出对应插件的名字,更多插件请到此处寻找):

    1. 语法检查(gulp-jshint)
    2. 合并文件(gulp-concat)
    3. 压缩代码(gulp-uglify)
    另外,我们可能还需要文件更名操作,所以 gulp-rename 也会很有用。接着我们需要先在项目下安装这些插件:

    $ npm install <PLUGIN_NAME> --save-dev
    最后我们完成所有任务的编写,完整的代码如下:

    var gulp = require('gulp');
    var jshint = require('gulp-jshint');
    var concat = require('gulp-concat');
    var uglify = require('gulp-uglify');
    var rename = require('gulp-rename');

    // 语法检查
    gulp.task('jshint', function () {
        return gulp.src('src/*.js')
            .pipe(jshint())
            .pipe(jshint.reporter('default'));
    });

    // 合并文件之后压缩代码
    gulp.task('minify', function (){
         return gulp.src('src/*.js')
            .pipe(concat('all.js'))
            .pipe(gulp.dest('dist'))
            .pipe(uglify())
            .pipe(rename('all.min.js'))
            .pipe(gulp.dest('dist'));
    });

    // 监视文件的变化
    gulp.task('watch', function () {
        gulp.watch('src/*.js', ['jshint', 'minify']);
    });

    // 注册缺省任务
    gulp.task('default', ['jshint', 'minify', 'watch']);
    可以看出,基本上所有的任务体都是这么个模式:

    gulp.task('任务名称', function () {
        return gulp.src('文件')
            .pipe(...)
            .pipe(...)
            // 直到任务的最后一步
            .pipe(...);
    });
    非常容易理解!获取要处理的文件,传递给下一个环节处理,然后把返回的结果继续传递给下一个环节……直到所有环节完成。pipe 就是 stream 模块里负责传递流数据的方法而已,至于最开始的 return 则是把整个任务的 stream 对象返回出去,以便任务和任务可以依次传递执行。

    或许写成这样会更直观:

    gulp.task('task_name', function () {
        var stream = gulp.src('...')
            .pipe(...)
            .pipe(...)
            // 直到任务的最后一步
            .pipe(...);
        return stream;
    });
    至此,你已经可以使用 gulp.js 完成绝大多数的构建工作了。下一步,我也为你准备了几条建议:

    1. 花点时间浏览一下 gulp.js 插件库,大致了解下利用已有的插件你都可以做哪些事情
    2. 对于常用的插件,仔细阅读它们自己的文档,以便发挥出它们最大的功效
    3. 抽时间学习 gulp.js API,特别是 gulp.task() 里关于任务体的详细描述,学会如何执行回调函数(callback),如何返回 promise 等等
    4. 尝试编写适合自己工作流程和习惯的任务,如果它工作良好,把它做成插件发布给大家吧!

    转自:http://blog.segmentfault.com/nightire/1190000000435599

    网友留言/评论

    我要留言/评论

    相关文章

    关于OPENGIS介绍:值此FOSS4G大会即将召开之日,最近我会在Blog上依次介绍一些OpenGIS标准、架构及用于实现的软件。一方面给初涉此行的朋友一个快速入门的概览,另一方面也是对我接触OpenGIS近一年来的总结。
    jvectormap中的地图转换器的使用注意事项:JVectorMap是一个优秀的、兼容性强的jQuery地图插件。它可以工作在包括IE6在内的各款浏览器中,矢量图输出,除官方提供各国地图数据外,用户可以使用数据转换程序定制地图数据。本文主要介绍其提供的地图数据转化工具的使用和注意事项。
    浅谈Heatmap:在自然界之中,蛇的眼睛有夜视功能,即便是茫茫黑夜,它也能轻而易举的找到猎物,这是因为任何物体都会辐射热红外,且辐射的高低和温度成正比,由于生命体的体温会明显高于周围环境的温度,所以在蛇眼面前便无处遁形。热红外成像被广泛应用于军事领域,士兵带上能识别热红外的眼镜后能轻而易举的发现藏匿的敌人。
    GEOS介绍和安装:GEOS是“Geometry Engine, Open Source”的含义,提供了OGC规范中简单几何要素对象操作的C++语言的实现。在地理信息系统领域,拓扑模型是重要的,其计算方法简单但是难得以实现。使得GEOS不同于其他项目的也正是“空间谓词”与“空间操作”。空间谓词是比较两个空间对象并返回一个布尔变量值作为结果,它表明了存在于两个空间对象之间特殊的关系。比如典型的空间谓词有Contains(), Intersects(), Touches(), and Crosses()函数等。GEOS项目中对该些函数的实现是异常强壮的,即使是奇异几何对象或是临时的坐标系统运算也不能使其运算不正常或计算错误。目前绝大多数的商业软件仍然在最基础的空间谓词处理上相对成熟,这正是GEOS项目的重要意义。“空间操作”则主要是对两个几何对象进行计算并且返回一个新的几何实体。比较典型的操作函数如Difference(), Union()以及Buffer()等。GEOS中的操作算法已经被广泛的经过了测试。GEOS类库被各类开源空间信息软件项目广泛应用,使用GEOS,它们可以基于最新的规范的几何实体来完成,同时也拥有了复杂空间方法的实现。
    有关技术管理经理的一些思考:这些天里工作的环境发生了一些微小的变化,可能以后对基层开发的程序员也会有更加具体的影响。上周参加 Open Party 时,重点听了《那些失败的项目们》,分析了一个项目的提出、实施,直到最后失败的过程。我也在想一个技术团队究竟应该用怎样的一种管理方式,才能让技术团队的效率达到更优。
    如何成为一位优秀的创业CEO:做创业公司的 CEO 可以说是世界上最有挑战性的事情之一。你得让客户喜欢你的产品,得组建团队,还要想办法从客户、合作者和投资者那里拿到资金;并且要指导整个工作流程的优化。
    关于独立游戏开发5个过程的相关建议:作为一名独立游戏开发者,在制作游戏过程中尽量多学些东西这一点极为重要。我认为这一过程包含以下几个步骤:1.想法 2.原型 3.迭代 4.测试 5.完工。我希望针对这个过程的每个阶段提供一些对你们有所帮助的建议,以便你们加快开发速度,提升游戏质量。
    代码审查:好事?坏事?:在软件开发领域,代码审查看起来是一个少有争议、相当平和的话题。
    如何避免重构带来的危险:重构代码很危险,它会给测试工作增加巨大的负担。除非你的程序需要重构,一定不要轻易重构代码。我这里所说的并不是把一个for循环改成while循环,或把一个StringBuffer改成StringBuilder,我说的是大动作,例如重写一个方法,一个函数,甚至整个类或包。如果你缺乏对一个方法或一个类的了解,那你重构它的条件就不充分。即使你有一个天才的计划,你也需要和团队一起设计其中重大的修改。
    Facebook如何提高软件质量?:刘彪是微软测试技术团队的一名软件设计工程师,他在自己的博客上分享了Facebook如何提高软件质量的原则、手段和背后的原因。