Grunt教程

写在前面

特别注意:如果觉得本文不够详尽,强烈建议去看Grunt英文官网的教程,因为中文版的对注释做了部分翻译,不如不翻译(容易被翻译误导)

  1. Grunt是什么?

    Grunt是自动构建工具,类似的东西还有Ant、Buildy、Gmake等等,更多的信息请查看黯羽轻扬:JS自动化

  2. Grunt有什么用?

    自动构建工具能把自动化工具整合起来,以任务的形式管理,所以Grunt官网的副标题是The JavaScript Task Runner。

    简单解释一下,构建项目 -> 模块化开发 -> 复用 -> 测试 -> 调试 -> 验证 -> 发布 -> 版本控制,这整个流程中需要用很多自动化工具,比如Require、QUnit、JSHint、Uglify等等。几乎每次修改源码都需要手动把某些工具按特定的顺序run again。。。

    有了Grunt这样的自动构建工具后就可以简化run again的操作,写好配置文件之后用命令行运行自定义任务即可,甚至可以配合grunt-contrib-watch插件监听文件修改,自动运行任务

  3. Grunt哪里好?

  • 火:Twitter、JQuery、Adobe、Mozilla等等都在用Grunt

  • 插件多:目前(2015.5.22)已经有4560个Grunt插件了,包括常用的JSHint、Require、Sass等等,大大地够用,实在不行还可以自己写插件

  • 好用:配置文件比较简单,插件文档齐全(npm官网提供统一管理)

一.安装grunt

  1. 安装NodeJS

    Windows直接去http://nodejs.org/下载安装包就好了,自带npm

    其它平台的安装教程

    装好之后命令行输入node -v测试一下

  2. 安装npm

    命令行输入npm -v测试一下,如果出错的话,自己想办法去装npm

  3. 安装Grunt-CLI(命令行工具Command Line Interface)

    命令行输入npm install -g grunt-cli等待安装完成即可

二.配置文件

需要两个配置文件:

  • package.json:用于Nodejs包管理,声明项目依赖模块(grunt以及grunt插件)

  • Gruntfile.js:Grunt配置文件,用来定义任务,可以叫Gruntfile.js或者Gruntfile.coffee

注意:package.json和Gruntfile.js都要放在项目的根目录下,与项目的源代码一起提交

说白了,学Grunt就是学怎么写配置文件

三.package.json

一般格式如下:

{
    "name": "项目名称",
    "version": "项目版本号",
    "description": "项目描述",
    "author": "项目创建者",
    "license": "项目版权",
    "devDependencies": {
        //项目依赖插件
    }
}

例如一个小项目的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"
  }
}

写好package.json后,命令行cd进来,执行npm install,下载刚才声明的各个依赖项,会被放在当前目录下的node_modules文件夹里

需要添加依赖项的时候直接npm install grunt-contrib-XXX –save-dev就可以了,能够自动更新package.json的内容

注意

  1. 不能有注释,否则npm install解析失败(无论是哪种形式的注释,无论放在文件首尾部分还是其它。。都不行)

  2. version必须是X.X.X形式,X.X报错

    其实version的格式有复杂的标准,请查看http://semver.org/

  3. 命令行输入npm install grunt-contrib-XXX –save-dev这样的命令可以自动更新package.json

    最后一个依赖模块grunt-contrib-watch就是这样添进去的

  4. 版本号前面的~和^是什么意思?

    没看到有资料解释这个,但我们可以猜:^表示会去找最新版本,而~表示指定版本(和git的HEAD^与HEAD~n可能一样。。当然,只是猜测)

    一般都用~指定具体版本,因为可能存在插件兼容性以及稳定性问题

    P.S.如果知道靠谱的解释的话请告诉我,谢谢

  5. 自动生成package.json

    命令行输入npm init根据命令行提示一步一步完成,实测不好用,建议手写,或者写一份常用的模版,强行复用

三.Gruntfile.js

一般格式如下:

module.exports = function(grunt){
    // 1.  定义任务
    grunt.initConfig({
        // 1.  读取package.json
        pkg: grunt.file.readJSON('package.json'),

        // 2.  初始化各个任务的配置对象
        task1: {
            options: {
                // 设置配置选项
            },

            build: {
                // 设置输入输出路径等等
            }
        },
        task2: {
            // ...
        }
    });

    // 2.  加载插件
    grunt.loadNpmTasks('Grunt插件名');

    // 3.  注册任务
    grunt.registerTask('default',['Grunt任务']);
    grunt.registerTask('mytask',['task1', 'task3']);
};

例如一个小项目的package.json:

module.exports = function(grunt) {

    // 1.  定义任务
    grunt.initConfig({
        // 1.  读取package.json
        pkg: grunt.file.readJSON("package.json"),

        // 2.  初始化各个任务的配置对象
        // 合并文件
        concat: {
            options: {
                // 防止合并出错(上一个文件尾部少了分号)
                separator: ";",
                // 顶部信息(需要自带注释格式,不自动注释,也不自动换行)
                banner: "/*<%= pkg.name %>_<%= pkg.version %> " +
                        // *注意*:yyyy-mm-dd要加引号,表示字符串参数
                        "<%= grunt.template.today('yyyy-mm-dd') %>*/\r\n\r\n",
                // 底部信息
                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"
            }
        },
        // 代码检查
        jshint: {
            options: {
                eqeqeq: true,   // 要求===
                trailing: true, // 要求尾部无空格
                //unused: true,   // 要求警告没用到的变量(模块化代码会报错)
                forin: true,    // 要求for-in必须有hasOwnProp过滤
                curly: true     // 要求花括号
            },

            files: ["Gruntfile.js", "src/*.js"]
        },
        // 代码瘦身
        uglify: {
            options: {
                // 不混淆变量名
                mangle: false,
                // 输出压缩率,可选的值有 false(不输出信息),gzip
                report: "min",
                // 顶部信息(需要自带注释格式,不自动注释,也不自动换行)
                banner: "/*<%= pkg.name %>_<%= pkg.version %> " +
                        "<%= grunt.template.today('yyyy-mm-dd') %>*/\r\n\r\n",
                // 底部信息
                footer: "\r\n\r\n/* author: http://ayqy.net/ */"
            },

            build: {
                files: {
                    // <%= concat.dist.dest %>表示uglify会自动瘦身concat任务中生成的文件
                    "build/<%= pkg.name %>.min.js": ["<%= concat.build.dest %>"]
                }
            }
        },
        // 监听文件变动,自动执行任务
        watch: {
            files: ["<%= jshint.files %>"],
            tasks: ["default"]
        }
    });

    // 2.  加载插件
    grunt.loadNpmTasks("grunt-contrib-concat");
    grunt.loadNpmTasks("grunt-contrib-jshint");
    grunt.loadNpmTasks("grunt-contrib-uglify");
    grunt.loadNpmTasks("grunt-contrib-watch");

    // 3.  注册任务
    grunt.registerTask("default", ["jshint", "concat", "uglify"]);  // 默认任务
    grunt.registerTask("check", ["jshint"]);    // 自定义任务:代码检查
};

写好Gruntfile.js后不需要执行num install之类的命令,直接用grunt TaskName执行对应的任务即可,比如:grunt执行default任务,grunt jshint执行代码检查,grunt mytask按顺序执行一连串的任务等等

如果有多个target的话可以用grunt TaskName:(英文半角冒号)TargetName执行指定的target,在注册任务的时候也可以用TaskName:TargetName指定target

P.S.至于target是什么,请往下看,配置Gruntfile.js可能有点麻烦,不过好在只用配置一次,以后直接用就可以了,可能对某些细节还不太理解,请务必看完下面的注意部分

注意

  1. dist和build到底用哪个?

    常见的有options-dist和options-build两种,都可以用,因为名字无所谓

    只有options是有所谓的,比如我们可以这样搞:

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

    当然还可以这样搞:

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

    然后执行grunt concat,会依次执行xx和xxx,所以如果只有一个target(xx和xxx都叫target)的话,用build和dist没什么区别,因为不需要语义区分

    如果有多个target,最好取一些语义友好的名字

    P.S.个人更倾向于build,当然,名字不重要,所以某些教程里甚至出现了bar、foo之类的,让人费解

  2. 关于options

    上面的例子说明grunt只认options(注意:少1个s都不行哟~),名字不是options的都一律当作target来执行

    前面例子里都是target级的options,其实也可以有task级的options,可以对task下所有的target起作用,当然,不知道这个也没关系,多写点代码而已

  3. 注册同名任务会造成死递归

    grunt.registerTask("concat", ["concat"]);
    
  4. 可以给任务取别名

    grunt.registerTask("check", ["jshint"]);    // 自定义任务:代码检查
    
  5. 更多的例子

    如果需要更多的例子帮助理解,建议自己写个小项目,慢慢测试

    如果实在没多少时间,请查看博客园:grunt使用小记之uglify:最全的uglify使用DEMO

四.在线资源

  1. 不知道哪个插件能满足需求,稳定,好用

    http://www.gruntjs.net/plugins

    链接页面给出了30天内下载量top100的插件,附有功能简介、最后更新时间等等,点击即可跳转至对应的npm官网插件主页。此外还支持搜索,非常方便

  2. 不知道XX插件有哪些配置选项

    https://www.npmjs.com/

    链接页面是npm官网,在搜索框里填插件名即可,例如grunt-contrib-watch

    插件主页提供了详细的配置说明以及例子,比如watch的配置选项:

    最简单的用法:

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

    或者复杂的:

    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

    链接页面有Grunt的所有API,比如grunt.log、grunt.initConfig等等等等,看到新东西就去查吧

参考资料

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*

code