# 配置任務
這個指南解釋了如何使用?`Gruntfile`?來為你的項目配置task。如果你還不知道?`Gruntfile`?是什么,請先閱讀?[快速入門](http://www.gruntjs.net/getting-started)?指南并看看這個[?Gruntfile 實例](http://www.gruntjs.net/sample-gruntfile)。
## 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會自動查找配置對象中的同名屬性。多任務(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](http://www.gruntjs.net/grunt.task#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方法名](http://nodejs.org/docs/latest/api/fs.html#fs_class_fs_stats)或者一個函數來匹配`src`文件路徑并根據匹配結果返回`true`或者`false`。
* `nonull`?如果被設置為?`true`,未匹配的模式也將執行。結合Grunt的`--verbore`標志, 這個選項可以幫助用來調試文件路徑的問題。
* `dot`?它允許模式模式匹配句點開頭的文件名,即使模式并不明確文件名開頭部分是否有句點。
* `matchBase`如果設置這個屬性,缺少斜線的模式(意味著模式中不能使用斜線進行文件路徑的匹配)將不會匹配包含在斜線中的文件名。 例如,a?b將匹配`/xyz/123/acb`但不匹配`/xyz/acb/123`。
* `expand`?處理動態的`src-dest`文件映射,更多的信息請查看[動態構建文件對象](http://www.gruntjs.net/configuring-tasks#building-the-files-object-dynamically)。
* 其他的屬性將作為匹配項傳遞給底層的庫。 請查看[node-glob](https://github.com/isaacs/node-glob)?和[minimatch](https://github.com/isaacs/minimatch)?文檔以獲取更多信息。
### 簡潔格式
這種形式允許每個目標對應一個**src-dest**文件映射。通常情況下它用于只讀任務,比如[grunt-contrib-jshint](https://github.com/gruntjs/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'},
],
},
},
});
~~~
### 較老的格式
**dest-as-target**文件格式在多任務和目標出現之前是一個過渡形式,目標文件路徑實際上就是目標名稱。遺憾的是, 由于目標名稱是文件路徑,那么運行`grunt task:target`可能不合適。此外,你也不能指定一個目標級的`options`或者給每個`src-dest`文件映射指定額外屬性。
此種格式已經不贊成使用,請盡量不要使用。
~~~
grunt.initConfig({
concat: {
'dest/a.js': ['src/aa.js', 'src/aaa.js'],
'dest/b.js': ['src/bb.js', 'src/bbb.js'],
},
});
~~~
### 自定義過濾函數
`filter`屬性可以給你的目標文件提供一個更高級的詳細幫助信息。只需要使用一個有效的[fs.Stats 方法名](http://nodejs.org/docs/latest/api/fs.html#fs_class_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](https://github.com/isaacs/node-glob)?和?[minimatch](https://github.com/isaacs/minimatch)?庫來匹配文件名(又叫作`globbing`)。
然這并不是一個綜合的匹配模式方面的教程,你只需要知道如何在文件路徑匹配過程中使用它們即可:
* `*`?匹配任意數量的字符,但不匹配?`/`
* `?`?匹配單個字符,但不匹配?`/`
* `**`?匹配任意數量的字符,包括?`/`,只要它是路徑中唯一的一部分
* `{}`?允許使用一個逗號分割的“或”表達式列表
* `!`?在模式的開頭用于排除一個匹配模式所匹配的任何文件
每個人都需要知道的是:`foo/*.js`將匹配位于`foo/`目錄下的所有的`.js`結尾的文件;而`foo/**/*js`將匹配`foo/`目錄以*及其子目錄*中所有以`.js`結尾的文件。
此外, 為了簡化原本復雜的通配符模式,Grunt允許指定一個數組形式的文件路徑或者一個通配符模式。所有模式按順序處理,模式處理的過程中,帶有`!`前綴的模式所匹配的文件將不包含在結果集中。 而且其結果集中的每一項也是唯一的。
例如:
~~~
// 指定單個文件:
{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: ...}
~~~
更多關于通配符模式的語法,請查看[node-glob](https://github.com/isaacs/node-glob)?和?[minimatch](https://github.com/isaacs/minimatch)?的文檔。
### 動態構建文件對象
當你希望處理大量的單個文件時,這里有一些附加的屬性可以用來動態的構建一個文件列表。這些屬性都可以用于`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 插件](http://github.com/gruntjs/grunt-contrib-uglify)中的?`uglify`?任務被配置用于壓縮一個源文件以及使用該元數據動態的生成一個banner注釋。
Grunt有`grunt.file.readJSON`和`grunt.file.readYAML`兩個方法分別用于引入JSON和YAML數據。
~~~
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
uglify: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
},
dist: {
src: 'src/<%= pkg.name %>.js',
dest: 'dist/<%= pkg.name %>.min.js'
}
}
});
~~~