使用命令行工具
最后更新于:2022-04-01 04:15:27
# 使用命令行工具
## 安装命令行工具
执行 `sudo npm install -g grunt-cli` 。
`grunt`命令行接口提供了一系列选项。你可以在你的终端中使用`grunt -h`查看这个选项。
### --help, -h
显示帮助信息。
### --base, -b
指定一个基本路径。默认情况下,所有文件路径都是相对于`Gruntfile`的。
还可以调用 `grunt.file.setBase(...)`
### --no-color
禁用彩色输出。
### --gruntfile
指定 `Gruntfile` 文件。
默认情况下,grunt会从当前目录或者父目录中寻找最近的`Gruntfile.js`或者`Gruntfile.coffee`文件。
### --debug, -d
对支持调试的任务启用调试模式。
### --stack
因警告或者致命错误退出时打印堆栈跟踪信息。
### --force, -f
一种强制跳过警告信息的方式。
如果像从警告中得到提示,就不要使用这个选项,可以根据提示信息修正代码。
### --tasks
指定一个包含可加载的任务和“额外”文件的目录。
还可以调用 `grunt.loadTasks(...)`
### --npm
在通过npm安装的插件中检查可加载的任何以及额外文件。
还可以调用 `grunt.loadNpmTasks(...)`
### --no-write
禁用写文件操作(可用于演示)。
### --verbose, -v
冗长模式(Verbose mode)。会输出很多的信息。
### --version, -V
打印grunt版本。结合 --verbose 一起使用可以获取更多信息。
### --completion
输出shell的自动补全规则。更多信息参考grunt-cli相关的文档。
创建插件
最后更新于:2022-04-01 04:15:25
# 创建插件
1. 通过 `npm install -g grunt-init` 命令安装 [grunt-init](https://github.com/gruntjs/grunt-init) 。
2. 通过
~~~
git clone git://github.com/gruntjs/grunt-init-gruntplugin.git ~/.grunt-init/gruntplugin
~~~
命令安装grunt插件模版。
3. 在一个空的目录中执行 `grunt-init gruntplugin` 。
4. 执行 `npm install` 命令以准备开发环境。
5. 为你的插件书写代码。
6. 执行 `npm publish` 命令将你创建的 Grunt 插件提发布npm!
## 注意
### 命名你的task
"grunt-contrib" 命名空间保留给 Grunt 团队维护的task使用,请给你自己的task起一个合适名字,并且避免使用被保留的命名空间。
### 调试
Grunt默认隐藏了error stack traces,但是可以通过 `--stack` 参数启用,方便你调试自己的task。如果你希望 Grunt 在出现错误时总是能记录下stack trace,可以在你的shell中创建一个命令别名(alias)。例如,在bash中,可以通过 `alias grunt='grunt --stack'` 命令创建一个别名。
### 存储任务文件
只在项目根目录中的 .grunt/[npm-module-name] 目录中存储数据文件,并在适当的时候将其清除。对于临时文件这并不是一个好的解决方案, 建议使用后面列出的几个常用npm模块(例如 [temporary](https://npmjs.org/package/temporary)、[tmp](https://npmjs.org/package/tmp))来调用操作系统级别的临时目录功能。
### 避免改变当前工作目录:`process.cwd()`
默认情况下,包含gruntfile文件的目录被设置为当前工作目录。用户可以在自己的gruntfile中通过执行`grunt.file.setBase()` 改变改变当前工作目录,但是插件不应该改变它。
`path.resolve('foo')` 可以被用来获取'foo' 相对于 `Gruntfile` 所在目录的绝对路径。
创建任务
最后更新于:2022-04-01 04:15:23
# 创建任务
任务是Grunt的面包和奶油。就像你常用的工具,如: `jshint` 或 `nodeunit`。每当运行Grunt时, 你可以为其指定一个或多个任务, 这些任务用于告诉Grunt你想要它做什么事情。
如果你没有指定一个任务,并且你已经定义一个名为 "default" 的任务,那么该任务将会默认被执行(不用诧异,总要做点儿什么啊!)。
## 任务别名
如果指定了一个任务列表,新任务将是这一个或多个指定任务的别名。当运行此 "任务别名" 时,在`taskList` 中指定的每个任务都会按照其出现的顺序依次执行。`taskList`参数必须时一个任务数组。
~~~
grunt.registerTask(taskName, [description, ] taskList)
~~~
下面的任务别名案例中定义了一个 'default' 任务,如果运行Grunt时没有指定任何任务,它将自动执行'jshint'、'qunit'、'concat' 和 'uglify' 任务。
~~~
grunt.registerTask('default', ['jshint', 'qunit', 'concat', 'uglify']);
~~~
还可以给任务指定参数。在下面的案例中,别名 "dist" 将执行 "concat" 和 "uglify" 两个任务,并且它们都带有一个 "dist" 参数:
~~~
grunt.registerTask('dist', ['concat:dist', 'uglify:dist']);
~~~
## 多任务
当运行一个多任务时,Grunt会自动从项目的配置对象中查找同名属性。多任务可以有多个配置,并且可以使用任意命名的'targets'。
同时指定像`grunt concat:foo`或者`grunt concat:bar`这样的任务和目标,在运行时Grunt只会处理指定目标的配置;然而如果运行`grunt concat`,将会遍历所有的目标, 并按任务指定的顺序处理每个目标。注意,如果一个任务已经使用[grunt.task.renameTask](http://www.gruntjs.net/grunt.task#grunt.task.renametask)重命名过,Grunt将会自动在配置对象中查找新任务名称属性。
大部分的contrib任务(主要是指官方提供的任务),包括[grunt-contrib-jshint插件的jshint任务](https://github.com/gruntjs/grunt-contrib-jshint#jshint-task),以及[grunt-contrib-concat插件的concat任务](https://github.com/gruntjs/grunt-contrib-concat#concat-task)都是多任务形式的。
~~~
grunt.registerMultiTask(taskName, [description, ] taskFunction)
~~~
对于指定的配置,这里有一个案例演示了如果通过`grunt log:foo`运行Grunt,它会输出`foo: 1,2,3`;如果通过`grunt log:bar`来运行Grunt, 它会输出`bar: hello world`。然而如果通过`grunt log`运行Grunt, 它会输出`foo: 1,2,3`,然后是`bar: hello world`,最后是`baz: false`(任务目标会按照指定的顺序进行处理)。
~~~
grunt.initConfig({
log: {
foo: [1, 2, 3],
bar: 'hello world',
baz: false
}
});
grunt.registerMultiTask('log', 'Log stuff.', function() {
grunt.log.writeln(this.target + ': ' + this.data);
});
~~~
## "基本" 任务
当一个基本任务执行时,Grunt并不会检查配置和环境 -- 它仅仅执行指定的任务函数,并传递任何使用冒号分割的参数作为函数的参数。
~~~
grunt.registerTask(taskName, [description, ] taskFunction)
~~~
下面的案例中,如果执行 `grunt foo:testing:123`,将输出日志 `foo, testing 123`。 如果执行这个任务时不传递参数,只是执行 `grunt foo`,那么将输出日志 `foo, no args`。
~~~
grunt.registerTask('foo', 'A sample task that logs stuff.', function(arg1, arg2) {
if (arguments.length === 0) {
grunt.log.writeln(this.name + ", no args");
} else {
grunt.log.writeln(this.name + ", " + arg1 + " " + arg2);
}
});
~~~
## 自定义任务
你可以和任务一起疯狂。如果你的任务并没有遵循 "多任务" 结构,那就使用自定义任务。
~~~
grunt.registerTask('default', 'My "default" task description.', function() {
grunt.log.writeln('Currently running the "default" task.');
});
~~~
在一个任务内部,你可以执行其他的任务。
~~~
grunt.registerTask('foo', 'My "foo" task.', function() {
// Enqueue "bar" and "baz" tasks, to run after "foo" finishes, in-order.
grunt.task.run('bar', 'baz');
// Or:
grunt.task.run(['bar', 'baz']);
});
~~~
任务也可以是异步的。
~~~
grunt.registerTask('asyncfoo', 'My "asyncfoo" task.', function() {
// Force task into async mode and grab a handle to the "done" function.
var done = this.async();
// Run some sync stuff.
grunt.log.writeln('Processing task...');
// And some async stuff.
setTimeout(function() {
grunt.log.writeln('All done!');
done();
}, 1000);
});
~~~
任务也可以访问它们自身名称和参数。
~~~
grunt.registerTask('foo', 'My "foo" task.', function(a, b) {
grunt.log.writeln(this.name, a, b);
});
// 用法:
// grunt foo foo:bar
// logs: "foo", undefined, undefined
// logs: "foo", "bar", undefined
// grunt foo:bar:baz
// logs: "foo", "bar", "baz"
~~~
如果记录到任何错误,那么任务就会失败。
~~~
grunt.registerTask('foo', 'My "foo" task.', function() {
if (failureOfSomeKind) {
grunt.log.error('This is an error message.');
}
// 如果这个任务出现错误则返回false
if (ifErrors) { return false; }
grunt.log.writeln('This is the success message');
});
~~~
当任务失败时,所有后续任务都将终止,除非指定 `--force` 。
~~~
grunt.registerTask('foo', 'My "foo" task.', function() {
// Fail synchronously.
return false;
});
grunt.registerTask('bar', 'My "bar" task.', function() {
var done = this.async();
setTimeout(function() {
// Fail asynchronously.
done(false);
}, 1000);
});
~~~
任务还可以依赖于其他任务的成功执行。注意 `grunt.task.requires` 并不会真正的运行其他任务,它仅仅检查其它任务是否已经执行,并且没有失败。
~~~
grunt.registerTask('foo', 'My "foo" task.', function() {
return false;
});
grunt.registerTask('bar', 'My "bar" task.', function() {
// 如果"foo"任务运行失败或者没有运行则任务失败。
grunt.task.requires('foo');
// 如果"foo"任务运行成功则执行这里的代码。
grunt.log.writeln('Hello, world.');
});
// 用法:
// grunt foo bar
// 没有输出,因为"foo"失败。
// grunt bar
// 没有输出,因为"foo"从未运行。
~~~
如果任务需要的配置属性不存在,其也可能失败。
~~~
grunt.registerTask('foo', 'My "foo" task.', function() {
// Fail task if "meta.name" config prop is missing
// Format 1: String
grunt.config.requires('meta.name');
// or Format 2: Array
grunt.config.requires(['meta', 'name']);
// Log... conditionally.
grunt.log.writeln('This will only log if meta.name is defined in the config.');
});
~~~
任务还可以访问配置属性。
~~~
grunt.registerTask('foo', 'My "foo" task.', function() {
// 记录属性值,如果属性未定义(undefined)则返回null。
grunt.log.writeln('The meta.name property is: ' + grunt.config('meta.name'));
// 同样的记录属性值,如果属性未定义(undefined)则返回null。
grunt.log.writeln('The meta.name property is: ' + grunt.config(['meta', 'name']));
});
~~~
在 [contrib tasks](https://github.com/gruntjs/) 中可以查看更多案例。
## CLI 参数 / 环境
通过 `process.env` 来访问[环境变量](http://en.wikipedia.org/wiki/Environment_variable)。
请参考 [使用命令行工具](http://using-the-cli/)章节,查看完整的的命令行选项列表。
## 为什么我的异步task没有完成?
如果发生这种情况,可能是由于你忘记调用 [this.async](http://www.gruntjs.net/inside-tasks#this.async) 方法来告诉Grunt你的任务是异步的。为了简单起见,Grunt使用同步的编码风格,可以在task体中通过调用 `this.async()` 将其转换为异步的。
注意,传递 `false` 给 `done()` 函数就会告诉Grunt你的任务已经失败。
例如:
~~~
grunt.registerTask('asyncme', 'My asynchronous task.', function() {
var done = this.async();
doSomethingAsync(done);
});
~~~
## 额外参考资料
如果你需要更多参考资料来创建自己的 task ,请参考 [API](http://www.gruntjs.net/api) 文档 if you need extra reference to create your tasks.
Gruntfile 实例
最后更新于:2022-04-01 04:15:20
# Gruntfile 实例
下面就针对一个 `Gruntfile` 案例做简单分析,也可以作为一个实例使用:
~~~
module.exports = function(grunt) {
grunt.initConfig({
jshint: {
files: ['Gruntfile.js', 'src/**/*.js', 'test/**/*.js'],
options: {
globals: {
jQuery: true
}
}
},
watch: {
files: ['<%= jshint.files %>'],
tasks: ['jshint']
}
});
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.registerTask('default', ['jshint']);
};
~~~
在页面底部是这个 `Gruntfile` 实例的完整内容,如果你按顺序阅读本文的话,可以跟随我们一步步分析这个文件中的每一部分。我们会用到以下5个 Grunt 插件:
* [grunt-contrib-uglify](https://github.com/gruntjs/grunt-contrib-uglify)
* [grunt-contrib-qunit](https://github.com/gruntjs/grunt-contrib-qunit)
* [grunt-contrib-concat](https://github.com/gruntjs/grunt-contrib-concat)
* [grunt-contrib-jshint](https://github.com/gruntjs/grunt-contrib-jshint)
* [grunt-contrib-watch](https://github.com/gruntjs/grunt-contrib-watch)
第一部分是"wrapper" 函数,它包含了整个Grunt配置信息。
~~~
module.exports = function(grunt) {
}
~~~
在这个函数中,我们可以初始化 configuration 对象:
~~~
grunt.initConfig({
});
~~~
接下来可以从`package.json` 文件读入项目配置信息,并存入`pkg` 属性内。这样就可以让我们访问到`package.json`文件中列出的属性了,如下:
~~~
pkg: grunt.file.readJSON('package.json')
~~~
到目前为止我们就可以看到如下配置:
~~~
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json')
});
};
~~~
现在我们就可以为我们的每个任务来定义相应的配置(逐个定义我们为项目定义的任务配置),然后每个任务的配置对象作为Grunt配置对象(即grunt.initConfig({})所接受的配置对象)的属性,并且这个属性名称与任务名相同。因此"concat"任务就是我们的配置对象中的"concat"键(属性)。下面便是我的"concat"任务的配置对象。
~~~
concat: {
options: {
// 定义一个用于插入合并输出文件之间的字符
separator: ';'
},
dist: {
// 将要被合并的文件
src: ['src/**/*.js'],
// 合并后的JS文件的存放位置
dest: 'dist/<%= pkg.name %>.js'
}
}
~~~
注意我是如何引用JSON文件(也就是我们在配置对象顶部引入的配置文件)中的`name`属性的。这里我使用`pkg.name`来访问我们刚才引入并存储在`pkg`属性中的`package.json`文件信息,它会被解析为一个JavaScript对象。Grunt自带的有一个简单的模板引擎用于输出配置对象(这里是指`package.json`中的配置对象)属性值,这里我让`concat`任务将所有存在于`src/`目录下以`.js`结尾的文件合并起来,然后存储在`dist`目录中,并以项目名来命名。
现在我们来配置uglify插件,它的作用是压缩(minify)JavaScript文件:
~~~
uglify: {
options: {
// 此处定义的banner注释将插入到输出文件的顶部
banner: '/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> */\n'
},
dist: {
files: {
'dist/<%= pkg.name %>.min.js': ['<%= concat.dist.dest %>']
}
}
}
~~~
这里我们让`uglify`在`dist/`目录中创建了一个包含压缩结果的JavaScript文件。注意这里我使用了`<%= concat.dist.dest>`,因此uglify会自动压缩concat任务中生成的文件。
QUnit插件的设置非常简单。 你只需要给它提供用于测试运行的文件的位置,注意这里的QUnit是运行在HTML文件上的。
~~~
qunit: {
files: ['test/**/*.html']
},
~~~
JSHint插件的配置也很简单:
~~~
jshint: {
// define the files to lint
files: ['gruntfile.js', 'src/**/*.js', 'test/**/*.js'],
// configure JSHint (documented at http://www.jshint.com/docs/)
options: {
// more options here if you want to override JSHint defaults
globals: {
jQuery: true,
console: true,
module: true
}
}
}
~~~
JSHint只需要一个文件数组(也就是你需要检测的文件数组), 然后是一个`options`对象(这个对象用于重写JSHint提供的默认检测规则)。你可以到[JSHint官方文档](http://www.jshint.com/docs/)站点中查看完整的文档。如果你乐于使用JSHint提供的默认配置,那么在Gruntfile中就不需要重新定义它们了.
最后,我们来看看watch插件:
~~~
watch: {
files: ['<%= jshint.files %>'],
tasks: ['jshint', 'qunit']
}
~~~
你可以在命令行使用`grunt watch`来运行这个任务。当它检测到任何你所指定的文件(在这里我使用了JSHint任务中需要检测的相同的文件)发生变化时,它就会按照你所指定的顺序执行指定的任务(在这里我指定了jshint和qunit任务)。
最后, 我们还要加载所需要的Grunt插件。 它们应该已经全部通过npm安装好了。
~~~
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-qunit');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-concat');
~~~
最后设置了一些task。最重要的是default任务:
~~~
// 在命令行上输入"grunt test",test task就会被执行。
grunt.registerTask('test', ['jshint', 'qunit']);
// 只需在命令行上输入"grunt",就会执行default task
grunt.registerTask('default', ['jshint', 'qunit', 'concat', 'uglify']);
~~~
下面便是最终完成的 `Gruntfile` 文件:
~~~
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
concat: {
options: {
separator: ';'
},
dist: {
src: ['src/**/*.js'],
dest: 'dist/<%= pkg.name %>.js'
}
},
uglify: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> */\n'
},
dist: {
files: {
'dist/<%= pkg.name %>.min.js': ['<%= concat.dist.dest %>']
}
}
},
qunit: {
files: ['test/**/*.html']
},
jshint: {
files: ['Gruntfile.js', 'src/**/*.js', 'test/**/*.js'],
options: {
//这里是覆盖JSHint默认配置的选项
globals: {
jQuery: true,
console: true,
module: true,
document: true
}
}
},
watch: {
files: ['<%= jshint.files %>'],
tasks: ['jshint', 'qunit']
}
});
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-qunit');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.registerTask('test', ['jshint', 'qunit']);
grunt.registerTask('default', ['jshint', 'qunit', 'concat', 'uglify']);
};
~~~
配置任务
最后更新于:2022-04-01 04:15:18
# 配置任务
这个指南解释了如何使用 `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'
}
}
});
~~~
快速入门
最后更新于:2022-04-01 04:15:16
# 快速入门
Grunt和 Grunt 插件是通过 [npm](https://npmjs.org/) 安装并管理的,npm是 [Node.js](http://nodejs.org/) 的包管理器。
*Grunt 0.4.x 必须配合Node.js `>= 0.8.0`版本使用。;奇数版本号的 Node.js 被认为是不稳定的开发版。*
在安装 Grunt 前,请确保当前环境中所安装的 [npm](https://npmjs.org/) 已经是最新版本,执行 `npm update -g npm` 指令进行升级(在某些系统中可能需要 `sudo` 指令)。
如果你已经安装了 Grunt,现在需要参考一些文档手册,那就请看一看 [`Gruntfile` 实例](http://www.gruntjs.net/sample-gruntfile) 和如何 [配置任务](http://www.gruntjs.net/configuring-tasks)吧。
## 安装 CLI
**还在使用 Grunt 0.3 版本吗?请查看 [Grunt 0.3 注意事项](http://www.gruntjs.net/upgrading-from-0.3-to-0.4#grunt-0.3-notes)**
在继续学习前,你需要先将Grunt命令行(CLI)安装到全局环境中。安装时可能需要使用sudo(针对OSX、*nix、BSD等系统中)权限或者作为管理员(对于Windows环境)来执行以下命令。
~~~
npm install -g grunt-cli
~~~
上述命令执行完后,`grunt` 命令就被加入到你的系统路径中了,以后就可以在任何目录下执行此命令了。
注意,安装`grunt-cli`并不等于安装了 Grunt!Grunt CLI的任务很简单:调用与`Gruntfile`在同一目录中 Grunt。这样带来的好处是,允许你在同一个系统上同时安装多个版本的 Grunt。
这样就能让多个版本的 Grunt 同时安装在同一台机器上。
## CLI 是如何工作的
每次运行`grunt` 时,他就利用node提供的`require()`系统查找本地安装的 Grunt。正是由于这一机制,你可以在项目的任意子目录中运行`grunt` 。
如果找到一份本地安装的 Grunt,CLI就将其加载,并传递`Gruntfile`中的配置信息,然后执行你所指定的任务。为了更好的理解 Grunt CLI的执行原理,请[阅读源码](https://github.com/gruntjs/grunt-cli/blob/master/bin/grunt)。
## 拿一份现有的 Grunt 项目练手
假定Grunt CLI已经正确安装,并且已经有一份配置好`package.json` 和 `Gruntfile` 文件的项目了,接下来就很容易拿Grunt练手了:
1. 将命令行的当前目录转到项目的根目录下。
2. 执行`npm install`命令安装项目依赖的库。
3. 执行 `grunt` 命令。
OK,就是这么简单。还可以通过`grunt --help` 命令列出所有已安装的Grunt任务(task),但是一般更建议去查看项目的文档以获取帮助信息。
## 准备一份新的 Grunt 项目
一般需要在你的项目中添加两份文件:`package.json` 和 `Gruntfile`。
**package.json**: 此文件被[npm](https://npmjs.org/)用于存储项目的元数据,以便将此项目发布为npm模块。你可以在此文件中列出项目依赖的grunt和Grunt插件,放置于[devDependencies](https://docs.npmjs.com/files/package.json#devdependencies)配置段内。
**Gruntfile**: 此文件被命名为 `Gruntfile.js` 或 `Gruntfile.coffee`,用来配置或定义任务(task)并加载Grunt插件的。 **此文档中提到的 `Gruntfile` 其实说的是一个文件,文件名是 `Gruntfile.js` 或`Gruntfile.coffee`**。
## package.json
`package.json`应当放置于项目的根目录中,与`Gruntfile`在同一目录中,并且应该与项目的源代码一起被提交。在上述目录(`package.json`所在目录)中运行`npm install`将依据`package.json`文件中所列出的每个依赖来自动安装适当版本的依赖。
下面列出了几种为你的项目创建`package.json`文件的方式:
* 大部分 [grunt-init](http://www.gruntjs.net/project-scaffolding) 模版都会自动创建特定于项目的`package.json`文件。
* [npm init](https://docs.npmjs.com/cli/init)命令会创建一个基本的`package.json`文件。
* 复制下面的案例,并根据需要做扩充,参考此[说明](https://docs.npmjs.com/files/package.json).
~~~
{
"name": "my-project-name",
"version": "0.1.0",
"devDependencies": {
"grunt": "~0.4.5",
"grunt-contrib-jshint": "~0.10.0",
"grunt-contrib-nodeunit": "~0.4.1",
"grunt-contrib-uglify": "~0.5.0"
}
}
~~~
### 安装Grunt 和 grunt插件
向已经存在的`package.json` 文件中添加Grunt和grunt插件的最简单方式是通过`npm install <module> --save-dev`命令。此命令不光安装了`<module>`,还会自动将其添加到[devDependencies](https://docs.npmjs.com/files/package.json#devdependencies) 配置段中,遵循[tilde version range](https://npmjs.org/doc/misc/semver.html#Ranges)格式。
例如,下面这条命令将安装Grunt最新版本到项目目录中,并将其添加到devDependencies内:
~~~
npm install grunt --save-dev
~~~
同样,grunt插件和其它node模块都可以按相同的方式安装。下面展示的实例就是安装 JSHint 任务模块:
~~~
npm install grunt-contrib-jshint --save-dev
~~~
在 [Grunt 插件](http://www.gruntjs.net/plugins) 页面可以看到当前可用的 Grunt 插件,他们可以直接在项目中安装并使用。
安装插件之后,请务必确保将更新之后的 `package.json` 文件提交到项目仓库中。
## Gruntfile
`Gruntfile.js` 或 `Gruntfile.coffee` 文件是有效的 JavaScript 或 CoffeeScript 文件,应当放在你的项目根目录中,和`package.json`文件在同一目录层级,并和项目源码一起加入源码管理器。
Gruntfile由以下几部分构成:
* "wrapper" 函数
* 项目与任务配置
* 加载grunt插件和任务
* 自定义任务
### Gruntfile文件案例
在下面列出的这个 `Gruntfile` 中,`package.json`文件中的项目元数据(metadata)被导入到 Grunt 配置中, [grunt-contrib-uglify](http://github.com/gruntjs/grunt-contrib-uglify) 插件中的`uglify` 任务(task)被配置为压缩(minify)源码文件并依据上述元数据动态生成一个文件头注释。当在命令行中执行 `grunt` 命令时,`uglify` 任务将被默认执行。
~~~
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
uglify: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
},
build: {
src: 'src/<%= pkg.name %>.js',
dest: 'build/<%= pkg.name %>.min.js'
}
}
});
// 加载包含 "uglify" 任务的插件。
grunt.loadNpmTasks('grunt-contrib-uglify');
// 默认被执行的任务列表。
grunt.registerTask('default', ['uglify']);
};
~~~
前面已经向你展示了整个 `Gruntfile`,接下来将详细解释其中的每一部分。
### "wrapper" 函数
每一份 `Gruntfile` (和grunt插件)都遵循同样的格式,你所书写的Grunt代码必须放在此函数内:
~~~
module.exports = function(grunt) {
// Do grunt-related things in here
};
~~~
### 项目和任务配置
大部分的Grunt任务都依赖某些配置数据,这些数据被定义在一个object内,并传递给[grunt.initConfig](http://www.gruntjs.net/grunt#grunt.initconfig) 方法。
在下面的案例中,`grunt.file.readJSON('package.json')` 将存储在`package.json`文件中的JSON元数据引入到grunt config中。 由于`<% %>`模板字符串可以引用任意的配置属性,因此可以通过这种方式来指定诸如文件路径和文件列表类型的配置数据,从而减少一些重复的工作。
你可以在这个配置对象中(传递给initConfig()方法的对象)存储任意的数据,只要它不与你任务配置所需的属性冲突,否则会被忽略。此外,由于这本身就是JavaScript,你不仅限于使用JSON;你可以在这里使用任意的有效的JS代码。如果有必要,你甚至可以以编程的方式生成配置。
与大多数task一样,[grunt-contrib-uglify](http://github.com/gruntjs/grunt-contrib-uglify) 插件中的`uglify` 任务要求它的配置被指定在一个同名属性中。在这里有一个例子, 我们指定了一个`banner`选项(用于在文件顶部生成一个注释),紧接着是一个单一的名为`build`的uglify目标,用于将一个js文件压缩为一个目标文件。
~~~
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
uglify: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
},
build: {
src: 'src/<%= pkg.name %>.js',
dest: 'build/<%= pkg.name %>.min.js'
}
}
});
~~~
### 加载 Grunt 插件和任务
像 [concatenation](https://github.com/gruntjs/grunt-contrib-concat)、[minification]、[grunt-contrib-uglify](http://github.com/gruntjs/grunt-contrib-uglify) 和 [linting](https://github.com/gruntjs/grunt-contrib-jshint)这些常用的任务(task)都已经以[grunt插件](https://github.com/gruntjs)的形式被开发出来了。只要在 `package.json` 文件中被列为dependency(依赖)的包,并通过`npm install`安装之后,都可以在`Gruntfile`中以简单命令的形式使用:
~~~
// 加载能够提供"uglify"任务的插件。
grunt.loadNpmTasks('grunt-contrib-uglify');
~~~
**注意:** `grunt --help` 命令将列出所有可用的任务。
### 自定义任务
通过定义 `default` 任务,可以让Grunt默认执行一个或多个任务。在下面的这个案例中,执行 `grunt` 命令时如果不指定一个任务的话,将会执行`uglify`任务。这和执行`grunt uglify` 或者 `grunt default`的效果一样。`default`任务列表数组中可以指定任意数目的任务(可以带参数)。
~~~
// Default task(s).
grunt.registerTask('default', ['uglify']);
~~~
如果Grunt插件中的任务(task)不能满足你的项目需求,你还可以在`Gruntfile`中自定义任务(task)。例如,在下面的 `Gruntfile` 中自定义了一个`default` 任务,并且他甚至不依赖任务配置:
~~~
module.exports = function(grunt) {
// A very basic default task.
grunt.registerTask('default', 'Log some stuff.', function() {
grunt.log.write('Logging some stuff...').ok();
});
};
~~~
特定于项目的任务不必在 `Gruntfile` 中定义。他们可以定义在外部`.js` 文件中,并通过[grunt.loadTasks](http://www.gruntjs.net/grunt/#grunt.loadtasks)方法加载。
## 扩展阅读
* The [Installing grunt](http://www.gruntjs.net/installing-grunt/) guide has detailed information about installing specific, production or in-development, versions of Grunt and grunt-cli.
* The [Configuring Tasks](http://www.gruntjs.net/configuring-tasks/) guide has an in-depth explanation on how to configure tasks, targets, options and files inside the `Gruntfile`, along with an explanation of templates, globbing patterns and importing external data.
* The [Creating Tasks](http://www.gruntjs.net/creating-tasks/) guide lists the differences between the types of Grunt tasks and shows a number of sample tasks and configurations.
* For more information about writing custom tasks or Grunt plugins, check out the [developer documentation](http://www.gruntjs.net/grunt).
概述
最后更新于:2022-04-01 04:15:14
>[info] # Grunt -- JavaScript 世界的构建工具
## 为何要用构建工具?
一句话:自动化。对于需要反复重复的任务,例如压缩(minification)、编译、单元测试、linting等,自动化工具可以减轻你的劳动,简化你的工作。当你在 [Gruntfile](http://www.gruntjs.net/sample-gruntfile) 文件正确配置好了任务,任务运行器就会自动帮你或你的小组完成大部分无聊的工作。
## 为什么要使用Grunt?
Grunt生态系统非常庞大,并且一直在增长。由于拥有数量庞大的插件可供选择,因此,你可以利用Grunt自动完成任何事,并且花费最少的代价。如果找不到你所需要的插件,那就自己动手创造一个Grunt插件,然后将其发布到npm上吧。先看看[入门文档](71449)吧。
> 原文出处:http://www.gruntjs.net/getting-started