Grunt 基本概念(package.json/Gruntfile.js)

前言

该系列文章就是直接翻译 http://gruntjs.com/getting-started 中的内容,摘要重要的内容,过程中给出自己的理解。加深记忆。

备注:另外一款gulp和 grunt 同样是做构建工作的,虽然最终的目的相同,但是过程中的侧重点不同,gulp构建过程的侧重点是基于并发,而 grunt 是侧重依次执行;见分析文章,http://sixrevisions.com/web-development/grunt-vs-gulp/ 其实,grunt 也支持并发,但是需要在定义 task 的时候明确指定,而 gulp 天生就是为并发而创建的。

靠,居然发现一个中文网站 http://www.gruntjs.net/getting-started

Grunt 是什么

一句话,grunt 就是类似于构建 java 的 maven 这样的构建工具,用来构建 javascript 工程的。Grunt可以执行像压缩, 编译, 单元测试, 代码检查以及打包发布的任务。注意,这里有别于 npm,NPM 是用来构建 NodeJs 工程的,而 grunt 是用来构建诸如使用 angularjs,jquery 等工具开发的项目工程的,两者虽然都是类似于 maven 这样的构建工具,但是服务的对象不同。

Grunt常用插件:

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

Grunt CLI

Grunt CLI 安装

全局安装grunt-cli

1
npm install -g grunt-cli

  1. This will put the grunt command in your system path, allowing it to be run from any directory.
    他会将 grunt 添加到你的系统目录,允许在任何目录中被调用。
  2. Note that installing grunt-cli does not install the Grunt task runner,
    注意,grunt-cli 并没有安装Grunt task runner,也就是说,它并不是 Grunt 的执行器。
  3. his job of the Grunt CLI is simple: run the version of Grunt which has been installed next to a Gruntfile. This allows multiple versions of Grunt to be installed on the same machine simultaneously.
    它的职责很简单,启动 Gruntfile 中指定的 Grunt 版本,也因此,允许多个不同版本的 Grunt 被同时安装在一台机器上。

Grunt CLI 如何加载不同版本的 Grunt 的?

Each time grunt is run, it looks for a locally installed Grunt using node’s require() system. Because of this, you can run grunt from any subfolder in your project.

If a locally installed Grunt is found, the CLI loads the local installation of the Grunt library, applies the configuration from your Gruntfile,read the code

简而言之,就是,每当应用启动的时候,从Gruntfile中读取配置并通过required()方法从系统中获取对应版本的Grunt

Working with an existing Grunt project

Assuming that the Grunt CLI has been installed and that the project has already been configured with a package.json and a Gruntfile(Gruntfile.js or Gruntfile.yaml), it’s very easy to start working with Grunt:

  1. Change to the project’s root directory.
  2. Install project dependencies with npm install.
  3. Run Grunt with grunt.
    备注,这里执行的是grunt default,执行默认 task

That’s really all there is to it. Installed Grunt tasks can be listed by running grunt --help but it’s usually a good idea to start with the project’s documentation. 对的,一个grunt project其实就是这三步即可,也就可以实施对一个grunt project的整个生命周期的构建行为了。这就是Grunt的强大之处了。

Preparing a new Grunt project

A typical setup will involve adding two files to your project: package.jsonand the Gruntfile.

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.

Gruntfile: This file is named Gruntfile.js or Gruntfile.coffee and is used to configure or define tasks and load Grunt plugins. When this documentation mentions a Gruntfile it is talking about a file, which is either a Gruntfile.js or a Gruntfile.coffee.

package.json

The package.json file belongs in the root directory of your project, next to the Gruntfile, and should be committed with your project source. Running npm install in the same folder as a package.json file will install the correct version of each dependency listed therein.

There are a few ways to create a package.json file for your project:

  • Most grunt-init templates will automatically create a project-specific package.json file.
  • The npm init command will create a basic package.json file.
  • Start with the example below, and expand as needed, following this specification.
    这步其实是手动创建,根据package.json specification的定义手动创建,

上面描述了如何创建 package.json 的三种方式,不过grunt-init有更多的内容,它主要是根据已有的工程模板去创建一个grunt 工程,当然里面附带了所必须的package.json;更多内容 npm 章节中的 package.json 的描述

Installing Grunt and Grunt Plugins

注意,Grunt本身也是一个插件。
The easiest way to add Grunt and gruntplugins to an existing package.json is with the command npm install <module> --save-dev. Not only will this install <module> locally, but it will automatically be added to the devDependencies section, using a tilde version range. (补充,如果使用 npm install <module> -save将会添加到 package.json 中的 dependencies 中)

For example, this will install the latest version of Grunt in your project folder, adding it to your devDependencies:

1
$ npm install grunt --save-dev

The same can be done for gruntplugins and other node modules. As seen in the following example installing the JSHint task module:

1
npm install grunt-contrib-jshint --save-dev

Checkout the current available gruntplugins to be installed and used on your project at the plugins page.

Be sure to commit the updated package.json file with your project when you’re done!

以上是官方内容的原文,以下给出自己的理解,

grunt本身就是 nodejs 的一个模块,通过 npm 管理和安装。安装方式如下所述,

  1. npm install grunt

    1
    >> npm install grunt

    将会安装最新版本的grunt模块到本地工程根目录的.node_modules目录中。

  2. npm install grunt –save-dev

    1
    >> npm install grunt --save-dev

    不但会安装 grunt 模块,还会自动将该模块添加添加到package.jsondevDependencies中。另外,若执行npm install grunt --save将会将模块添加到package.jsondependecies

  3. 通过 package.json 的依赖关系进行安装
    在 package.json 中定义好 grunt 模块的依赖关系,然后grunt install即可安装。参考手动创建 Package.json

The Gruntfile

Gruntfile 和 package.json 一样,放置在项目的根目录当中,并且作为源码提交到代码版本管理器中,Gruntfile由如下部分组成

  • The “wrapper” function
  • Project and task configuration
  • Loading Grunt plugins and tasks
  • Custom tasks
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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 wrapper function

The wrapper function,任何的 grunt 代码都必须在该方法中定义。

1
2
3
module.exports = function(grunt) {
// Do grunt-related things in here
};

pkg 参数

package.json中加载元数据,并将其命名为pkg参数,这样的话,通过pkg就可以访问到 package.json 中的 json 元素,

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

定义模块的行为

定义grunt-contrib-uglify模块的行为,uglify模块的默认行为就是对 javascript 进行压缩,去掉注释,空格符等。然后自动生成 banner 注释。由此可以看到,实际上,Gruntfile 就是定义 Grunt 的执行行为,options

1
2
3
4
5
6
7
8
9
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')的方式加载;

1
2
// Load the plugin that provides the "uglify" task.
grunt.loadNpmTasks('grunt-contrib-uglify');

自动加载

有些时候,挨个手动的去加载比较麻烦,当模块比较多的时候,也比较容易出错,我们可以使用 jit-grunt 模块 实现模块的自动加载(备注,我是从 yeoman 生成的 angular 项目的 Gruntfile.js 中发现它的)

  1. 安装jit-grunt模块

    1
    $ npm install jit-grunt --save-dev
  2. Gruntfile.js中的wrapper中添加

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    module.exports = function (grunt) {
    // Automatically load required Grunt tasks
    require('jit-grunt')(grunt, {
    useminPrepare: 'grunt-usemin',
    ngtemplates: 'grunt-angular-templates',
    cdnify: 'grunt-google-cdn'
    });

    grunt.initConfig({
    ......
    })
    }
  3. 删除grunt.loadNpmTasks('grunt-contrib-uglify');
    将会自动加载了。

  4. 在命令行通过grunt uglify就可以执行了
  5. 完整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
    module.exports = function(grunt) {
    // 自动加载grunt模块
    require('jit-grunt')(grunt, {
    useminPrepare: 'grunt-usemin',
    ngtemplates: 'grunt-angular-templates',
    cdnify: 'grunt-google-cdn'
    });

    // 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']);
    };

grunt default(注册默认任务)

1
2
// Default task(s).
grunt.registerTask('default', ['uglify']);

可以通过grunt uglify或者grunt default或者直接grunt就可以启动和执行uglify task,并且可以在数组里面,添加任意多个 task。

custom tasks

如果你需要的 tasks 不是通过 Grunt Plugins 定义的,那么你可以定义你自己的 tasks,

  1. 方式一、代码定义在 Gruntfile 中

    1
    2
    3
    4
    5
    6
    7
    8
    module.exports = function(grunt) {

    // A very basic default task.
    grunt.registerTask('default', 'Log some stuff.', function() {
    grunt.log.write('Logging some stuff...').ok();
    });

    };
  2. 方式二、可以将 function 以外部 .js 的方式定义在外部,并通过grunt.loadTasks进行加载
    TODO, 未验证。