[web] grunt base
安装
npm install -g grunt-cli
npm install grunt -D
//npm i grunt-contrib-uglify -D
大多数Grunt任务依赖于传递给grunt.initConfig方法的对象中定义的配置数据。
grunt --help
命令将列出所有可用的任务。
Gruntfile通过grunt.initConfig
方法指定任务配置。此配置将主要位于任务命名属性下,但可能包含任意数据。只要属性与任务需要的属性不冲突,它们将被忽略。
此外,由于这是JavaScript,将不限于JSON; 可以在这里使用任何有效的JavaScript。如果需要,甚至可以以编程方式生成配置。
当一个任务运行时,Grunt在同名的属性下查找其配置。多任务可以有多个配置,使用任意命名的“目标”进行定义。
在任务配置中,options可以指定一个属性来覆盖内置的默认值。此外,每个目标可能具有options特定于该目标的属性。目标级选项将覆盖任务级选项。
options对象是可选的,如果不需要,也可以省略。
包裹Grunt配置。
module.exports = function(grunt) {
};
初始化配置对象:
grunt.initConfig({
});
将package.json文件中的项目设置存储到pkg属性中
pkg: grunt.file.readJSON('package.json')
插件
grunt-contrib-concat配置
concat: {
options: {
// define a string to put between each file in the concatenated output
separator: ';'
},
dist: {
// the files to concatenate
src: ['src/**/*.js'],
// the location of the resulting JS file
//读取package.json的name
dest: 'dist/<%= pkg.name %>.js'
}
}
grunt-contrib-uglify配置
uglify: {
options: {
// the banner is inserted at the top of the output
banner: '/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> */\n'
},
dist: {
files: {
'dist/<%= pkg.name %>.min.js': ['<%= concat.dist.dest %>']
}
}
}
grunt-contrib-qunit配置
qunit: {
//报告输出文件
files: ['test/**/*.html']
}
grunt-contrib-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
}
}
}
grunt-contrib-watch配置
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');
设定任务
// this would be run by typing "grunt test" on the command line
grunt.registerTask('test', ['jshint', 'qunit']);
// the default task can be run just by typing "grunt" on the command line
grunt.registerTask('default', ['jshint', 'qunit', 'concat', 'uglify']);
配置
concat任务有foo和bar目标,而该uglify任务只有一个bar目标。
同时指定任务和目标,grunt concat:foo
或grunt concat:bar
将只处理指定目标的配置,而运行时grunt concat将遍历所有目标,依次对其进行处理。
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.initConfig({
concat: {
bar: {
src: ['src/bb.js', 'src/bbb.js'],
dest: 'dest/b.js',
}
}
});
文件对象格式:
grunt.initConfig({
concat: {
bar: {
files: {
'dest/b.js': ['src/bb.js', 'src/bbb.js'],
'dest/b1.js': ['src/bb1.js', 'src/bbb1.js'],
}
}
}
});
文件数组格式:
grunt.initConfig({
concat: {
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.initConfig({
concat: {
'dest/a.js': ['src/aa.js', 'src/aaa.js'],
'dest/b.js': ['src/bb.js', 'src/bbb.js'],
}
});
自定义过滤功能
这里的isFile实际上是nodejs里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);
},
},
},
});
利用了globbing
和expand:true features -
可以避免覆盖目标中已经存在的文件,需要注意当检查目的地是否存在时,不会考虑重命名属性。
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
}
}]
}
}
});
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: ...}
动态构建文件对象
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.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
}
}]
}
}
});
将文件从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.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'
}
}
});
任务
任务别名
grunt.registerTask(taskName, [description, ] taskList)
例如定义了一个“默认”任务,如果在不指定任何任务的情况下执行Grunt,“jshint”,“qunit”,“concat”和“uglify”任务将自动运行:
grunt.registerTask('default', ['jshint', 'qunit', 'concat', 'uglify']);
也可以指定任务参数。
别名“dist”运行“concat”和“uglify”任务,每个任务都有一个“dist”参数:
grunt.registerTask('dist', ['concat:dist', 'uglify:dist']);
多任务
grunt.registerMultiTask(taskName, [description, ] taskFunction)
当运行多任务时,Grunt在Grunt配置中查找同名的属性。多任务可以有多个配置,使用任意命名的“目标”进行定义。
同时指定任务和目标,grunt concat:foo
或grunt concat:bar
将只处理指定目标的配置,而运行时grunt concat将遍历所有目标,依次对其进行处理.
如果执行log,会执行3个子任务。也可以通过grunt log:foo
这种形式执行单个任务。
grunt.initConfig({
log: {
foo: [1, 2, 3], //foo: 1,2,3
bar: 'hello world', //bar: hello world
baz: false //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('bar', '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);
});
//Running "asyncfoo" task
//Processing task...
//All done!
访问自己的名称与参数
grunt.registerTask('foo', 'My "foo" task.', function(a, b) {
grunt.log.writeln(this.name, a, b);
});
// Usage:
// grunt foo
// logs: "foo", undefined, undefined
// grunt foo:bar
// 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.');
}
// Fail by returning false if this task had errors
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() {
// Fail task if "foo" task failed or never ran.
grunt.task.requires('foo');
// This code executes if the "foo" task ran successfully.
grunt.log.writeln('Hello, world.');
});
// Usage:
// grunt foo bar
// doesn't log, because foo failed.
// ***Note: This is an example of space-separated sequential commands,
// (similar to executing two lines of code: `grunt foo` then `grunt bar`)
// grunt bar
// doesn't log, because foo never ran.
如果需要的配置属性不存在,任务可能会失败。
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() {
// Log the property value. Returns null if the property is undefined.
grunt.log.writeln('The meta.name property is: ' + grunt.config('meta.name'));
// Also logs the property value. Returns null if the property is undefined.
grunt.log.writeln('The meta.name property is: ' + grunt.config(['meta', 'name']));
});
可以通过this.async()
在任务体内调用来切换到异步。
传递false给该done()功能告知Grunt该任务已失败。
grunt.registerTask('asyncme', 'My asynchronous task.', function() {
var done = this.async();
doSomethingAsync(done);
});
命令
Grunt默认隐藏错误堆栈跟踪,可以使用--stack
选项显示堆栈追踪。