Grunt docs
Grunt和 Grunt 插件是通过 npm 安装并管理的,npm是 Node.js 的包管理器。
安装 grunt-cli
npm install grunt-cli -g
注意,安装grunt-cli并不等于安装了 Grunt!Grunt CLI的任务很简单:调用与Gruntfile在同一目录中 Grunt。这样带来的好处是,允许你在同一个系统上同时安装多个版本的 Grunt。
这样就能让多个版本的 Grunt 同时安装在同一台机器上。
每次运行grunt 时,他就利用node提供的require()系统查找本地安装的 Grunt。正是由于这一机制,你可以在项目的任意子目录中运行grunt 。
如果找到一份本地安装的 Grunt,CLI就将其加载,并传递Gruntfile中的配置信息,然后执行你所指定的任务。为了更好的理解 Grunt CLI的执行原理,请阅读源码。
本地安装 grunt
npm install grunt
本地安装 grunt 到当前文件夹( npm install grunt --registry http://registry.cnpmjs.org
从镜像站安装)
grunt项目
在 grunt-cli
和 grunt
已经安装好后,在项目文件夹创建好 package.json
和 gruntfile.js
文件, 然后就可以安装 grunt任务插件
npm install //安装任务插件
grunt //执行任务 默认执行 default
创建grunt项目
一般需要在你的项目中添加两份文件:package.json
和 Gruntfile
。
package.json: 此文件被npm用于存储项目的元数据,以便将此项目发布为npm模块。你可以在此文件中列出项目依赖的grunt和Grunt插件,放置于devDependencies配置段内。
Gruntfile: 此文件被命名为 Gruntfile.js
或 Gruntfile.coffee
,用来配置或定义任务(task)并加载Grunt插件的。 此文档中提到的 Gruntfile 其实说的是一个文件,文件名是 Gruntfile.js 或 Gruntfile.coffee。
1. package.json
package.json
应当放置于项目的根目录中,与Gruntfile.js
在同一目录中,并且应该与项目的源代码一起被提交。
创建package.json
文件的3种方式:
-
grunt-init自动创建项目的
package.json
文件。 -
npm init
问答的方式创建package.json
- 手工编写
package.json
文件,name,version,main,dependencies,devDependencies 常用字段
示例
{
"name": "my-project-name",
"version": "0.1.0",
"devDependencies": {
"grunt": "~0.4.5",
"grunt-contrib-jshint": "~0.10.0",
"grunt-contrib-nodeunit": "~0.4.1",
"grunt-contrib-uglify": "~0.5.0"
}
}
2. 安装Grunt 和 grunt插件
npm install //安装package.json中定义的插件
npm install ungilify --save-dev //向已经存在的package.json 文件中添加Grunt和grunt插件
npm install grunt --save-dev
npm install grunt-contrib-jshint --save-dev
在 Grunt 插件 页面可以看到当前可用的 Grunt 插件,他们可以直接在项目中安装并使用。
3. gruntfile.js
Gruntfile.js
或 Gruntfile.coffee
文件是有效的 JavaScript 或 CoffeeScript 文件,应当放在你的项目根目录中,和package.json
文件在同一目录层级,并和项目源码一起加入源码管理器。
gruntfile.js
由以下4部分组成:
- "wrapper" 函数
- 项目和任务配置
- 加载 grunt 插件和任务
- 自定义任务
示例
在下面列出的这个 Gruntfile.js
中,package.json
文件中的项目元数据(metadata)被导入到 Grunt 配置中, grunt-contrib-uglify
插件中的uglify 任务(task)被配置为压缩(minify)源码文件并依据上述元数据动态生成一个文件头注释。当在命令行中执行 grunt 命令时,uglify 任务将被默认执行。
module.exports = function(grunt) {
// Project configuration. 项目和任务配置
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'), //pkg为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']);
};
gruntfile.js详解
-
"wrapper" 函数
每一份Gruntfile
(和grunt插件
)都遵循同样的格式,你所书写的Grunt代码必须放在此函数内:module.exports = function(grunt){
//do grunt-related things in here
} -
项目和任务配置
大部分的Grunt任务都依赖某些配置数据,这些数据被定义在一个object内,并传递给grunt.initConfig
方法。由于<% %>模板字符串可以引用任意的配置属性,因此可以通过这种方式来指定诸如文件路径和文件列表类型的配置数据,从而减少一些重复的工作。
你可以在这个配置对象中(传递给initConfig()方法的对象)存储任意的数据。此外,由于这本身就是JavaScript,你不仅限于使用JSON;你可以在这里使用任意的有效的JS代码。如果有必要,你甚至可以以编程的方式生成配置。
与大多数task一样,
grunt-contrib-uglify
插件中的uglify 任务要求它的配置被指定在一个同名属性中。在这里有一个例子, 我们指定了一个banner选项(用于在文件顶部生成一个注释)。// 项目和任务配置
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'), //获取 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' //压缩目标文件
}
}
}); -
加载grunt插件任务
像 concatenation、[minification]、grunt-contrib-uglify 和 linting这些常用的任务(task)都已经以grunt插件的形式被开发出来了。只要在 package.json 文件中被列为dependency(依赖)的包,并通过npm install安装之后,都可以在Gruntfile中以简单命令的形式使用:// 加载能够提供"uglify"任务的插件。
grunt.loadNpmTasks('grunt-contrib-uglify'); -
自定义任务
通过定义default
任务,可以让Grunt默认执行一个或多个任务。在下面的这个案例中,执行grunt
命令时如果不指定一个任务的话,将会执行uglify任务。这和执行grunt uglify
或者grunt default
的效果一样。default任务列表数组中可以指定任意数目的任务(可以带参数)。//default task(s)
grunt.registerTask('default',['uglify']);如果Grunt插件中的任务(task)不能满足你的项目需求,你还可以在Gruntfile中自定义任务(task)。例如,在下面的 Gruntfile 中自定义了一个default 任务,并且他甚至不依赖任务配置:
module.exports = function(grunt) { // A very basic default task.
grunt.registerTask('default', 'Log some stuff.', function() {
grunt.log.write('Logging some stuff...').ok();
}); };
4. 执行 grunt
grunt
查看帮助
grunt -h
;
;
Grunt 配置
Grunt的task配置都是在 Gruntfile
中的 grunt.initConfig
方法中指定的。此配置主要是以任务名称命名的属性,也可以包含其他任意数据。一旦这些代表任意数据的属性与任务所需要的属性相冲突,就将被忽略。
此外,由于这本身就是JavaScript,因此你不仅限于使用JSON;你可以在这里使用任何有效的JavaScript。必要的情况下,你甚至可以以编程的方式生成配置。
grunt.initConfig({
concat: {
// 这里是concat任务的配置信息。
},
uglify: {
// 这里是uglify任务的配置信息
},
// 任意数据。
my_property: 'whatever',
my_src_files: ['foo/*.js', 'bar/*.js'],
});
任务配置和目标
当运行一个任务时,Grunt会自动查找配置对象中的同名属性 (如: grunt uglify
) 。多任务(multi-task)可以通过任意命名的“目标(target)”来定义多个配置。在下面的案例中,concat任务有名为foo和bar两个目标,而uglify任务仅仅只有一个名为bar目标。
grunt.initConfig({
concat: {
foo: {
// concat task "foo" target options and files go here.
},
bar: {
// concat task "bar" target options and files go here.
},
},
uglify: {
bar: {
// uglify task "bar" target options and files go here.
},
},
});
同时指定任务(task)和目标(target),例如grunt concat:foo
或者grunt concat:bar
,将只会处理指定目标(target)的配置,而运行 grunt concat
将遍历所有目标(target)并依次处理。注意,如果一个任务使用grunt.task.renameTask重命名过,Grunt将在配置对象中查找以新的任务名命名的属性。
options 属性
在一个任务配置中,options
属性可以用来指定覆盖内置属性的默认值。此外,每一个目标(target)中还可以拥有一个专门针对此目标(target)的options
属性。目标(target)级的options
将会覆盖任务级的options
。
options
属性是可选的,如果不需要,可忽略
grunt.initConfig({
concat: {
options: {
// 这里是任务级的Options,覆盖默认值
},
foo: {
options: {
// "foo" target options may go here, overriding task-level options.
},
},
bar: {
// No options specified; this target will use task-level options.
},
},
});
文件
由于大多的任务都是执行文件操作,Grunt有一个强大的抽象层用于声明任务应该操作哪些文件。这里有好几种定义src-dest(源文件-目标文件)文件映射的方式,均提供了不同程度的描述和控制操作方式。任何一种多任务(multi-task)都能理解下面的格式,所以你只需要选择满足你需求的格式就行。
所有的文件格式都支持src和dest属性,此外"Compact"[简洁]和"Files Array"[文件数组]格式还支持以下一些额外的属性:
- filter 它通过接受任意一个有效的fs.Stats方法名或者一个函数来匹配src文件路径并根据匹配结果返回true或者false。
- nonull 如果被设置为 true,未匹配的模式也将执行。结合Grunt的--verbore标志, 这个选项可以帮助用来调试文件路径的问题。
- dot 它允许模式模式匹配句点开头的文件名,即使模式并不明确文件名开头部分是否有句点。
- matchBase如果设置这个属性,缺少斜线的模式(意味着模式中不能使用斜线进行文件路径的匹配)将不会匹配包含在斜线中的文件名。 例如,a?b将匹配/xyz/123/acb但不匹配/xyz/acb/123。
- expand 处理动态的src-dest文件映射,更多的信息请查看动态构建文件对象。
其他的属性将作为匹配项传递给底层的库。 请查看node-glob 和minimatch 文档以获取更多信息。
简洁格式
这种形式允许每个目标对应一个src-dest文件映射。通常情况下它用于只读任务,比如grunt-contrib-jshint,它就只需要一个单一的src属性,而不需要关联的dest选项. 这种格式还支给每个src-dest文件映射指定额外的属性。
grunt.initConfig({
jshint: {
foo: {
src: ['src/aa.js', 'src/aaa.js']
},
},
concat: {
bar: {
src: ['src/bb.js', 'src/bbb.js'],
dest: 'dest/b.js',
},
},
});
文件对象格式
这种形式支持每个目标对应多个src-dest形式的文件映射,属性名就是目标文件,源文件就是它的值(源文件列表则使用数组格式声明)。可以使用这种方式指定数个src-dest文件映射, 但是不能够给每个映射指定附加的属性。
grunt.initConfig({
concat: {
foo: {
files: {
'dest/a.js': ['src/aa.js', 'src/aaa.js'],
'dest/a1.js': ['src/aa1.js', 'src/aaa1.js'],
},
},
bar: {
files: {
'dest/b.js': ['src/bb.js', 'src/bbb.js'],
'dest/b1.js': ['src/bb1.js', 'src/bbb1.js'],
},
},
},
});
文件数组格式
这种形式支持每个目标对应多个src-dest文件映射,同时也允许每个映射拥有额外属性:
grunt.initConfig({
concat: {
foo: {
files: [
{src: ['src/aa.js', 'src/aaa.js'], dest: 'dest/a.js'},
{src: ['src/aa1.js', 'src/aaa1.js'], dest: 'dest/a1.js'},
],
},
bar: {
files: [
{src: ['src/bb.js', 'src/bbb.js'], dest: 'dest/b/', nonull: true},
{src: ['src/bb1.js', 'src/bbb1.js'], dest: 'dest/b1/', filter: 'isFile'},
],
},
},
});
自定义过滤函数
filter属性可以给你的目标文件提供一个更高级的详细帮助信息。只需要使用一个有效的fs.Stats 方法名。下面的配置仅仅清理一个与模式匹配的真实的文件:
grunt.initConfig({
clean: {
foo: {
src: ['tmp/**/*'],
filter: 'isFile',
},
},
});
或者创建你自己的filter函数,根据文件是否匹配来返回true或者false。下面的例子将仅仅清理一个空目录:
grunt.initConfig({
clean: {
foo: {
src: ['tmp/**/*'],
filter: function(filepath) {
return (grunt.file.isDir(filepath) && require('fs').readdirSync(filepath).length === 0);
},
},
},
});
通配符模式
通常分别指定所有源文件路径是不切实际的,因此Grunt通过内置支持node-glob 和 minimatch 库来匹配文件名(又叫作globbing)。
然这并不是一个综合的匹配模式方面的教程,你只需要知道如何在文件路径匹配过程中使用它们即可:
-
*
匹配任意数量的字符,但不匹配 / -
?
匹配单个字符,但不匹配 / -
**
匹配任意数量的字符,包括 /,只要它是路径中唯一的一部分 -
{}
允许使用一个逗号分割的“或”表达式列表 -
!
在模式的开头用于排除一个匹配模式所匹配的任何文件
每个人都需要知道的是:foo/*.js
将匹配位于foo/目录下的所有的.js结尾的文件;而foo/**/*js
将匹配foo/目录以及其子目录中所有以.js结尾的文件。
// 指定单个文件:
{src: 'foo/this.js', dest: ...}
// 指定一个文件数组:
{src: ['foo/this.js', 'foo/that.js', 'foo/the-other.js'], dest: ...}
// 使用一个匹配模式:
{src: 'foo/th*.js', dest: ...}
// 一个独立的node-glob模式:
{src: 'foo/{a,b}*.js', dest: ...}
// 也可以这样编写:
{src: ['foo/a*.js', 'foo/b*.js'], dest: ...}
// foo目录中所有的.js文件,按字母顺序排序:
{src: ['foo/*.js'], dest: ...}
// 首先是bar.js,接着是剩下的.js文件,并按字母顺序排序:
{src: ['foo/bar.js', 'foo/*.js'], dest: ...}
// 除bar.js之外的所有的.js文件,按字母顺序排序:
{src: ['foo/*.js', '!foo/bar.js'], dest: ...}
// 按字母顺序排序的所有.js文件,但是bar.js在最后。
{src: ['foo/*.js', '!foo/bar.js', 'foo/bar.js'], dest: ...}
// 模板也可以用于文件路径或者匹配模式中:
{src: ['src/<%= basename %>.js'], dest: 'build/<%= basename %>.min.js'}
// 它们也可以引用在配置中定义的其他文件列表:
{src: ['foo/*.js', '<%= jshint.all.src %>'], dest: ...}
动态构建文件对象
当你希望处理大量的单个文件时,这里有一些附加的属性可以用来动态的构建一个文件列表。这些属性都可以用于Compact和Files Array文件映射格式。
expand
设置为true用于启用下面的选项:
-
cwd
所有src指定的匹配都将相对于此处指定的路径(但不包括此路径) 。 -
src
相对于cwd路径的匹配模式。 -
dest
目标文件路径前缀。 -
ext
对于生成的dest路径中所有实际存在文件,均使用这个属性值替换扩展名。 -
extDot
用于指定标记扩展名的英文点号的所在位置。可以赋值 'first' (扩展名从文件名中的第一个英文点号开始) 或 'last' (扩展名从最后一个英文点号开始),默认值为 'first' [添加于 0.4.3 版本] -
flatten
从生成的dest路径中移除所有的路径部分。 -
rename
对每个匹配的src文件调用这个函数(在重命名后缀和移除路径之后)。dest和匹配的src路径将被作为参数传入,此函数应该返回一个新的dest值。 如果相同的dest返回不止一次,那么,每个返回此值的src来源都将被添加到一个数组中作为源列表。
在下面的例子中,uglify 任务中的static_mappings和dynamic_mappings两个目标具有相同的src-dest文件映射列表, 这是因为任务运行时Grunt会自动展开dynamic_mappings文件对象为4个单独的静态src-dest文件映射--假设这4个文件能够找到。
可以指定任意静态src-dest和动态的src-dest文件映射相互结合。
grunt.initConfig({
uglify: {
static_mappings: {
// Because these src-dest file mappings are manually specified, every
// time a new file is added or removed, the Gruntfile has to be updated.
files: [
{src: 'lib/a.js', dest: 'build/a.min.js'},
{src: 'lib/b.js', dest: 'build/b.min.js'},
{src: 'lib/subdir/c.js', dest: 'build/subdir/c.min.js'},
{src: 'lib/subdir/d.js', dest: 'build/subdir/d.min.js'},
],
},
dynamic_mappings: {
// Grunt will search for "**/*.js" under "lib/" when the "uglify" task
// runs and build the appropriate src-dest file mappings then, so you
// don't need to update the Gruntfile when files are added or removed.
files: [
{
expand: true, // Enable dynamic expansion.
cwd: 'lib/', // Src matches are relative to this path.
src: ['**/*.js'], // Actual pattern(s) to match.
dest: 'build/', // Destination path prefix.
ext: '.min.js', // Dest filepaths will have this extension.
extDot: 'first' // Extensions in filenames begin after the first dot
},
],
},
},
});
模板
使用<% %>
分隔符指定的模板会在任务从它们的配置中读取相应的数据时将自动扩展扫描。模板会被递归的展开,直到配置中不再存在遗留的模板相关的信息(与模板匹配的)。
整个配置对象决定了属性上下文(模板中的属性)。此外,在模板中使用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({
concat: {
sample: {
options: {
banner: '/* <%= baz %> */\n', // '/* abcde */\n'
},
src: ['<%= qux %>', 'baz/*.js'], // [['foo/*.js', 'bar/*.js'], 'baz/*.js']
dest: 'build/<%= baz %>.js', // 'build/abcde.js'
},
},
//用于任务配置模板的任意属性
foo: 'c',
bar: 'b<%= foo %>d', // 'bcd'
baz: 'a<%= bar %>e', // 'abcde'
qux: ['foo/*.js', 'bar/*.js'],
});
导入外部数据
在下面的Gruntfile中,项目的元数据是从package.json
文件中导入到Grunt配置中的,并且grunt-contrib-uglify
插件中的 uglify 任务被配置用于压缩一个源文件以及使用该元数据动态的生成一个banner注释。
Grunt有grunt.file.readJSON和grunt.file.readYAML两个方法分别用于引入JSON和YAML数据。
Gruntfile 实例
下面就针对一个 Gruntfile
案例做简单分析,也可以作为一个实例使用:
module.exports = function(grunt) {
grunt.initConfig({
jshint: {
files: ['Gruntfile.js', 'src//*.js', 'test//*.js'],
options: {
globals: {
jQuery: true
}
}
},
watch: {
files: ['<%= jshint.files %>'],
tasks: ['jshint']
}
});
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.registerTask('default', ['jshint']);
};
在页面底部是这个 Gruntfile
实例的完整内容,如果你按顺序阅读本文的话,可以跟随我们一步步分析这个文件中的每一部分。我们会用到以下5个 Grunt 插件:
- grunt-contrib-uglify
- grunt-contrib-qunit
- grunt-contrib-concat
- grunt-contrib-jshint
- grunt-contrib-watch
第一部分是"wrapper" 函数,它包含了整个Grunt配置信息。
module.exports = function(grunt) {
}
在这个函数中,我们可以初始化 configuration 对象:
grunt.initConfig({
});
接下来可以从package.json
文件读入项目配置信息,并存入pkg
属性内。这样就可以让我们访问到package.json
文件中列出的属性了,如下:
pkg: grunt.file.readJSON('package.json')
到目前为止我们就可以看到如下配置:
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json')
});
};
现在我们就可以为我们的每个任务来定义相应的配置(逐个定义我们为项目定义的任务配置),然后每个任务的配置对象作为Grunt配置对象(即grunt.initConfig({})所接受的配置对象)的属性,并且这个属性名称与任务名相同。因此"concat"任务就是我们的配置对象中的"concat"键(属性)。下面便是我的"concat"任务的配置对象。
concat: {
options: {
// 定义一个用于插入合并输出文件之间的字符
separator: ';'
},
dist: {
// 将要被合并的文件
src: ['src/**/*.js'],
// 合并后的JS文件的存放位置
dest: 'dist/<%= pkg.name %>.js'
}
}
注意我是如何引用JSON文件(也就是我们在配置对象顶部引入的配置文件)中的name
属性的。这里我使用pkg.name
来访问我们刚才引入并存储在pkg
属性中的package.json
文件信息,它会被解析为一个JavaScript对象。Grunt自带的有一个简单的模板引擎用于输出配置对象(这里是指package.json
中的配置对象)属性值,这里我让concat
任务将所有存在于src/
目录下以.js
结尾的文件合并起来,然后存储在dist
目录中,并以项目名来命名。
现在我们来配置uglify插件,它的作用是压缩(minify)JavaScript文件:
uglify: {
options: {
// 此处定义的banner注释将插入到输出文件的顶部
banner: '/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> */\n'
},
dist: {
files: {
'dist/<%= pkg.name %>.min.js': ['<%= concat.dist.dest %>']
}
}
}
这里我们让uglify
在dist/
目录中创建了一个包含压缩结果的JavaScript文件。注意这里我使用了<%= concat.dist.dest>
,因此uglify会自动压缩concat任务中生成的文件。
QUnit插件的设置非常简单。 你只需要给它提供用于测试运行的文件的位置,注意这里的QUnit是运行在HTML文件上的。
qunit: {
files: ['test/**/*.html']
},
JSHint插件的配置也很简单:
jshint: {
// define the files to lint
files: ['gruntfile.js', 'src/**/*.js', 'test/**/*.js'],
// configure JSHint (documented at http://www.jshint.com/docs/)
options: {
// more options here if you want to override JSHint defaults
globals: {
jQuery: true,
console: true,
module: true
}
}
}
JSHint只需要一个文件数组(也就是你需要检测的文件数组), 然后是一个options
对象(这个对象用于重写JSHint提供的默认检测规则)。你可以到JSHint官方文档站点中查看完整的文档。如果你乐于使用JSHint提供的默认配置,那么在Gruntfile中就不需要重新定义它们了.
最后,我们来看看watch插件:
watch: {
files: ['<%= jshint.files %>'],
tasks: ['jshint', 'qunit']
}
你可以在命令行使用grunt watch
来运行这个任务。当它检测到任何你所指定的文件(在这里我使用了JSHint任务中需要检测的相同的文件)发生变化时,它就会按照你所指定的顺序执行指定的任务(在这里我指定了jshint和qunit任务)。
最后, 我们还要加载所需要的Grunt插件。 它们应该已经全部通过npm安装好了。
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-qunit');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-concat');
最后设置了一些task。最重要的是default任务:
// 在命令行上输入"grunt test",test task就会被执行。
grunt.registerTask('test', ['jshint', 'qunit']);
// 只需在命令行上输入"grunt",就会执行default task
grunt.registerTask('default', ['jshint', 'qunit', 'concat', 'uglify']);
下面便是最终完成的 Gruntfile
文件:
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
concat: {
options: {
separator: ';'
},
dist: {
src: ['src//.js'],
dest: 'dist/<%= pkg.name %>.js'
}
},
uglify: {
options: {
banner: '/! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> */\n'
},
dist: {
files: {
'dist/<%= pkg.name %>.min.js': ['<%= concat.dist.dest %>']
}
}
},
qunit: {
files: ['test//.html']
},
jshint: {
files: ['Gruntfile.js', 'src/**/.js', 'test/**/*.js'],
options: {
//这里是覆盖JSHint默认配置的选项
globals: {
jQuery: true,
console: true,
module: true,
document: true
}
}
},
watch: {
files: ['<%= jshint.files %>'],
tasks: ['jshint', 'qunit']
}
});
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-qunit');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.registerTask('test', ['jshint', 'qunit']);
grunt.registerTask('default', ['jshint', 'qunit', 'concat', 'uglify']);
};
File an issue.
创建任务
任务是Grunt的面包和奶油。就像你常用的工具,如: jshint
或 nodeunit
。每当运行Grunt时, 你可以为其指定一个或多个任务, 这些任务用于告诉Grunt你想要它做什么事情。
如果你没有指定一个任务,并且你已经定义一个名为 "default" 的任务,那么该任务将会默认被执行(不用诧异,总要做点儿什么啊!)。
任务别名
如果指定了一个任务列表,新任务将是这一个或多个指定任务的别名。当运行此 "任务别名" 时,在 taskList
中指定的每个任务都会按照其出现的顺序依次执行。taskList
参数必须时一个任务数组。
grunt.registerTask(taskName, [description, ] taskList)
下面的任务别名案例中定义了一个 'default' 任务,如果运行Grunt时没有指定任何任务,它将自动执行'jshint'、'qunit'、'concat' 和 'uglify' 任务。
grunt.registerTask('default', ['jshint', 'qunit', 'concat', 'uglify']);
还可以给任务指定参数。在下面的案例中,别名 "dist" 将执行 "concat" 和 "uglify" 两个任务,并且它们都带有一个 "dist" 参数:
grunt.registerTask('dist', ['concat:dist', 'uglify:dist']);
多任务
当运行一个多任务时,Grunt会自动从项目的配置对象中查找同名属性。多任务可以有多个配置,并且可以使用任意命名的'targets'。
同时指定像grunt concat:foo
或者grunt concat:bar
这样的任务和目标,在运行时Grunt只会处理指定目标的配置;然而如果运行grunt concat
,将会遍历所有的目标, 并按任务指定的顺序处理每个目标。注意,如果一个任务已经使用grunt.task.renameTask重命名过,Grunt将会自动在配置对象中查找新任务名称属性。
大部分的contrib任务(主要是指官方提供的任务),包括grunt-contrib-jshint插件的jshint任务,以及grunt-contrib-concat插件的concat任务都是多任务形式的。
grunt.registerMultiTask(taskName, [description, ] taskFunction)
对于指定的配置,这里有一个案例演示了如果通过grunt log:foo
运行Grunt,它会输出foo: 1,2,3
;如果通过grunt log:bar
来运行Grunt, 它会输出bar: hello world
。然而如果通过grunt log
运行Grunt, 它会输出foo: 1,2,3
,然后是bar: hello world
,最后是baz: false
(任务目标会按照指定的顺序进行处理)。
grunt.initConfig({
log: {
foo: [1, 2, 3],
bar: 'hello world',
baz: false
}
});
grunt.registerMultiTask('log', 'Log stuff.', function() {
grunt.log.writeln(this.target + ': ' + this.data);
});
"基本" 任务
当一个基本任务执行时,Grunt并不会检查配置和环境 -- 它仅仅执行指定的任务函数,并传递任何使用冒号分割的参数作为函数的参数。
grunt.registerTask(taskName, [description, ] taskFunction)
下面的案例中,如果执行 grunt foo:testing:123
,将输出日志 foo, testing 123
。 如果执行这个任务时不传递参数,只是执行 grunt foo
,那么将输出日志 foo, no args
。
grunt.registerTask('foo', 'A sample task that logs stuff.', function(arg1, arg2) {
if (arguments.length === 0) {
grunt.log.writeln(this.name + ", no args");
} else {
grunt.log.writeln(this.name + ", " + arg1 + " " + arg2);
}
});
自定义任务
你可以和任务一起疯狂。如果你的任务并没有遵循 "多任务" 结构,那就使用自定义任务。
grunt.registerTask('default', 'My "default" task description.', function() {
grunt.log.writeln('Currently running the "default" task.');
});
在一个任务内部,你可以执行其他的任务。
grunt.registerTask('foo', 'My "foo" task.', function() {
// Enqueue "bar" and "baz" tasks, to run after "foo" finishes, in-order.
grunt.task.run('bar', 'baz');
// Or:
grunt.task.run(['bar', 'baz']);
});
任务也可以是异步的。
grunt.registerTask('asyncfoo', 'My "asyncfoo" task.', function() {
// Force task into async mode and grab a handle to the "done" function.
var done = this.async();
// Run some sync stuff.
grunt.log.writeln('Processing task...');
// And some async stuff.
setTimeout(function() {
grunt.log.writeln('All done!');
done();
}, 1000);
});
任务也可以访问它们自身名称和参数。
grunt.registerTask('foo', 'My "foo" task.', function(a, b) {
grunt.log.writeln(this.name, a, b);
});
// 用法:
// grunt foo foo:bar
// logs: "foo", undefined, undefined
// logs: "foo", "bar", undefined
// grunt foo:bar:baz
// logs: "foo", "bar", "baz"
如果记录到任何错误,那么任务就会失败。
grunt.registerTask('foo', 'My "foo" task.', function() {
if (failureOfSomeKind) {
grunt.log.error('This is an error message.');
}
// 如果这个任务出现错误则返回false
if (ifErrors) { return false; }
grunt.log.writeln('This is the success message');
});
当任务失败时,所有后续任务都将终止,除非指定 --force
。
grunt.registerTask('foo', 'My "foo" task.', function() {
// Fail synchronously.
return false;
});
grunt.registerTask('bar', 'My "bar" task.', function() {
var done = this.async();
setTimeout(function() {
// Fail asynchronously.
done(false);
}, 1000);
});
任务还可以依赖于其他任务的成功执行。注意 grunt.task.requires
并不会真正的运行其他任务,它仅仅检查其它任务是否已经执行,并且没有失败。
grunt.registerTask('foo', 'My "foo" task.', function() {
return false;
});
grunt.registerTask('bar', 'My "bar" task.', function() {
// 如果"foo"任务运行失败或者没有运行则任务失败。
grunt.task.requires('foo');
// 如果"foo"任务运行成功则执行这里的代码。
grunt.log.writeln('Hello, world.');
});
// 用法:
// grunt foo bar
// 没有输出,因为"foo"失败。
// grunt bar
// 没有输出,因为"foo"从未运行。
如果任务需要的配置属性不存在,其也可能失败。
grunt.registerTask('foo', 'My "foo" task.', function() {
// Fail task if "meta.name" config prop is missing
// Format 1: String
grunt.config.requires('meta.name');
// or Format 2: Array
grunt.config.requires(['meta', 'name']);
// Log... conditionally.
grunt.log.writeln('This will only log if meta.name is defined in the config.');
});
任务还可以访问配置属性。
grunt.registerTask('foo', 'My "foo" task.', function() {
// 记录属性值,如果属性未定义(undefined)则返回null。
grunt.log.writeln('The meta.name property is: ' + grunt.config('meta.name'));
// 同样的记录属性值,如果属性未定义(undefined)则返回null。
grunt.log.writeln('The meta.name property is: ' + grunt.config(['meta', 'name']));
});
在 contrib tasks 中可以查看更多案例。
CLI 参数 / 环境
通过 process.env
来访问环境变量。
请参考 使用命令行工具章节,查看完整的的命令行选项列表。
为什么我的异步task没有完成?
如果发生这种情况,可能是由于你忘记调用 this.async 方法来告诉Grunt你的任务是异步的。为了简单起见,Grunt使用同步的编码风格,可以在task体中通过调用 this.async()
将其转换为异步的。
注意,传递 false
给 done()
函数就会告诉Grunt你的任务已经失败。
例如:
grunt.registerTask('asyncme', 'My asynchronous task.', function() {
var done = this.async();
doSomethingAsync(done);
});
额外参考资料
如果你需要更多参考资料来创建自己的 task ,请参考 API 文档 if you need extra reference to create your tasks.
File an issue.
使用命令行工具
安装命令行工具
执行 sudo npm install -g grunt-cli
。
grunt
命令行接口提供了一系列选项。你可以在你的终端中使用grunt -h
查看这个选项。
--help, -h
显示帮助信息。
--base, -b
指定一个基本路径。默认情况下,所有文件路径都是相对于Gruntfile
的。
还可以调用 grunt.file.setBase(...)
--no-color
禁用彩色输出。
--gruntfile
指定 Gruntfile
文件。
默认情况下,grunt会从当前目录或者父目录中寻找最近的Gruntfile.js
或者Gruntfile.coffee
文件。
--debug, -d
对支持调试的任务启用调试模式。
--stack
因警告或者致命错误退出时打印堆栈跟踪信息。
--force, -f
一种强制跳过警告信息的方式。
如果像从警告中得到提示,就不要使用这个选项,可以根据提示信息修正代码。
--tasks
指定一个包含可加载的任务和“额外”文件的目录。
还可以调用 grunt.loadTasks(...)
--npm
在通过npm安装的插件中检查可加载的任何以及额外文件。
还可以调用 grunt.loadNpmTasks(...)
--no-write
禁用写文件操作(可用于演示)。
--verbose, -v
冗长模式(Verbose mode)。会输出很多的信息。
--version, -V
打印grunt版本。结合 --verbose 一起使用可以获取更多信息。
--completion
输出shell的自动补全规则。更多信息参考grunt-cli相关的文档。
File an issue.