Java 世界里的 Maven 提供了强大的包依赖管理和构建生命周期管理。在 JavaScript 的世界里,随着 Node.js 的流行,JavaScript 原生的构建工具已经成为可能。
Grunt.js 是基于 Node.js 的自动化任务运行器。Grunt.js 结合 NPM 的包依赖管理,完全可以媲美 Maven。Grunt.js 天然适合前端应用程序的构建——不仅限于 JavaScript 项目,同样可以用于其他语言的应用程序构建。越来越多的 JavaScript 项目已经在使用 Grunt,其中最大的使用者包括著名的 jQuery 项目。
Grunt 的生态系统在迅速的成长,目前已经有上百种插件发布在 NPM 上可供选择。同时,任何人都可以方便的发布自己的插件到 NPM 上供其他人使用。
Grunt 没有像 Maven 那样强调构建的生命周期,各种任务的执行顺序可以随意配置。Grunt 本身仅是一个执行器,大量的功能都存在于 NPM 管理的插件中。特别是以grunt-contrib-
开头的核心插件,覆盖了大部分的核心功能,比如 handlebars,jade,less,compass,jshint,jasmine,clean,concat,minify,copy,uglify,watch,minify,uglify 等。
通过提供通用的接口以进行代码规范检验(Lint)、合并、压缩、测试及版本控制等任务,Grunt 使入门门槛大大降低了。
安装
Grunt 目前的最新版本为 v0.4 版。相比 v0.3 版,Grunt 本身不再作为一个整体全局模块安装,而是分为了三个部分:grunt,grunt-cli,grunt-init。
grunt-cli 用于命令行启动 Grunt,必须作为全局模块安装:
npm install -g grunt-cli
grunt-init 是可选的,用于以命令行新建 Grunt 项目模板,必须作为全局模块来安装:
npm install -g grunt-init
Grunt 核心作为项目或插件的依赖包而单独存在,避免由于不同的插件依赖的 Grunt 版本不同而造成的冲突。
新建项目工程
在命令行中执行 grunt init
无论新建项目是应用程序还是一个 Grunt 插件,Grunt 生成的工程都是一个标准的 NPM 模块。
新工程目录下有两个文件非常重要:
package.json
: NPM 的发布配置文件,包含了包依赖信息和项目工程的元数据。熟悉 Node.js 的读者应该不陌生。
gruntfile.js
: Grunt 配置文件,用于配置或定义 Grunt 任务,以及加载 Grunt 插件等。
Grunt 任务配置
所有的任务配置都存在 gruntfile.js 中。作为 JavaScript 源文件,其配置信息以 JSON 对象的方式来存放,并可以使用 JS 函数来动态生成,比 Maven 的 XML 配置方式更加简洁和富有灵活性。一份典型的 gruntfile 如下:
module.exports = function(grunt) { // 项目配置信息. grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), uglify: { options: { banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n' }, build: { src: 'src/<%= pkg.name %>.js', dest: 'build/<%= pkg.name %>.min.js' } } }); // 加载 "uglify" 插件.. grunt.loadNpmTasks('grunt-contrib-uglify'); // 注册默认任务. grunt.registerTask('default', ['uglify']); };
可以看到,它就是一个标准的 Node.js 模块定义。
- 插件配置:形如 JSON 对象的配置信息,包括定义文件操作的源与目标,标志变量等。Grunt 还提供了一些模板变量用于插入常用的信息,如日期时间等。
- 加载插件:通过 grunt.loadNpmTask(),显式地加载外部 Grunt 插件。
- 注册自定义任务。用法见下文“自定义任务”。可以注册任意的任务链,但至少注册一个默认任务链。
运行
进入项目的根目录,在命令行执行grunt
即可根据配置文件来执行所有的自动化任务。
$ grunt Running "jshint:gruntfile" (jshint) task 1 file lint free. Running "jshint:src" (jshint) task 1 file lint free. Running "uglify:dist" (uglify) task File "dist/ba-tiny-pubsub.min.js" created. Uncompressed size: 389 bytes. Compressed size: 119 bytes gzipped (185 bytes minified). Done, without errors. $
也可以选择性地执行某些任务,甚至给不同的插件附加命令行参数:
例如,只执行 clean 插件的 dist 子任务和 jasmine 插件的所有任务:
grunt clean:dist jasmine
流行的插件
v0.4 版以后,各种常用插件基本都归入了 grunt-contrib-xxx
系列。下面介绍几个比较常用的插件:
- grunt-contrib-clean: 用于清理指定文件(夹),一般是构建之前或之后进行
- grunt-contrib-coffee: 将 CoffeeScript 编译为 JavaScript
- grunt-contrib-compass: 调用 Compass 工具生成 CSS 文件
- grunt-contrib-concat: 连接源文件,减少 HTTP 请求
- grunt-contrib-copy: 复制文件(夹)
- grunt-contrib-handlebars: 将 handlebars 模板预编译为 JST 文件,提高运行时性能
- grunt-contrib-jasmine: 借助 Jasmine 在 PhantomsJS 中运行单元测试,结合 grunt-template-jasmine-istanbul,还能实现单元测试覆盖率计算
- grunt-contrib-jshint: JS 代码质量检查工具,类似 jsLint
- grunt-contrib-watch: 监视磁盘文件,一旦更改就会重新运行指定的任务,例如使 http 服务器重新加载源文件
- grunt-contrib-ugligy: 压缩 JS 源文件,提高运行时性能
自定义任务
利用 Grunt 的 API 可以很方便的创建自定义任务和插件。两种方式所用到的 API 完全一致,只不过自定义任务在写在项目的 gruntfile.js 中,而插件可以自主发布。
任务分为两种:
- grunt.registerTask():以 JS 函数的形式定义的一个独立的任务,可以从命令行传入参数、调用 Grunt API、 或者仅仅是作为别名(alias),链式地调用其他任务。
- grunt.registerMultiTask():与 registerTask() 函数不同的是,同一任务可以同时存在多组配置目标 (target),Grunt 会自动地遍历读取每组配置目标分别执行,也可以指定仅以某个配置来单独执行任务。这对于文件操作任务比较常用。例如要使用清理文件的 clean 插件,可能会有两组配置,分别用于清理打包文件夹和清理单元测试临时文件夹。此外,jshint,concat,uglify 等也都是 MuitiTask 类型。
API 扩展
无论是自定义任务还是创建独立的 Grunt 插件,都少不了要用到 Grunt 提供的 API。其中一些是对 Node.js 功能的扩展,另一些是满足 Grunt 的特殊需要:
- grunt.config: 读取和管理 gruntfile 中的配置信息
- grunt.event: 自定义事件
- grunt.fail: 用于异常处理时发出警告或强制终止任务
- grunt.file: 用于磁盘文件管理的扩展函数,包括 read,write,copy,delete,mkdir,expand,exist,path 等
- grunt.log: Grunt 自有的 log 功能
- grunt.option: 用于从命令行中读取参数
- grunt.task: 用于注册自定义任务和加载外部任务
- grunt.template: 处理 gruntfile 中的模板变量,以及提供了常用的日期模板辅助(helper)函数,包括 template.data,template.today
- grunt.util: 各种公用工具函数,以及集成了各种外部库,包括 Lo-Dash,Async,Hook 等
小结
本文介绍了基于 Node.js 的自动化构建工具 Grunt.js,展示了其基本组成、安装步骤,配置文件示例以及运行示例,并且列出了流行的 Grunt 插件以及其 API 扩展接口。
作者简介:
申健,在跨国企业从事 __8__ 年以上的研发和管理工作,涉及电信、金融、互联网等领域,拥有丰富的移动应用程序和面向服务分布式系统的项目经验。2007__ 年开始接触敏捷开发。对大型组织(500__ 人以上_)的敏捷转型,以及敏捷工程实践的落地运用具有丰富的经验。拥有 __CSP, CSM。擅长面向服务 __(SOA)_的分布式架构分析,以及移动应用的前后端整体解决方案。天津软件沙龙和敏捷之旅天津站等活动的组织者。
感谢田永强对本文的审校。
给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ )或者腾讯微博( @InfoQ )关注我们,并与我们的编辑和其他读者朋友交流。
评论