設定任務
本指南說明如何使用 Gruntfile
為專案設定任務。如果您不知道 Gruntfile
是什麼,請閱讀 入門 指南,並查看 範例 Gruntfile。
Grunt 設定
任務設定在 Gruntfile
中透過 grunt.initConfig
方法指定。此設定大部分會在以任務為名的屬性中,但可能包含任何任意資料。只要屬性不會與任務所需的屬性衝突,否則它們將會被忽略。
此外,由於這是 JavaScript,您不限於 JSON;您可以在這裡使用任何有效的 JavaScript。必要時,您甚至可以透過程式產生設定。
grunt.initConfig({
concat: {
// concat task configuration goes here.
},
uglify: {
// uglify task configuration goes here.
},
// Arbitrary non-task-specific properties.
my_property: 'whatever',
my_src_files: ['foo/*.js', 'bar/*.js'],
});
任務設定和目標
當任務執行時,Grunt 會在同名屬性中尋找其設定。多重任務可以有多個設定,使用任意命名的「目標」定義。在以下範例中,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.
},
},
});
同時指定任務和目標,例如 grunt concat:foo
或 grunt concat:bar
,將只處理指定的目標設定,而執行 grunt concat
則會逐一處理所有目標。請注意,如果任務已使用 grunt.task.renameTask 重新命名,Grunt 將會在設定物件中尋找新的任務名稱屬性。
選項
在任務設定中,可以指定 options
屬性來覆寫內建預設值。此外,每個目標都可以有特定於該目標的 options
屬性。目標層級的選項將會覆寫任務層級的選項。
options
物件是選用的,如果不需要,可以省略。
grunt.initConfig({
concat: {
options: {
// Task-level options may go here, overriding task defaults.
},
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(來源-目的地)檔案對應,提供不同程度的詳細資料和控制。任何多重工作任務都將了解以下所有格式,因此請選擇最符合您需求的格式。
所有檔案格式都支援 src
和 dest
,但 精簡 和 檔案陣列 格式支援一些額外屬性
filter
有效的 fs.Stats 方法名稱 或傳遞已配對src
檔案路徑並傳回true
或false
的函數。 請參閱範例nonull
如果設定為true
,則操作將包含不符合的模式。結合 Grunt 的--verbose
旗標,此選項可協助除錯檔案路徑問題。dot
允許模式與以句點開頭的檔名相符,即使模式在該位置並未明確包含句點。matchBase
如果設定,則不含斜線的模式將與路徑的基礎檔名相符(如果路徑包含斜線)。例如,a?b
將與路徑/xyz/123/acb
相符,但與/xyz/acb/123
不相符。expand
處理動態 src-dest 檔案對應,請參閱 "動態建立檔案物件" 以取得更多資訊。- 其他屬性將傳遞到基礎函式庫中,作為配對選項。請參閱 node-glob 和 minimatch 文件,以取得更多選項。
Grunt 和工作任務選項之間的差異
大多數工作任務都執行檔案操作,因此 Grunt 提供內建基礎架構,以擷取工作任務應處理的檔案。優點是工作任務作者不必再次實作此邏輯。為了讓使用者指定這些檔案,Grunt 提供 nonull
和 filter
等選項。
除了要處理的檔案之外,每個工作任務都有其特定需求。工作任務作者可能希望允許其使用者設定一些選項,以覆寫預設行為。這些工作任務特定選項不應與前面說明的 Grunt 選項混淆。
為了進一步釐清此差異,讓我們來看一個使用 grunt-contrib-jshint 的範例
grunt.initConfig({
jshint: {
ignore_warning: {
options: {
'-W015': true,
},
src: 'js/**',
filter: 'isFile'
}
}
});
此設定使用 Grunt 選項 src
和 filter
來指定要處理的檔案。它也使用 grunt-contrib-jshint 工作任務特定選項 -W015
來忽略特定警告(代碼為 W015
的警告)。
精簡格式
此格式允許每個目標有一個 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'},
],
},
},
});
舊格式
dest-as-target 檔案格式是多任務和目標出現之前的遺留物,其中目的地檔案路徑實際上是目標名稱。不幸的是,由於目標名稱是檔案路徑,執行 grunt task:target
可能很尷尬。此外,您不能為每個 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 方法名稱。以下內容將僅在模式與實際檔案相符時進行清理
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);
},
},
},
});
另一個範例,它利用 glob 和 expand: true 功能,讓您避免覆寫已存在於目的地中的檔案
grunt.initConfig({
copy: {
templates: {
files: [{
expand: true,
cwd: ['templates/css/'], // Parent folder of original CSS templates
src: '**/*.css', // Collects all `*.css` files within the parent folder (and its subfolders)
dest: 'src/css/', // Stores the collected `*.css` files in your `src/css/` folder
filter: function (dest) { // `dest`, in this instance, is the filepath of each matched `src`
var cwd = this.cwd, // Configures variables (these are documented for your convenience only)
src = dest.replace(new RegExp('^' + cwd), '');
dest = grunt.task.current.data.files[0].dest;
return (!grunt.file.exists(dest + src)); // Copies `src` files ONLY if their destinations are unoccupied
}
}]
}
}
});
請記住,上述技術在檢查目的地是否存在時不考慮 rename 屬性。
Glob 模式
逐一指定所有來源檔案路徑通常不切實際,因此 Grunt 支援透過內建的 node-glob 和 minimatch 函式庫進行檔名擴充 (也稱為 glob)。
雖然這不是 glob 模式的完整教學,但請知道在檔案路徑中
*
匹配任意數量的字元,但不匹配/
?
匹配單一字元,但不匹配/
**
匹配任意數量的字元,包括/
,只要它是路徑部分中唯一的內容即可{}
允許使用逗號分隔的「或」表達式清單- 模式開頭的
!
會否定比對結果
大多數人只需要知道 foo/*.js
會比對 foo/
子目錄中所有以 .js
結尾的檔案,而 foo/**/*.js
會比對 foo/
子目錄及其所有子目錄中所有以 .js
結尾的檔案。
此外,為了簡化原本複雜的 glob 比對模式,Grunt 允許指定檔案路徑或 glob 比對模式陣列。模式會依序處理,以 !
為字首的比對會將比對到的檔案從結果集中排除。結果集會唯一化。
例如
// You can specify single files:
{src: 'foo/this.js', dest: ...}
// Or arrays of files:
{src: ['foo/this.js', 'foo/that.js', 'foo/the-other.js'], dest: ...}
// Or you can generalize with a glob pattern:
{src: 'foo/th*.js', dest: ...}
// This single node-glob pattern:
{src: 'foo/{a,b}*.js', dest: ...}
// Could also be written like this:
{src: ['foo/a*.js', 'foo/b*.js'], dest: ...}
// All .js files, in foo/, in alpha order:
{src: ['foo/*.js'], dest: ...}
// Here, bar.js is first, followed by the remaining files, in alpha order:
{src: ['foo/bar.js', 'foo/*.js'], dest: ...}
// All files except for bar.js, in alpha order:
{src: ['foo/*.js', '!foo/bar.js'], dest: ...}
// All files in alpha order, but with bar.js at the end.
{src: ['foo/*.js', '!foo/bar.js', 'foo/bar.js'], dest: ...}
// Templates may be used in filepaths or glob patterns:
{src: ['src/<%= basename %>.js'], dest: 'build/<%= basename %>.min.js'}
// But they may also reference file lists defined elsewhere in the config:
{src: ['foo/*.js', '<%= jshint.all.src %>'], dest: ...}
有關 glob 模式語法的更多資訊,請參閱 node-glob 和 minimatch 文件。
動態建立 files 物件
當您要處理許多個別檔案時,可以使用幾個額外屬性來動態建立檔案清單。這些屬性可以在 精簡 和 檔案陣列 對應格式中指定。
expand
設定為 true
會啟用下列屬性
cwd
所有src
比對都相對於此路徑(但不包含此路徑)。src
相對於cwd
的比對模式。dest
目標路徑字首。ext
在產生的dest
路徑中,用此值取代任何現有副檔名。extDot
用來指出表示副檔名的句點位於何處。可以採用'first'
(副檔名從檔名中的第一個句點開始)或'last'
(副檔名從最後一個句點開始),預設設定為'first'
[在 0.4.3 中新增]flatten
從產生的dest
路徑中移除所有路徑部分。rename
嵌入自訂函式,此函式會傳回包含新目標和檔名的字串。此函式會針對每個比對到的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
},
],
},
},
});
rename 屬性
rename
屬性是唯一的,因為它唯一有效的值是 JavaScript 函數。儘管函數會傳回字串,但您不能僅將字串用作 rename
的值(這麼做會產生錯誤:物件 # 的 'rename' 屬性不是函數
)。在以下範例中,copy
任務會建立 README.md 的備份。
grunt.initConfig({
copy: {
backup: {
files: [{
expand: true,
src: ['docs/README.md'], // The README.md file has been specified for backup
rename: function () { // The value for rename must be a function
return 'docs/BACKUP.txt'; // The function must return a string with the complete destination
}
}]
}
}
});
當函數被呼叫時,會傳入 dest
和匹配的 src
路徑,可用於傳回輸出字串。在以下範例中,檔案會從 dev
資料夾複製到 dist
資料夾,並重新命名為移除「beta」字樣的檔案。
grunt.initConfig({
copy: {
production: {
files: [{
expand: true,
cwd: 'dev/',
src: ['*'],
dest: 'dist/',
rename: function (dest, src) { // The `dest` and `src` values can be passed into the function
return dest + src.replace('beta',''); // The `src` is being renamed; the `dest` remains the same
}
}]
}
}
});
如果多個匹配的 src
路徑重新命名為相同的目的地(也就是說,如果兩個不同的檔案重新命名為同一個檔案),每個輸出都會新增到它的來源陣列中。
範本
使用 <% %>
分隔符指定的範本會在任務從設定檔中讀取它們時自動展開。範本會遞迴展開,直到沒有更多範本為止。
整個設定檔物件是解析屬性的內容。此外,grunt
及其方法在範本中可用,例如 <%= grunt.template.today('yyyy-mm-dd') %>
。
<%= prop.subprop %>
展開到設定檔中prop.subprop
的值,不論類型為何。像這樣的範本可用於參照不只是字串值,還有陣列或其他物件。<% %>
執行任意的內嵌 JavaScript 程式碼。這對於控制流程或迴圈很有用。
給定以下範例 concat
任務設定,執行 grunt concat:sample
會產生一個名為 build/abcde.js
的檔案,方法是將標語 /* abcde */
與所有符合 foo/*.js
+ bar/*.js
+ baz/*.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'
},
},
// Arbitrary properties used in task configuration templates.
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
任務會設定為縮小來源檔案並使用該元資料動態產生標語註解。
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'
}
}
});