安装

  • 首先,grunt依赖Nodejs和npm环境
  • 使用npm安装grunt-cli命令行工具
  • 全局安装
    1
    npm install -g grunt-cli
  • 在项目中添加
    1
    npm install grunt --save-dev
  • 在项目根目录下创建Gruntfile.js文件

Gruntfile.js文件结构

  • 一个’wrapper’方法
  • 配置项目和任务
    • task //任务
      • target //任务子项
        • options //任务的配置参数
  • 加载grunt插件
  • 配置任务

Task

作为Task就是一个个任务
例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
copy: {
html: {
files: [{
expand: true,
cwd: './src/',
src: '**/**.html',
dest: './dist/'
}]
},
js:{
files:[{
expand:true,
cwd:'./src/',
src:'**/**.js',
dest:'./dist/'
}]
}
}

这个任务就是将html和js文件都复制到dist目录下

target

而target则是任务中针对不同操作分配的子项,在上述copy任务中,html和js就是两个target,他们分别负责复制不同的文件。

options

options就是每个任务的配置项目,在上述的copy任务中,expand,cwd,src,dest都是options

完整的Gruntfile.js示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
//wrapper函数
module.exports = function (grunt) {
//配置项目和任务
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
//复制html和图片文件到dist
copy: {
html: {
files: [{
expand: true,
cwd: './src/',
src: '**/**.html',
dest: './dist/'
}]
},
js:{
files:[{
expand:true,
cwd:'./src/',
src:'**/**.js',
dest:'./dist/'
}]
},
images: {
files: [{
expand: true,
cwd: './src/images',
src: '*',
dist: './dist/images/',
}]
}
},
//合并js
concat:{
dist: {
file: {
'./dist/js/all.js':['./dist/js/a1.js','./dist/js/a2.js']
}
}
},
//sass
sass: {
output: {
files: {
'./dist/css/style.css': './src/less/a.sass'
}
}
},
//压缩
uglify: {
uglifyjs: {
files: {
'./dist/js/all.js': ['./dist/js/all.js']
}
}
},
//打开服务器
connect: {
options: {
port: 9000,
open: true,
livareload: 35729,
//change this to '0.0.0.0' to access the server from outside
hostname: 'localhost'
},
server: {
options: {
port: 9001,
base: './'
}
}
}
});

//加载插件
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-sass');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-connect');
//注册任务
grunt.registerTask('copyhtml', ['copy:html']); //可以用task:target的方法分别注册
grunt.registerTask('concatjs', ['concat']); //也可以只用task名称注册,默认执行task下全部target
grunt.registerTask('outputcss', ['sass']);
// grunt.registerTask('watchit', ['concat', 'sass', 'jshint', 'uglify', 'connect', 'watch']);
grunt.registerTask('default', ['copy', 'concat', 'sass', 'uglify']);
};

配置任务

任务配置是Gruntfile通过grunt.initConfig方法指定的。这种配置大多会在任务命名的属性下,但可能包含任何任意的数据。只要属性不与任务需要的属性相冲突,它们将被忽略。

另外,因为这是JavaScript,所以不仅限于JSON。可以在这里使用任何有效的JavaScript。甚至可以编程生成配置,如果有必要的话。

1
2
3
4
5
6
7
8
9
10
11
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'],
});

配置task和target,添加options

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
grunt.initConfig({
concat: {
options: {
//task级别的options,会默认执行
},
foo: {
options: {
// "foo" 的options,会覆盖task级别的options
},
},
bar: {
//没有配置options,将会使用task级别的options
},
},
});

文件输入和输出

src-dest两个options将用于定义他们

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
grunt.initConfig({
concat: {
foo: {
src:'/src/a.js', //可以使用字符串格式
dest:'/dist/a.js'
},
bar: {
src:['/src/a.js','/src/aa.js'], //可以使用数组格式
dest:'/dist/a.js'
},
baz:{
files:{
'/dist/a.js':['/src/a.js','/src/aa.js'] //也可以使用文件对象格式
}
},
boz:{
files:[
{'/dist/a.js':['/src/a.js','/src/aa.js']}, //文件对象格式也支持多个src-dest映射
{'/dist/b.js':['/src/b.js','/src/bb.js']}
]
}
},
});

Globbing模式

单独指定所有源文件路径通常是不切实际的,所以Grunt通过内置的node-glob和minimatch库支持文件名扩展(也称为globbing)。

在文件路径中:

  • *匹配任意数量的字符,但不是 /
  • ?匹配单个字符,但不匹配 /
  • **匹配任意数量的字符,包括/只要它是路径部分中唯一的字符
  • {}允许逗号分隔的“或”表达式列表
  • !在一个模式的开始将忽略这种文件

所有大多数人需要知道的是,foo/.js将匹配.js在foo/子目录中结束的所有文件,但foo/**/.js将匹配.js在foo/子目录及其所有子目录中结束的所有文件。

另外,为了简化复杂的通配模式,Grunt允许指定文件路径或Globing模式的数组。模式按顺序处理,!前缀匹配不包括结果集中的匹配文件。结果集是唯一的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 匹配单文件:
{src: 'foo/this.js', dest: ...}
// 匹配数组中的文件:
{src: ['foo/this.js', 'foo/that.js', 'foo/the-other.js'], dest: ...}
// 以th开头的js文件:
{src: 'foo/th*.js', dest: ...}

// 仅a.js和b.js:
{src: 'foo/{a,b}*.js', dest: ...}
// a,b开头的js文件:
{src: ['foo/a*.js', 'foo/b*.js'], dest: ...}

// 根目录下的所有js文件:
{src: ['foo/*.js'], dest: ...}
// foo目录下的所有js文件,但是bar.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: ...}

模板

使用<% %>分隔符指定的模板将在任务从配置中读取时自动展开。递归地扩展模板直到没有更多的剩余。

整个配置对象是属性被解析的上下文。另外,grunt它的方法在模板中可用,例如。<%= grunt.template.today(‘yyyy-mm-dd’) %>。

  • <%= prop.subprop %>prop.subprop无论类型如何,展开到配置中的值。像这样的模板不仅可以用来引用字符串值,还可以用来引用数组或其他对象。
  • <% %>执行任意的内联JavaScript代码。这在控制流程或循环中很有用。

导入外部数据

grunt由grunt.file.readJSON和grunt.file.readYAML导入JSON和YAML数据的方法。

1
pkg: grunt.file.readJSON('package.json')

创建任务

注册任务

语法:

1
grunt.registerTask(taskName, [description, ] taskList)

默认任务,将在输入grunt之后直接执行,一下任务将在不指定任何人物的情况下执行jshint,qunit,concat,uglify

1
grunt.registerTask('default', ['jshint', 'qunit', 'concat', 'uglify']);

target可以以被指定,一下任务将在输入grunt dist命令时执行concat的dist target和uglify的dist target

1
grunt.registerTask('dist', ['concat:dist', 'uglify:dist']);

多任务

当多任务运行时,Grunt在Grunt配置中查找同名的属性(task)。多任务可以有多个配置,使用任意命名的“target”定义。

1
grunt.registerMultiTask(taskName, [description, ] taskFunction)

自定义任务

当一个基本任务运行时,Grunt不会查看配置或者环境 - 它只是运行指定的任务函数。

1
grunt.registerTask(taskName, [description, ] taskFunction)

API

官方api地址:https://gruntjs.com/api/grunt

这里不赘述