Grunt 入门教程二:配置任务
2.1 Grunt 配置
任务配置都定义在GruntFile 文件中的 grunt.initConfig 方法中。这个配置一般都是一些以任务名命名的属性,但是也可以包含任意的数据。如果一个属性没有被任何任务用到,就会被忽略。
而且,因为这是 JavaScript 文件,任何合法的 JS 代码都可以写,而不是仅仅局限于 JSON。如果有必要,你甚至可以用代码动态生成配置。
- 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'],
- });
2.2 任务配置和任务目标
当一个任务运行时,Grunt 会自动寻找配置中的同名属性。可以通过自定义的“targets”属性来配置多个任务。在下面这个例子中,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.renameTask 重命名了,Grunt 会在config对象中寻找新的名字命名的属性。
2.3 默认配置
在一个任务配置里,可以通过 options 属性来覆盖默认配置。任务中的每一个目标也都可以有一个与之对应的 options 属性。目标中的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.
- },
- },
- });
2.4 文件
因为大部分的任务都是执行文件操作,所以Grunt对所要操作的文件有很强的抽象。有几种方式可以实现源文件到目标文件的映射,可以有多级的冗余和控制(不太好翻译)。任何一种多任务都可以理解下面的这些格式,所以你可以使用任何最合适的格式。
所有的文件的格式都支持”src“和”dest“,但是”简洁“和”文件数组“两种格式还支持如下一些附加属性:
- filter:过滤器,任意一个合法的 fs.stats 方法名(http://nodejs.org/docs/latest/api/fs.html#fs_class_fs_stats),或者任何一个接受 src 作为文件路径并返回 True 或者 False的方法都可以。
- nonull:非空,如果没有任何一个匹配被找到,那么就返回一个包含这个模式本身的列表。否则,返回一个空列表。这个选项可以结合grunt的 --verbose 参数来帮助调试文件路径问题。
- dot:可以让模式匹配一个以句点开头的文件名,即使模式没有显式地指定一个句点。
- matchBase:只匹配base路径,如果设置了这个参数,那么不会匹配子目录。比如 a?b 会匹配 /xyz/123/acb 但是不会匹配 /xyz/acb/123。
- expand:拓展,执行一个动态的 源文件到目标文件的映射,参见“Building the files object dynamically”。
- 其他所有的参数都会被当做配置参数传给底层库,参见 node-glob (https://github.com/isaacs/node-glob) 和 minimatch(https://github.com/isaacs/minimatch).
2.5 简洁格式
这种模式一般用在只读任务中,只需要一个 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',
- },
- },
- });
2.6 文件对象格式
这种格式可以在一个目标中配置多个 源文件到目标文件的映射。属性名是目标文件,属性值是源文件。对每个目标文件可以定义任意数量的源文件,但是不支持附加属性
- 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'],
- },
- },
- },
- });
2.7 文件数组格式
这种格式也可以在一个目标中配置多个 源文件到目标文件的映射。而且可以使用附加属性。
- 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'},
- ],
- },
- },
- });
2.8 过时的格式
下面这个是在多任务和多目标出现之前的一种过时的格式,这种配置的目标文件路径实际上是目标名,所以会导致 grunt task:target 这样的命令出错,不建议使用这种格式。
- grunt.initConfig({
- concat: {
- 'dest/a.js': ['src/aa.js', 'src/aaa.js'],
- 'dest/b.js': ['src/bb.js', 'src/bbb.js'],
- },
- });
2.9 自定义的过滤方法
filter 属性可以帮你定义很高级的文件细节。最简单的,可以直接使用一个 fs.Stats 方法名。下面这段代码只会匹配真实的文件。
- grunt.initConfig({
- clean: {
- foo: {
- src: ['tmp/**/*'],
- filter: 'isFile',
- },
- },
- });
或者你可以创建一个自己的方法,对于应该被匹配的文件返回 true,对于不该被匹配的文件返回false。比如下面这个例子只会匹配到空文件夹。
- grunt.initConfig({
- clean: {
- foo: {
- src: ['tmp/**/*'],
- filter: function(filepath) {
- return (grunt.file.isDir(filepath) && require('fs').readdirSync(filepath).length === 0);
- },
- },
- },
- });
2.10 globbing 模式(不太好翻译,类似正则式)
有时候很难把所有的源文件都一一列举出来,所以 Grunt 支持 通过 node-glob(https://github.com/isaacs/node-glob) 和 minimatch(https://github.com/isaacs/minimatch) 来进行文件名描述(又称 globbing)。
这不是一个很好理解的 globbing 模式教程,在一个文件路径中:
- "*" 匹配任意字符,除了 "/"
- "?" 匹配按个字符,除了"/"
- "**" 匹配任意字符,包括"/"
- "{}" 一个以 逗号分隔的 “或”逻辑
- "!" 在模式的开头表示否定。
大家都知道,foo/*.js 会匹配 foo/ 目录下以 .js 结尾的任何文件(但是不包括子目录),而 foo/**/*.js 会匹配foo以及其子目录下的任何以 .js 结尾的文件。
并且,为了简化复杂的globbing 模式,Grunt 允许在一个列表中写多个 globbing 模式。这些模式会按顺序被处理。以 "!" 开头的会被排除。
- // 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(https://github.com/isaacs/node-glob) 和 minimatch(https://github.com/isaacs/minimatch)
2.11 动态创建文件
如果你想处理大量的单个文件,可以用一些附加属性来帮助动态创建文件。这些属性在“简洁格式“和”文件数组格式“下都可用。
- expand:设为 true 来启用下面这些属性。
- cwd:所有的 src 都相对于此路径(但是不包含)。
- src:需要匹配的模式,相对于cwd。
- dest:目标文件。
- ext:在dest中的所有文件后缀都替换掉。
- flatten:在dest中的所有路径的片段都替换掉。
- rename:每当匹配到一个src时,都会调用此方法(在ext和flatten执行之后)。dest和src属性会被当参数传入,这个函数必须返回一个新的dest值。如果相同的dest被返回超过一次,每一个用它的src都会被添加到一个源数组。
再下面这个例子中,名为 minify 的任务static_mapping和 dynamic_mapping 执行的完全相同的任务。
- grunt.initConfig({
- minify: {
- 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 "minify" 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.
- },
- ],
- },
- },
- });
任何静态和动态的方式都可以结合使用。
2.12 模板
通过 <% %> 来定义模板,任务执行时会自动把从配置中读取数据并拓展掉模板。模板会被递归拓展,直到没有任何模板为止。
整个config对象就是使用模板的上下文。并且,Grunt 和 它的方法都可以在模板中使用,比如 <% grunt.template.today("yyy-mm-dd") %>。
- <%= xx %> 以config对象为上下文解析出 xx 的值,类型无关。字符串、数组或者对象都可。
- <% %> 执行任意的js代码,可以用来进行流程控制或者执行循环等。
下面这个例子是一个concat任务,执行 grunt concat:sample 会把所有符合 foo/*.js+bar/*.js+baz/*.js的文件连接起来,再加上 /* abcde */ 的baner,最后生成 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'
- },
- },
- // Arbitrary properties used in task configuration templates.
- foo: 'c',
- bar: 'b<%= foo %>d', // 'bcd'
- baz: 'a<%= bar %>e', // 'abcde'
- qux: ['foo/*.js', 'bar/*.js'],
- });
2.13 导入外部数据
在下面这个例子中,项目的元数据是通过 package.json 文件导入的。uglify(http://github.com/gruntjs/grunt-contrib-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'
- }
- }
- });