Configuring tasks
配置任务
本指南解释了如何使用 Gruntfile
为您的项目配置任务。如果您不知道什么是 Gruntfile
,请阅读 新手入门 指南并查看 示例 Gruntfile。
Grunt 配置
任务配置是通过 grunt.initConfig
方法在您的 Gruntfile
中指定的。这个配置主要位于任务名称属性下,但可以包含任何任意数据。只要属性不与任务所需的属性冲突,它们就会被忽略。
另外,因为这是 JavaScript,您不仅限于 JSON;您可以在此处使用任何有效的 JavaScript。如果需要,您甚至可以以编程方式生成配置。
grunt.initConfig({
concat: {
// concat 任务配置在这里。
},
uglify: {
// uglify 任务配置在这里。
},
// 任意非任务特定属性
my_property: 'whatever',
my_src_files: ['foo/*.js', 'bar/*.js'],
});
任务配置和目标
当运行一个任务时,Grunt 会在同名属性下查找其配置。多任务可以有多个配置,使用任意命名的"目标"。在下面的示例中,concat
任务有 foo
和 bar
目标,而 uglify
任务只有一个 bar
目标。
grunt.initConfig({
concat: {
foo: {
// concat 任务 "foo" 目标的选项和文件在这里。
},
bar: {
// concat 任务 "bar" 目标的选项和文件在这里。
},
},
uglify: {
bar: {
// uglify 任务 "bar" 目标的选项和文件在这里。
},
},
});
指定任务和目标,如 grunt concat:foo
或 grunt concat:bar
将只处理指定目标的配置,而运行 grunt concat
将遍历所有目标,依次处理每个目标。注意,如果任务已使用 grunt.task.renameTask 重命名,Grunt 将在配置对象中查找新任务名称的属性。
选项
在任务配置中,可以指定 options
属性以覆盖内置默认值。此外,每个目标可以有特定于该目标的 options
属性。目标级别的选项将覆盖任务级别的选项。
options
对象是可选的,如果不需要可以省略。
grunt.initConfig({
concat: {
options: {
// 任务级别选项可以在这里,覆盖任务默认值。
},
foo: {
options: {
// "foo" 目标选项可以在这里,覆盖任务级别选项。
},
},
bar: {
// 未指定选项;此目标将使用任务级别选项。
},
},
});
文件
因为大多数任务执行文件操作,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'},
],
},
},
});
旧格式
目标作为目标文件格式是在多任务和目标存在之前的遗留格式,其中目标文件路径实际上是目标名称。不幸的是,因为目标名称是文件路径,运行 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);
},
},
},
});
另一个示例——利用通配符和 expand: true 功能——允许您避免覆盖已存在于目标中的文件:
grunt.initConfig({
copy: {
templates: {
files: [{
expand: true,
cwd: ['templates/css/'], // 原始 CSS 模板的父文件夹
src: '**/*.css', // 收集父文件夹中的所有 `*.css` 文件(及其子文件夹)
dest: 'src/css/', // 将收集的 `*.css` 文件存储在您的 `src/css/` 文件夹中
filter: function (dest) { // 在这个实例中,`dest` 是每个匹配的 `src` 的文件路径
var cwd = this.cwd, // 配置变量(仅为您的方便记录)
src = dest.replace(new RegExp('^' + cwd), '');
dest = grunt.task.current.data.files[0].dest;
return (!grunt.file.exists(dest + src)); // 仅在目标未被占用时复制 `src` 文件
}
}]
}
}
});
请注意,上述技术在检查目标是否存在时不考虑 重命名属性。
通配符模式
单独指定所有源文件路径通常是不切实际的,因此 Grunt 通过内置的 node-glob 和 minimatch 库支持文件名扩展(也称为通配符)。
通配符模式的工作方式类似于 shell 中的文件名扩展。通配符模式可以包含以下特殊字符:
*
匹配任意数量的字符,但不匹配/
?
匹配单个字符,不包括/
**
匹配任意数量的字符,包括/
,只要模式中的其他部分匹配{}
允许使用逗号分隔的"或"表达式,例如{a,b}
匹配a
或b
!
在模式开头表示否定模式,如果文件匹配后面的模式,则不会被包括
以下是一些示例:
// 匹配所有在 `js/` 目录中的 `.js` 文件
'js/**/*.js'
// 匹配 `js/` 目录中的所有 `.js` 文件,但不包括子目录
'js/*.js'
// 匹配 `js/` 目录中的所有 `.js` 文件,包括子目录,但不包括 `js/vendor/` 目录
['js/**/*.js', '!js/vendor/**']
动态构建文件对象
当您需要更多控制文件映射时,可以使用 expand
选项动态生成文件对象。
基本示例
grunt.initConfig({
concat: {
dist: {
expand: true, // 启用动态扩展
cwd: 'src/', // 源文件的相对路径
src: ['**/*.js'], // 匹配所有子目录中的 .js 文件
dest: 'dist/', // 目标路径前缀
ext: '.min.js', // 目标文件扩展名
extDot: 'first' // 扩展名添加在文件名的第一个点之前
}
}
});
高级示例
grunt.initConfig({
rename: {
dist: {
expand: true,
cwd: 'src/',
src: ['**/*.js'],
dest: 'dist/',
rename: function(dest, src) {
return dest + src.replace('.js', '.min.js');
}
}
}
});
模板
Grunt 允许在配置中使用模板。这些模板可以引用其他配置属性,并且可以使用 <% %>
语法。
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
concat: {
dist: {
src: ['src/<%= pkg.name %>.js'],
dest: 'dist/<%= pkg.name %>.js'
}
}
});
自定义任务选项
每个任务都可以有自己的选项。这些选项可以在任务级别或目标级别指定。
grunt.initConfig({
uglify: {
options: {
banner: '/* <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
},
dist: {
options: {
compress: true
},
files: {
'dist/app.min.js': ['src/app.js']
}
}
}
});
总结
Grunt 的任务配置非常灵活,支持多种文件处理和选项配置方式。通过理解这些配置技术,您可以创建强大且高效的构建流程。