2

这篇文章的目标是帮助大家快速上手grunt,适用的grunt版本为0.4.x,本文只是大致介绍,如果想做深入了解请阅读grunt官方文档

安装grunt命令行工具

首先确保你的node版本在0.8以上(暂时不建议适用0.10.0),命令:

node -v

然后安装grunt命令行工具grunt-cli

npm install -g grunt-cli

可能需要前面加上sudo(例如 OSX, *nix)。

如果你只前装过grunt的老版本的话则需要卸载:

npm uninstall -g grunt

安装grunt模板

grunt模板的作用是帮助你生成初始的Gruntfile.js文件,当然你也可以直接把其它项目的Gruntfile.js文件拷贝过来使用。

首先要安装命令行工具grunt-init:

npm install -g grunt-init

然后安装模板,目前有三种模板是由grunt官方做维护的,还有别的可在github上找到,或者你自己实现一个。 官方模板的安装命令如下:

git clone git://github.com/gruntjs/grunt-init-gruntfile.git $HOME/.grunt-init/
git clone git://github.com/gruntjs/grunt-init-jquery.git $HOME/.grunt-init/
git clone git://github.com/gruntjs/grunt-init-node.git $HOME/.grunt-init/

三种分别对应默认grunt模板,jquery插件的grunt模板,node包的grunt模板。

然后就可以适用grunt-init命令来初始化你的Gruntfile.js文件了,例如你要安装默认模板:

grunt-init grunt-init-gruntfile #最后一个参数也可以是模板所在的文件夹

它会问你一些问题,然后根据你的答案创建当前项目的Gruntfile.js文件。

安装所需node包

首先是创建package.json文件(npm的包管理配置文件)。
你可以使用npm init命令创建这个文件或者直接拷贝其它项目的。

然后在项目的根目录下安装你开发依赖的各种grunt包,首先是grunt:

npm install grunt --save-dev

--save-dev可以将你所安装的包自动保存到package.json文件中的devDependencies属性中去,如果你的项目使用时(不仅仅是开发)也需要用到某个包,
你应该使用--save将其保存在dependcies属性中。

你肯定要用到需要其它grunt模块来帮你完成构建任务,这时你就可以用这种方法把他们都加到项目中,例如最常用的concat jshint uglify模块:

npm install grunt-contrib-concat --save-dev
npm install grunt-contrib-jshint --save-dev
npm install grunt-contrib-uglify --save-dev

还有种方式就是先把依赖的包写到package.json中的devDependencies中去,然后直接使用npm nistall来安装。

编辑Gruntfile.js文件使之工作

我们以本站当前使用的Gruntfile.js为例来了解一下grunt设置中最重要的部分。

var path = require('path');
var snippet = require('grunt-contrib-livereload/lib/utils').livereloadSnippet;

module.exports = function(grunt) {
  // Project configuration.
  grunt.initConfig({
    // Task configuration.
    concat: {
      dist: {
        src: ['javascripts/common.js','javascripts/respond.js'],
        dest: 'javascripts/main.js'
      }
    },
    uglify: {
      options: {
        banner: '/*script for site: http://chemzqm.me*/'
      },
      dist: {
        src: '<%= concat.dist.dest %>',
        dest: 'javascripts/main.min.js'
      }
    },
    cssmin: {
      compress:{
        files:{
          "stylesheets/styles.min.css":["stylesheets/styles.css"]
        }
      }
    },
    jshint: {
      options: {
        curly: true,
        eqeqeq: true,
        immed: true,
        latedef: true,
        newcap: true,
        noarg: true,
        sub: true,
        undef: true,
        unused: false,
        boss: true,
        eqnull: true,
        browser: true,
        node: true,
        globals: {
          jQuery: true
        }
      },
      all: ['javascripts/common.js','Gruntfile.js']
    },
    regarde: {
      livereload: {
        files: ['_posts/*.md', 'javascripts/common.js', 'stylesheets/styles.css', '*.html'],
        tasks: ['default', 'livereload']
      }
    },
    connect: {
      livereload: {
        options: {
          port: 8000,
          middleware: function(connect, options) {
            return [snippet, connect.static(path.resolve(process.cwd(), '_site'))];
          }
        }
      }
    },
    bgShell:{
      jekyll:{
        cmd: 'jekyll'
      }
    }
  });

  // These plugins provide necessary tasks.
  grunt.loadNpmTasks('grunt-contrib-cssmin');
  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-regarde');
  grunt.loadNpmTasks('grunt-bg-shell');
  grunt.loadNpmTasks('grunt-contrib-connect');
  grunt.loadNpmTasks('grunt-contrib-livereload');

  // Default task.
  grunt.registerTask('default', ['jshint', 'concat', 'uglify', 'cssmin', 'bgShell:jekyll']);
  // start livereload server
  grunt.registerTask('server', ['livereload-start', 'connect', 'regarde']);

};
  • module.exports指向调用该模块时将返回的对象,而当你使用grunt命令的时候这个名为Gruntfile.js的文件就会被grunt找到(0.4.0以上会找Gruntfile.js,而之前是grunt.js,具体的找法也很简单,就是从运行命令的目录开始一直往上找,直到找到为止),然后以grunt对象为参数执行该模块返回的函数。

  • grunt.initConfig方法接受一个Plain Object做为参数,该对象的每个属性名代表一个顶层task名字,而值则是具体的配置。配置任务时要注意不能把子任务名配置成任务要读取的配置属性,例如uglify需要options做为配置属性,这时就不能把子任务名也设置成options。如果任务是multitask则可以为一个任务配置多个子任务,例如配置多个合并文件的任务:

    concat: {
      dist: {
        src: ['javascripts/common.js','javascripts/respond.js'],
        dest: 'javascripts/main.js'
      },  
      dist2:{
        src: ['javascripts/*.js'],
        dest:'javascripts/all.js'
      }   
    },
    

    这样你就可以通过grunt concat来顺序执行两个合并任务,或者grunt concat:dist只执行dist任务。上面用到的任务都是multitask,具体实践的时候从文档或者源码都可以判断。此外,grunt还支持文件名的模式匹配和模板替换的功能,例如匹配所有js文件可以用*.js,想引用concat:dist任务的dist属性可以用<%= concat.dist.dest %>, 详细的说明还请阅读官方文档

  • grunt.loadNpmTasks负责载入npm模块定义的任务,grunt 0.4.0之后没有了核心任务,所以每个任务都需要开发者写代码载入(还有一个grunt.task.loadTasks方法用来加载指定目录task文件,具体参阅task官方文档)。

  • grunt.registerTask用来注册新的任务,它最简单的形式如下:
    grunt.task.registerTask(taskName, taskList)
    名为taskname的任务出发后,tasklist数组(grunt 0.4.0之前是空格分格的字符串)中的任务将被顺序执行, 比较特殊的是名为default的任务,它将在命令行输入grunt后被调用。grunt.registerTaskgrunt.registerMultiTask还可通过传入函数的方式创建新的任务,具体参阅创建task文档

最后重点介绍一下livereload模块,这个模块分两个任务liveload-startliveload ,前一个任务启动livereload服务,后一个任务负责通知浏览器进行刷新。livereload本身不提供文件的web服务,所以我们需要connect任务插件来提供。然后通过require('grune-contrib-livereload/lib/utils').livereloadSnippet获取到livereload插件提供的中间件函数(实质上就是参数为req, res, next的函数),然后配置到connectmiddleware参数中,这时请求connect服务
(http://localhost:8000) 得到的html就会被动态的加入负责与livereload服务端通讯的一段代码。grunt-regarde模块负责监控文件变化(不用grunt-watch是因为那个获得不了变化的文件名),这里我们根据需要配置成每当文件变化后就执行default任务和livereload任务。最后,配置

grunt.registerTask('server', ['livereload-start', 'connect', 'regarde']);

就可以通过grunt server命令同时启用liveload, connect, 和regard服务(顺序可以任意)

grunt已经开始被越来越多的知名项目所使用了(例如jquery和AngularJS),而且它的任务插件也在不断的扩展之中(例如coffeescript, jade, stylus)。虽说相比Makefile笨拙了一些,但更容易满足团队中各种操作系统下开发的需要


chemzqm
2k 声望83 粉丝

Javascript全栈开发,产品设计,自动化工具。追求简洁的设计,模块化开发,卓越的用户体验。