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 的任务配置非常灵活,支持多种文件处理和选项配置方式。通过理解这些配置技术,您可以创建强大且高效的构建流程。