Grunt Samples

Grunt CLI 安装

Grunt CLI 安装

Grunt 安装

Installing Grunt and gruntplugins

Grunt 工程

该部分更多的描述参看Preparing a new Grunt project,大致总结如下,

package.json 和 Gruntfile.js

Grunt 工程需要包含两个基本的配置文件package.jsonGruntfile.js

package.json

This file is used by npm to store metadata for projects published as npm modules. You will list grunt and the Grunt plugins your project needs as devDependencies in this file.
npm使用,存储项目的元数据,并且将你的项目作为npm modules发布出去。devDependencies(Grunt plugins)是通过该文件进行配置的。

一句话,package.json用来管理相关模块和模块的依赖关系(这里就是维护 nodejs 的模块用的),更多内容 npm 章节中的 package.json 的描述

Gruntfile.js

is used to configure or define tasks and load Grunt plugins。用来配置和定义任务,并且加载Grunt plugins。

Gruntfile.js 才是 Grunt 自家的东西,是 Grunt 的核心,主要是用来描述 Grunt 工程的构建行为的,类似于maven build命令,会进行 java 源码质量检测,编译源码,打包,最后发布到 ./build 中,类似的,Gruntfile 中也定义了相关的行为,通过grunt build命令做类似的事情,首先进行 javascript 源码质量检测,编译源码,压缩,打包,最后发布到 ./build 中。
more: The Grundle file

Grunt Sample I

这里创建一个最简单,最简单的工程grunt-test来直观的了解下grunt能够做什么;该用例使用uglify模块(grunt-contrib-uglify)来实现 javascript 的压缩。
备注,可以通过grunt init来根据已有的工程模板来快速创建一个 jquery 或者 angular 的项目。

创建工程目录

创建工程grunt-start,既是在本地创建一个 grunt-test 的目录,(该工程在我的本地目录是/Users/mac/workspace/javascript/grunt/grunt-start)

创建 Package.json 和 Gruntfile

手动创建 Package.json 并导入包依赖

这里,我们手动的来创建 package.json 文件,package.json 的三种创建方式

1
$ vim package.json

输入如下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"name": "nodejs-start",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node app.js"
},
"dependencies": {
"express": "3.2.2",
"ejs": "*"
},
"devDependencies": {
"grunt": "~0.4.1",
"grunt-contrib-uglify": "~0.1.1"
}
}

我们使用到了两个 nodejs 模块,gruntgrunt-contrib-uglify,配置在devDependencies当中。注意,属性name定义了你的工程名称。

执行npm install命令,该命令根据 package.json 中的模块依赖定义导入相关依赖包。

1
$ npm install

这样,gruntuglify模块将会被加载。more refers npm 章节中的 package.json 的描述

创建 Gruntfile.js

创建 Gruntfile.js,

1
$ vim Gruntfile.js

添加如下的内容,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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'
}
}
});
// Load the plugin that provides the "uglify" task.
grunt.loadNpmTasks('grunt-contrib-uglify');
// Default task(s).
grunt.registerTask('default', ['uglify']);
};

含义参看The Gruntfile
注意几点,

  1. 这里构建目录指定在 build/ 目录中;
  2. src 使用的是pkg.name既是 package.json 中定义的nodejs-start,也就是将src/nodejs-start.js作为src输入。

javascript 源码目录和文件

首先在grunt-start中创建两个目录 src/ 和 build/,(build/ 用于构建输出),当前工程目录结构如下,

1
2
3
4
5
├── Gruntfile.js
├── build
├── node_modules
├── package.json
└── src

在 ./src 中创建测试文件nodejs-start.js,注意,这里的名字必须和package.json中定义的name属性值匹配,因为 Gruntfile.js 中使用的是pkg.name定义的src,(备注,pkg就是 package.json 的引用)

1
$ vim src/nodejs-grunt.js

输入如下内容,

1
2
3
var sayHello = function(name){
return "Hello " + name;
}

执行构建

  1. $ grunt

    1
    2
    3
    $ grunt
    Running "uglify:build" (uglify) task
    File "build/nodejs-start.min.js" created.

    查看结果

    1
    2
    3
    $cat build/nodejs-start.min.js 
    /*! nodejs-start 2017-02-19 */
    var sayHello=function(l){return"Hello "+l};

    重命名了比较长的参数名name,改成了l。看来uglify的压缩能力够强大的。

  2. $ grunt --stack
    默认情况下,$ grunt命令并不会输出错误信息,使用$ grunt --stack

Grunt Sample II

前言

该 Sample 引用自 http://gruntjs.com/sample-gruntfile, 这里的目的是一步一步将它在本地实现。注意官方教程里的这个 sample 是不完整的,没有package.json文件,同时也没有qunit的测试用例。这些是需要补全的。

Requirement

这个用例用到了所有常用的 grunt plugins,如下

  1. grunt-contrib-uglify:压缩js代码
  2. grunt-contrib-concat:合并js文件
  3. grunt-contrib-qunit 或者 jasmine:单元测试
  4. grunt-contrib-jshint:js代码检查
  5. grunt-contrib-watch:文件监控

package.json

The Gurntfile

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
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: {
// options here to override JSHint defaults
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']);

};

下面我们来逐个解读

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

package.json解析成 javascript 对象pkg,可以通过<% pkg.name %>来访问package.jsonname元素。

concat
1
2
3
4
5
6
7
8
9
concat: {
options: {
separator: ';'
},
dist: {
src: ['src/**/*.js'],
dest: 'dist/<%= pkg.name %>.js'
}
},

options

1
2
3
options: {
separator: ';'
},

覆盖concat的默认行为,合并的时候,通过符号;作为分隔符来连接两个文件的内容。

dist

1
2
3
4
dist: {
src: ['src/**/*.js'],
dest: 'dist/<%= pkg.name %>.js'
}

src目录以及子目录中的javascript文件合并成一个文件输出到dist/<% pkg.name %>

uglify
1
2
3
4
5
6
7
8
9
10
uglify: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> */\n'
},
dist: {
files: {
'dist/<%= pkg.name %>.min.js': ['<%= concat.dist.dest %>']
}
}
},

这里唯一需要特别注意的是,dist任务,dest'dist/<%= pkg.name %>.min.js',而src'<%= concat.dist.dest %>'。这里的Files采用的是 File Object Format 的写法。

qunit
jshint
watch

Grunt Sample III

创建一个 Angular 的工程样例。

Reference

offical: grunt getting started
grunt让Nodejs规范起来
Difference between Grunt, NPM and Bower ( package.json vs bower.json )