Skip to main content

Grunt Tutorial

Free2015-05-22#Tool#grunt#grunt教程#grunt指南

A Grunt tutorial without much nonsense, not too long, simple and clear

Preface

Special Note: If you feel this article is not detailed enough, strongly recommend reading the tutorial on Grunt Official English Website, because the Chinese version partially translated the comments, which is worse than not translating (easy to be misled by translation)

  1. What is Grunt?

Grunt is an automatic build tool, similar things include Ant, Buildy, Gmake, etc. For more information please see [AnYuQingYang: JS Automation](/articles/js 自动化/)

  1. What is Grunt used for?

Automatic build tools can integrate automation tools together, managed in the form of tasks, so Grunt official website's subtitle is The JavaScript Task Runner.

Simply explained, building projects -> modular development -> reuse -> testing -> debugging -> verification -> release -> version control, this entire process requires many automation tools, such as Require, QUnit, JSHint, Uglify, etc. Almost every source code modification requires manually running certain tools in specific order again...

With automatic build tools like Grunt, you can simplify the run again operations. After writing the configuration file, just run custom tasks via command line. You can even cooperate with grunt-contrib-watch plugin to listen for file modifications and automatically run tasks

  1. What's good about Grunt?
  • Popular: Twitter, JQuery, Adobe, Mozilla, etc. all use Grunt

  • Many plugins: Currently (2015.5.22) there are already 4560 Grunt plugins, including commonly used JSHint, Require, Sass, etc., more than enough. If really not enough, you can write plugins yourself

  • Easy to use: Configuration files are relatively simple, plugin documentation is complete (npm official website provides unified management)

I. Installing Grunt

  1. Install NodeJS

Windows users just go to http://nodejs.org/ to download and install the package, comes with npm

Installation tutorial for other platforms

After installation, type node -v in command line to test

  1. Install npm

Type npm -v in command line to test. If error occurs, figure out how to install npm yourself

  1. Install Grunt-CLI (Command Line Interface)

Type npm install -g grunt-cli in command line and wait for installation to complete

II. Configuration Files

Need two configuration files:

  • package.json: Used for Nodejs package management, declares project dependent modules (grunt and grunt plugins)

  • Gruntfile.js: Grunt configuration file, used to define tasks, can be named Gruntfile.js or Gruntfile.coffee

Note: Both package.json and Gruntfile.js must be placed in the project root directory, submitted together with project source code

To put it simply, learning Grunt is learning how to write configuration files

III. package.json

General format is as follows:

{
    "name": "project name",
    "version": "project version number",
    "description": "project description",
    "author": "project creator",
    "license": "project copyright",
    "devDependencies": {
        // project dependent plugins
    }
}

For example, a small project's package.json:

{
  "name": "world",
  "version": "0.4.0",
  "devDependencies": {
    "grunt": "~0.4.2",
    "grunt-contrib-concat": "~0.3.0",
    "grunt-contrib-jshint": "~0.6.3",
    "grunt-contrib-uglify": "~0.2.2",
    "grunt-contrib-watch": "^0.6.1"
  }
}

After writing package.json, cd into the directory via command line, execute npm install, download the declared dependencies, they will be placed in the node_modules folder under current directory

When needing to add dependencies, just use npm install grunt-contrib-XXX --save-dev, it can automatically update package.json content

Note:

  1. Cannot have comments, otherwise npm install parsing fails (no matter what form of comments, whether at file beginning, end or elsewhere... all won't work)

  2. version must be in X.X.X format, X.X will error

Actually version format has complex standards, please see http://semver.org/

  1. Command like npm install grunt-contrib-XXX --save-dev can automatically update package.json

The last dependency module grunt-contrib-watch was added this way

  1. What do ~ and ^ before version numbers mean?

Didn't see any materials explaining this, but we can guess: ^ means it will look for latest version, while ~ means specified version (might be same as git's HEAD^ and HEAD~n... of course, just guessing)

Generally use ~ to specify concrete version, because there might be plugin compatibility and stability issues

P.S. If you know reliable explanation, please tell me, thank you

  1. Automatically generate package.json

Type npm init in command line and follow command line prompts step by step to complete, tested not very useful, recommend writing by hand, or write a commonly used template, force reuse

III. Gruntfile.js

General format is as follows:

module.exports = function(grunt){
    // 1.  Define tasks
    grunt.initConfig({
        // 1.  Read package.json
        pkg: grunt.file.readJSON('package.json'),

        // 2.  Initialize configuration objects for each task
        task1: {
            options: {
                // Set configuration options
            },
            
            build: {
                // Set input/output paths, etc.
            }
        },
        task2: {
            // ...
        }
    });

    // 2.  Load plugins
    grunt.loadNpmTasks('Grunt plugin name');

    // 3.  Register tasks
    grunt.registerTask('default',['Grunt task']);
    grunt.registerTask('mytask',['task1', 'task3']);
};

For example, a small project's package.json:

module.exports = function(grunt) {
    
    // 1.  Define tasks
    grunt.initConfig({
        // 1.  Read package.json
        pkg: grunt.file.readJSON("package.json"),
        
        // 2.  Initialize configuration objects for each task
        // Concatenate files
        concat: {
            options: {
                // Prevent concatenation errors (missing semicolon at end of previous file)
                separator: ";",
                // Top information (need to bring your own comment format, doesn't auto-comment, doesn't auto-line-break)
                banner: "/*<%= pkg.name %>_<%= pkg.version %> " +
                        // *Note*: yyyy-mm-dd needs quotes, indicating string parameter
                        "<%= grunt.template.today('yyyy-mm-dd') %>*/\r\n\r\n",
                // Bottom information
                footer: "\r\n\r\n/* author: http://ayqy.net/ */"
            },
            
            build: {
                src: ["src/w.js", "src/Const.js", "src/Item.js", "src/Map.js", "src/Util.js", "src/Core.js"],
                dest: "build/<%= pkg.name %>.js"
            }
        },
        // Code checking
        jshint: {
            options: {
                eqeqeq: true,   // Require ===
                trailing: true, // Require no trailing spaces
                //unused: true,   // Require warning for unused variables (modular code will error)
                forin: true,    // Require for-in must have hasOwnProp filter
                curly: true     // Require curly braces
            },
            
            files: ["Gruntfile.js", "src/*.js"]
        },
        // Code minification
        uglify: {
            options: {
                // Don't mangle variable names
                mangle: false,
                // Output compression rate, optional values are false(don't output info), gzip
                report: "min",
                // Top information (need to bring your own comment format, doesn't auto-comment, doesn't auto-line-break)
                banner: "/*<%= pkg.name %>_<%= pkg.version %> " +
                        "<%= grunt.template.today('yyyy-mm-dd') %>*/\r\n\r\n",
                // Bottom information
                footer: "\r\n\r\n/* author: http://ayqy.net/ */"
            },
            
            build: {
                files: {
                    // <%= concat.dist.dest %> means uglify will automatically minify files generated in concat task
                    "build/<%= pkg.name %>.min.js": ["<%= concat.build.dest %>"]
                }
            }
        },
        // Listen for file changes, automatically execute tasks
        watch: {
            files: ["<%= jshint.files %>"],
            tasks: ["default"]
        }
    });
    
    // 2.  Load plugins
    grunt.loadNpmTasks("grunt-contrib-concat");
    grunt.loadNpmTasks("grunt-contrib-jshint");
    grunt.loadNpmTasks("grunt-contrib-uglify");
    grunt.loadNpmTasks("grunt-contrib-watch");
    
    // 3.  Register tasks
    grunt.registerTask("default", ["jshint", "concat", "uglify"]);  // Default task
    grunt.registerTask("check", ["jshint"]);    // Custom task: code checking
};

After writing Gruntfile.js, no need to execute num install or similar commands, just use grunt TaskName to execute corresponding tasks directly. For example: grunt executes default task, grunt jshint executes code checking, grunt mytask executes a series of tasks in order, etc.

If there are multiple targets, can use grunt TaskName*: (English half-width colon)*TargetName to execute specified target. When registering tasks, can also use TaskName:TargetName to specify target

P.S. As for what target is, please continue reading below. Configuring Gruntfile.js might be a bit troublesome, but fortunately only need to configure once, can use directly afterwards. May not fully understand some details, please be sure to finish reading the Note section below

Note:

  1. Which to use between dist and build?

Common are options-dist and options-build, both can be used, because names don't matter

Only options is what matters, for example we can do this:

    concat: {
        options: {
            separator: ";"
        },
        
        xx: {
            src: "src/*.js",
            dest: "<%= pkg.name %>.js"
        }
    }
    

Of course can also do this:

    concat: {
        options: {
            separator: ";"
        },
        
        xx: {
            src: "src/*.js",
            dest: "<%= pkg.name %>.js"
        },
        
        xxx: {
            src: "src/*.js",
            dest: "<%= pkg.name %>.js"
        }
    }
    

Then execute grunt concat, will execute xx and xxx in sequence. So if there's only one target (both xx and xxx are called targets), using build and dist makes no difference, because no need for semantic distinction

If there are multiple targets, better to choose some semantically friendly names

P.S. Personally prefer build, of course, names don't matter, so some tutorials even have bar, foo, etc., which is confusing

  1. About options

Above examples show grunt only recognizes options (Note: missing 1 's' won't work~), names that aren't options are all treated as targets to execute

Above examples are all target-level options, actually can also have task-level options, can affect all targets under task. Of course, doesn't matter if you don't know this, just write more code

  1. Registering same-name tasks will cause infinite recursion

    grunt.registerTask("concat", ["concat"]);
    
  2. Can give tasks aliases

    grunt.registerTask("check", ["jshint"]);    // Custom task: code checking
    
  3. More examples

If need more examples to help understand, recommend writing a small project yourself, test slowly

If really don't have much time, please see CnBlogs: Grunt Usage Notes - uglify: Most Complete uglify Usage DEMO

IV. Online Resources

  1. Don't know which plugin can meet requirements, stable, easy to use

http://www.gruntjs.net/plugins

Link page gives top 100 plugins by downloads in last 30 days, with function introduction, last update time, etc. Click to jump to corresponding npm official website plugin homepage. Also supports search, very convenient

  1. Don't know what configuration options XX plugin has

https://www.npmjs.com/

Link page is npm official website, fill in plugin name in search box, for example grunt-contrib-watch

Plugin homepage provides detailed configuration instructions and examples, for example watch configuration options:

Simplest usage:

    watch: {
        files: ['**/*'],
        tasks: ['jshint']
    }
    

Or complex:

    watch: {
        sass: {
            // We watch and compile sass files as normal but don't live reload here 
            files: ['src/sass/*.sass'],
            tasks: ['sass']
        },
        livereload: {
            // Here we watch the files the sass task will compile to 
            // These files are sent to the live reload server after sass compiles to them 
            options: { livereload: true },
            files: ['dest/**/*']
        }
    }
    

3. Grunt API

http://gruntjs.com/api/grunt

Link page has all Grunt APIs, such as grunt.log, grunt.initConfig, etc., etc. Go check when seeing new things

References

Comments

No comments yet. Be the first to share your thoughts.

Leave a comment