Grunt项目中都是通过“Gruntfile.js”来配置任务。可以说,“Gruntfile.js”文件在任何一个Grunt项目中都是必不可少的,从这也可以看出这个文件的重要性。欲要搞清楚Grunt怎么工作,就必须要先了解Grunt是如何通过“Gruntfile.js”配置任务,帮助你实现你需要的功能。我们通过下面的介绍,学习如何配置。
Gruntfile.js创建
假设您已经在您的系统中创建了一个Grunt项目“my-grunt”。要正常的运行这个Grunt项目,其中package.json和Gruntfile.js是必不可少的,而且他们都必须位于Grunt项目的跟目录之下。
最简单的Gruntfile.js文件内容包含如下初始信息:
module.exports = function(grunt){ //配置项目 grunt.initConfig({ //配置任务 }); //加载任务 grunt.loadNpmTasks('grunt任务插件名'); //默认任务 grunt.registerTasks('default',['任务名']); }; |
文件搞定后,那么问题来了,我们应该怎么在这个文件中配置需要的任务。接下来,我们一起来了解他的配置。
Wrapper函数
每个Gruntfile.js和Grunt插件都是使用这个基本格式,所有你的Grunt代码都必须指定在这个函数里面:
module.exports = function(grunt){ //你的grunt代码 }; |
Grunt配置
Grunt的任务配置都是在你的Gruntfile.js文件中的grunt.initConfig({});方法中指定。这个配置主要都是一些命名任务属性,通常任务都被定义为一个对象作为参数给grunt.initConfig()方法,而任务都是作为这个对象的属性定义。也可以包含任意其他数据。但这些属性不能与你的任务所需要的属性相冲突,否则它将被忽略(一般情况下,任务中的属性名都是约定俗成的)
此外,由于这本身就是javascript,因此你不仅限于使用json;你可以在这里使用任何有效的javascript。必要的情况下,你甚至可以以编程的方式生成配置(比如通过其他的程序生成一个或多个任务配置)。
gruntinitCongif({ concat:{ //这里是concat任务的配置信息 }, uglify:{ //这里是uglify任务的配置信息 }, my_property:'whatever', my_src_file:['foo/*js','bar/*js'] }); |
项目的任务配置
前面说过,大多数Grunt任务所依赖的配置数据都被定义在传递给grunt.initConfig方法的一个对象中。我们先来看一个简单的例子,为了实现实例中的功能,需要先完善package.json文件的内容,如下所示:
{ "name": "my-grunt", "version": "0.1.0", "description": "this is test project with grunt.", "author": "airen", "license": "BSD", "devDependencies": { "grunt": "~0.4.1", "grunt-contrib-uglify": "*" } } |
如果你不认识package.json中的内容,没关系,在下一节中我们将对package.json文件做一个较为详细的阐述。
在下面的示例中,我们会看到grunt.file.readJSON('pageage.json')会把存储在package.json中的JSON元素数据导入到Grunt配置中。由于<% %>模板字符串可以引用任意的配置属性,因此可以通过这种方式来制定诸如文件路径和文件列表类型的配置数据,从而减少一些重复的工作(比如我们通常需要通过复制粘贴的方式来在不同的地方引用同一个属性,使用<%%>的方式可以简单的理解为将某些特定的数据存储在变量中,然后在其他地方像使用变量一样使用这些数据属性)。
与大多数任务一样,grunt-contrib-uglify插件的uglify任务要求他的配置被指定在同一个同名属性中。在这里有一个例子,我们指定了一个banner
选项(用于在文件顶部生成一个注释),紧接都会是一个单一的名为build
的uglify目录,用于将一个js文件压缩为一个目标文件(比如将src/js
目录下的jquery-1.9.0.js
压缩为jquery-1.9.min.js
,然后存储到dest/js
目录)。
// 项目配置 grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), uglify: { options: { banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n' }, build: { src: 'src/js/<%= pkg.name %>.js', dest: 'build/js/<%= pkg.name %>.min.js' } } }); |
任务配置和目标
当运行一个任务时,Grunt会自动查找配置对象中的同名属性。多个任务可以有多个配置,每个任务可以使用任意的命名“targets”来自定义多个任务目标。在上面的示例中,添加Sass任务:
//Wrapper函数 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/js/<%= pkg.name %>.js', dest: 'build/js/<%= pkg.name %>.min.js' } }, sass: { dist: { options: { style: 'compressed' }, expand: true, cwd: 'src/styles/sass/', src: ['*.scss'], dest: 'build/styles/css', ext: '.css' }, dev: { options: { style: 'expanded', debugInfo: true, lineNumbers: true }, expand: true, cwd: 'src/styles/sass/', src: ['*.scss'], dest: 'build/styles/css', ext: '.css' } } }); }; |
在示例当中,sass
任务有两个字任务dist
和dev
。在命令行中执行grunt sass:dist
或者grunt sass:dev
的任务和目标只会处理指定的任务目标配置,而运行grunt sass
将遍历所有的(定义在sass
任务中的)目标并依次处理。注意,如果一个任务使用grunt.renameTask重命过,Grunt将在配置对象中查找新的任务名称属性。
加载Grunt插件和任务
许多常用的任务像concatenation,minification和linting都被作为grunt插件来使用。只要一个插件被作为一个依赖指定在项目的package.json
文件中,并且已经通过npm install
安装好,都可以在你的Gruntfile.js
文件中使用下面这个简单的命令启用它(所依赖的任务)。
//加载提供“uglify”任务的插件 |
设置默认任务
在Gruntfile.js
文件中,你可以通过定义一个default
任务来配置Grunt,让它默认运行一个或者多个任务。在下面的例子中,在命令中运行grunt
而不指定特定的任务将自动运行uglify
、sass
任务。这个功能与显示的运行grunt uglify
和grunt sass
或者等价运行grunt default
一样。你可以在任务参数数组指定任意数量的任务(这些任务可以带参数,也可以不带参数)。
// 默认任务 |
自定义任务
如果你的项目所需的任务没有对应的Grunt插件提供相应的功能,你可以在Grunt
内定义自定义的任务。例如,下面的Gruntfile就定义了一个完整的自定义的default
任务,它甚至没有利用任务配置(没有使用grunt.initConfig({})
方法):
module.exports = function(grunt){ |
自定义的项目特定的任务可以不定义在Gruntfile.js
中;它可以定义在一个外部.js
文件中,然后通过grunt.loadTasks方法来加载。
options
在一个任务配置中,options
属性可以用来指定覆盖内置属性的默认值。此外,每一个任务目标中更具体的目标都可以拥有一个options
属性。目标级的选项将会覆盖任务级的选项(就近原则——options
离目标越近,其优先级越高)。
options
对象是可选,如果不需要,可以省略。
grunt.initConfig({ |
文件
由于大多的任务都是执行文件操作,Grunt有一个强大的抽象声明说明任务应该操作哪些文件。这里有好几种定义src-dest(源文件——目标文件)文件映射的方式,都提供了不同程度的描述和控制操作方式。任何一种多任务(包含多个任务目标的任务)都能理解下面的格式,所以你只需要选择满足你需要的格式就行。
所有的文件格式都支持src
和dest
属性。
简洁格式
这种形式允许每个目标对应一个src-dest文件映射。通常情况下它用于只读任务,比如grunt-contrib-jshint,它就只需要一个单一的src
属性,而不需要关联的dest
选项。这种格式还支持给每个src-dest
文件映射指定附加属性。
文件对象格式
这种形式支持每个任务目标对就多个src-dest
形式的文件映射,属性名就是目标文件,源文件就是它的值(源文件列表则使用数组格式声明)。可以使用这种方式指定数个src-dest
文件映射,但是不能够给每个映射指定附加的属性。
grunt.initConfig({ |
文件数组格式
这种形式支持每个任务目标对应多个src-dest
文件映射,同时也允许每个映射拥有附加属性:
grunt.initConfig({ |
较老的格式
dest-as-target文件格式在多任务和目标形式出现之前是一个过渡形式,目标文件路径实际上就是目标名称。遗憾的是, 由于目标名称是文件路径,那么运行grunt task:target
可能不合适。此外,你也不能指定一个目标级的options
或者给每个src-dest
文件映射指定附加属性。
grunt.initConfig({ |
自定义过滤函数
filter
属性可以给你的目标文件提供一个更高级的详细帮助信息。只需要使用一个有效的fs.Stats方法名。下面的配置仅仅清理一个与模式匹配的真实的文件:
grunt.initConfig({ |
或者创建你自己的filter
函数根据文件是否匹配来返回true
或者false
。下面的例子将仅仅清理一个空目录:
grunt.initConfig({ |
通配符模式
通常分别指定所有源文件路径的是不切实际的(也就是将源文件-目标文件一一对应的关系列出来),因此Grunt支持通过内置的node-glob和minimatch库来匹配文件名(又叫作globbing
)。
当然这并不是一个综合的匹配模式方面的教程,你只需要知道如何在文件路径匹配过程中使用它们即可:
-
*
匹配任意数量的字符,但不匹配/
-
?
匹配单个字符,但不匹配/
-
**
匹配任意数量的字符,包括/
,只要它是路径中唯一的一部分 -
{}
允许使用一个逗号分割的列表或者表达式 -
!
在模式的开头用于否定一个匹配模式(即排除与模式匹配的信息)
大多数的人都知道foo/*.js
将匹配位于foo/
目录下的所有的.js
结尾的文件, 而foo/**/*js
将匹配foo/
目录以及其子目录中所有以.js
结尾的文件。
此外, 为了简化原本复杂的通配符模式,Grunt允许指定一个数组形式的文件路径或者一个通配符模式。模式处理的过程中,带有!
前缀模式不包含结果集中与模式相配的文件。 而且其结果集也是唯一的。
示例:
//可以指定单个文件 //一个独立的node-glob模式 //foo目录中所有的.js文件,按字母排序 //除bar.js之外的所有的.js文件,按字母排序 //模板也可以用于文件路径或者匹配模式中 |
可以在node-glob和minimatch文档中查看更多的关于通配符模式的语法。
构建动态文档对象
当你希望处理大量的单个文件时,这里有一些附加的属性可以用来动态的构建一个文件. 这些属性都可以指定在Compact
和Files Array
映射格式中(这两种格式都可以使用)。
expand
设置true
用于启用下面的选项:cwd
相对于当前路径所匹配的所有src
路径(但不包括当前路径。)src
相对于cwd
路径的匹配模式。dest
目标文件路径前缀。ext
使用这个属性值替换生成的dest
路径中所有实际存在文件的扩展名(比如我们通常将压缩后的文件命名为.min.js
)。flatten
从生成的dest
路径中移除所有的路径部分。rename
对每个匹配的src
文件调用这个函数(在执行ext
和flatten
之后)。传递dest
和匹配的src
路径给它,这个函数应该返回一个新的dest
值。 如果相同的dest
返回不止一次,每个使用它的src
来源都将被添加到一个数组中。
在下面的例子中,minify
任务将在static_mappings
和dynamic_mappings
两个目标中查看相同的src-dest
文件映射列表, 这是因为任务运行时Grunt
会自动展开dynamic_mappings
文件对象为4个单独的静态src-dest
文件映射--假设这4个文件能够找到。
可以指定任意结合的静态src-dest
和动态的src-dest
文件映射。
grunt.initConfig({ |
模版
使用<% %>
分隔符指定的模会在任务从它们的配置中读取相应的数据时将自动扩展扫描。模板会被递归的展开,直到配置中不再存在遗留的模板相关的信息(与模板匹配的)。
整个配置对象决定了属性上下文(模板中的属性)。此外,在模板中使用grunt
以及它的方法都是有效的,例如:<%= grunt.template.today('yyyy-mm-dd') %>
。
-
<%= prop.subprop %>
将会自动展开配置信息中的prop.subprop
的值,不管是什么类型。像这样的模板不仅可以用来引用字符串值,还可以引用数组或者其他对象类型的值。 -
<% %>
执行任意内联的JavaScript代码,对于控制流或者循环来说是非常有用的。
下面提供了一个concat
任务配置示例,运行grunt concat:sample
时将通过banner中的/* abcde */
连同foo/*.js+bar/*.js+bar/*.js
匹配的所有文件来生成一个名为build/abcde.js
的文件。
grunt.initConfig({ |
导入外部数据
在下面的Gruntfile
中,项目的元数据是从package.json
文件中导入到Grunt配置中的,并且grunt-contrib-uglify插件的uglify
任务被配置用于压缩一个源文件以及使用该元数据动态的生成一个banner
注释。
Grunt有grunt.file.readJSON
和grunt.file.readYAML
两个方法分别用于引入JSON和YAML数据。
grunt.initConfig({ |
到此,Grunt的任务配置相关的知识都已涉及到了,但不同的Grunt的任务其配置都略有不同,不过针对不同的插件任务配置,可以查阅每个Grunt插件的说明文档。
对于初学者来说,或许上面的内容有很多都理解不过来,其实我也一样,不过不用担心,当你看得多,做得多,到一定的时候自然就整得明白是怎么一回事。现在我们返回到示例中。根据上面的内容,给"my-grunt"项目创建了grunt-contrib-uglify
和grunt-contrib-sass
两个任务,并且完成了一些简单的配置:
//Wrapper函数 // 配置项目 uglify: { // 加载任务 // 默认任务. }; |
仅这样Grunt不会自动执行任何任务,因为我们只完成了配置,但没有安装Grunt里面的任务。如果你需要安装,只需要在命令终端执行:
$ npm install |
在终端中,看到下面这样的结束信息,表示安装成功:
... |
同时在项目的根目录下会自生成一个node_modules
目录,而且在Grunt中配置的相关的任务都放在这个目录中。
+my-grunt |
在整个项目中,uglify
任务是用来压缩项目中.js
文件,sass
任务是用来编译项目中的.scss
文件。为了证明我们创建的任务是正确的,我们在项目中创建一个js
文件和.scss
文件来检验是否成功。并且将.js
文件放置在src/js
目录下,.scss
文件放置在src/styles/sass
目录下。同时我们创建两测试文件:my-grunt.js
和main.scss
。
/my-grunt.js (function($){ |
//main.scss body { |
注:因为我们uglify
任务中使用是<% pk.name %>
模板,而这里面的name
对应的就是package.json
中的name
参数。因此需要创建一个my-grunt.js
。
你要执行Grunt任务的时候,只需要在命令行中执行:
$ grunt |
这个时候,你在终端可以看到对应的信息:
Running "uglify:build" (uglify) task Running "sass:dist" (sass) task Running "sass:dev" (sass) task Done, without errors. |
同时项目自动会生成一个名叫build
的目录,而压缩的.min.js
文件对应放置在build/js
目录下;编译出来的.css
放置在build/style/css
下:
+my-grunt |
因为我们在Grunt中配置了默认任务,如果没有设置默认任务,必须需要单独执行:
$ grunt uglify |
结论:
本文主要向大家介绍了Grunt项目中Gruntfile.js
文件的配置以及如何在这个文件中配置对应的任务。并且详细介绍了配置任务中的一些参数与细节的使用。最后通过一个简单示例,向大家介绍了如何使用Grunt配置任务。虽然文章中有很多部分对于初学者来说理解有一定难度,但我相信,随着时间的推移,会慢慢搞懂文章中介绍的所有知识。最后希望本文对初学者有所帮助。
文中有很多部分摘自Toobug和Basecss翻译的Grunt中文文档。在此特别感谢他们为我们提供中文版本的API。
- 新手上路
- 配置任务
- Getting started—grunt入门指南
- 前端工作流,Grunt上手指南
打完收工。。。。。