grunt api 文档-程序员宅基地

技术标签: ViewUI  shell  json  javascript  

Grunt docs

Grunt和 Grunt 插件是通过 npm 安装并管理的,npm是 Node.js 的包管理器。

安装 grunt-cli

npm install grunt-cli -g

注意,安装grunt-cli并不等于安装了 Grunt!Grunt CLI的任务很简单:调用与Gruntfile在同一目录中 Grunt。这样带来的好处是,允许你在同一个系统上同时安装多个版本的 Grunt。

这样就能让多个版本的 Grunt 同时安装在同一台机器上。
每次运行grunt 时,他就利用node提供的require()系统查找本地安装的 Grunt。正是由于这一机制,你可以在项目的任意子目录中运行grunt 。

如果找到一份本地安装的 Grunt,CLI就将其加载,并传递Gruntfile中的配置信息,然后执行你所指定的任务。为了更好的理解 Grunt CLI的执行原理,请阅读源码

本地安装 grunt

npm install grunt

本地安装 grunt 到当前文件夹( npm install grunt --registry http://registry.cnpmjs.org 从镜像站安装)

grunt项目

grunt-cligrunt 已经安装好后,在项目文件夹创建好 package.jsongruntfile.js 文件, 然后就可以安装 grunt任务插件

npm install  //安装任务插件
grunt //执行任务 默认执行 default

创建grunt项目

一般需要在你的项目中添加两份文件:package.jsonGruntfile

package.json: 此文件被npm用于存储项目的元数据,以便将此项目发布为npm模块。你可以在此文件中列出项目依赖的grunt和Grunt插件,放置于devDependencies配置段内。

Gruntfile: 此文件被命名为 Gruntfile.jsGruntfile.coffee,用来配置或定义任务(task)并加载Grunt插件的。 此文档中提到的 Gruntfile 其实说的是一个文件,文件名是 Gruntfile.js 或 Gruntfile.coffee。

1. package.json

package.json 应当放置于项目的根目录中,与Gruntfile.js在同一目录中,并且应该与项目的源代码一起被提交。

创建package.json文件的3种方式:

  • grunt-init自动创建项目的 package.json 文件。
  • npm init 问答的方式创建 package.json
  • 手工编写 package.json 文件,name,version,main,dependencies,devDependencies 常用字段

示例

{
    "name": "my-project-name",
    "version": "0.1.0",
    "devDependencies": {
    "grunt": "~0.4.5",
    "grunt-contrib-jshint": "~0.10.0",
    "grunt-contrib-nodeunit": "~0.4.1",
    "grunt-contrib-uglify": "~0.5.0"
    }
}
2. 安装Grunt 和 grunt插件
npm install  //安装package.json中定义的插件

npm install ungilify --save-dev  //向已经存在的package.json 文件中添加Grunt和grunt插件

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

Grunt 插件 页面可以看到当前可用的 Grunt 插件,他们可以直接在项目中安装并使用。

3. gruntfile.js

Gruntfile.jsGruntfile.coffee文件是有效的 JavaScript 或 CoffeeScript 文件,应当放在你的项目根目录中,和package.json文件在同一目录层级,并和项目源码一起加入源码管理器。

gruntfile.js由以下4部分组成:

  • "wrapper" 函数
  • 项目和任务配置
  • 加载 grunt 插件和任务
  • 自定义任务

示例

在下面列出的这个 Gruntfile.js 中,package.json文件中的项目元数据(metadata)被导入到 Grunt 配置中, grunt-contrib-uglify 插件中的uglify 任务(task)被配置为压缩(minify)源码文件并依据上述元数据动态生成一个文件头注释。当在命令行中执行 grunt 命令时,uglify 任务将被默认执行。

module.exports = function(grunt) {

    // Project configuration. 项目和任务配置
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'), //pkg为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'
            }
        }
    });

    // 加载包含 "uglify" 任务的插件。
    grunt.loadNpmTasks('grunt-contrib-uglify');

    // 默认被执行的任务列表。
    grunt.registerTask('default', ['uglify']);

};

gruntfile.js详解

  1. "wrapper" 函数
    每一份 Gruntfile(和grunt插件)都遵循同样的格式,你所书写的Grunt代码必须放在此函数内:

     module.exports = function(grunt){
         //do grunt-related things in here
     }
  2. 项目和任务配置
    大部分的Grunt任务都依赖某些配置数据,这些数据被定义在一个object内,并传递给grunt.initConfig 方法。

    由于<% %>模板字符串可以引用任意的配置属性,因此可以通过这种方式来指定诸如文件路径和文件列表类型的配置数据,从而减少一些重复的工作。

    你可以在这个配置对象中(传递给initConfig()方法的对象)存储任意的数据。此外,由于这本身就是JavaScript,你不仅限于使用JSON;你可以在这里使用任意的有效的JS代码。如果有必要,你甚至可以以编程的方式生成配置。

    与大多数task一样,grunt-contrib-uglify 插件中的uglify 任务要求它的配置被指定在一个同名属性中。在这里有一个例子, 我们指定了一个banner选项(用于在文件顶部生成一个注释)。

     // 项目和任务配置
     grunt.initConfig({
       pkg: grunt.file.readJSON('package.json'), //获取 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' //压缩目标文件
         }
       }
     });
  3. 加载grunt插件任务
    concatenation、[minification]、grunt-contrib-uglify 和 linting这些常用的任务(task)都已经以grunt插件的形式被开发出来了。只要在 package.json 文件中被列为dependency(依赖)的包,并通过npm install安装之后,都可以在Gruntfile中以简单命令的形式使用:

     // 加载能够提供"uglify"任务的插件。
     grunt.loadNpmTasks('grunt-contrib-uglify');
  4. 自定义任务
    通过定义 default 任务,可以让Grunt默认执行一个或多个任务。在下面的这个案例中,执行 grunt 命令时如果不指定一个任务的话,将会执行uglify任务。这和执行grunt uglify 或者 grunt default 的效果一样。default任务列表数组中可以指定任意数目的任务(可以带参数)。

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

    如果Grunt插件中的任务(task)不能满足你的项目需求,你还可以在Gruntfile中自定义任务(task)。例如,在下面的 Gruntfile 中自定义了一个default 任务,并且他甚至不依赖任务配置:

     module.exports = function(grunt) {
    
         // A very basic default task.
         grunt.registerTask('default', 'Log some stuff.', function() {
         grunt.log.write('Logging some stuff...').ok();
         });
    
     };
4. 执行 grunt
grunt   
查看帮助
grunt -h

;

;

Grunt 配置

Grunt的task配置都是在 Gruntfile 中的 grunt.initConfig方法中指定的。此配置主要是以任务名称命名的属性,也可以包含其他任意数据。一旦这些代表任意数据的属性与任务所需要的属性相冲突,就将被忽略。

此外,由于这本身就是JavaScript,因此你不仅限于使用JSON;你可以在这里使用任何有效的JavaScript。必要的情况下,你甚至可以以编程的方式生成配置。

grunt.initConfig({
  concat: {
    // 这里是concat任务的配置信息。
  },
  uglify: {
    // 这里是uglify任务的配置信息
  },
  // 任意数据。
  my_property: 'whatever',
  my_src_files: ['foo/*.js', 'bar/*.js'],
});

任务配置和目标

当运行一个任务时,Grunt会自动查找配置对象中的同名属性 (如: grunt uglify) 。多任务(multi-task)可以通过任意命名的“目标(target)”来定义多个配置。在下面的案例中,concat任务有名为foo和bar两个目标,而uglify任务仅仅只有一个名为bar目标。

grunt.initConfig({
  concat: {
    foo: {
      // concat task "foo" target options and files go here.
    },
    bar: {
      // concat task "bar" target options and files go here.
    },
  },
  uglify: {
    bar: {
      // uglify task "bar" target options and files go here.
    },
  },
});

同时指定任务(task)和目标(target),例如grunt concat:foo或者grunt concat:bar将只会处理指定目标(target)的配置,而运行 grunt concat 将遍历所有目标(target)并依次处理。注意,如果一个任务使用grunt.task.renameTask重命名过,Grunt将在配置对象中查找以新的任务名命名的属性。

options 属性

在一个任务配置中,options 属性可以用来指定覆盖内置属性的默认值。此外,每一个目标(target)中还可以拥有一个专门针对此目标(target)的options属性。目标(target)级的options将会覆盖任务级的options

options属性是可选的,如果不需要,可忽略

grunt.initConfig({
  concat: {
    options: {
      // 这里是任务级的Options,覆盖默认值 
    },
    foo: {
      options: {
        // "foo" target options may go here, overriding task-level options.
      },
    },
    bar: {
      // No options specified; this target will use task-level options.
    },
  },
});

文件

由于大多的任务都是执行文件操作,Grunt有一个强大的抽象层用于声明任务应该操作哪些文件。这里有好几种定义src-dest(源文件-目标文件)文件映射的方式,均提供了不同程度的描述和控制操作方式。任何一种多任务(multi-task)都能理解下面的格式,所以你只需要选择满足你需求的格式就行。

所有的文件格式都支持src和dest属性,此外"Compact"[简洁]和"Files Array"[文件数组]格式还支持以下一些额外的属性:

  • filter 它通过接受任意一个有效的fs.Stats方法名或者一个函数来匹配src文件路径并根据匹配结果返回true或者false。
  • nonull 如果被设置为 true,未匹配的模式也将执行。结合Grunt的--verbore标志, 这个选项可以帮助用来调试文件路径的问题。
  • dot 它允许模式模式匹配句点开头的文件名,即使模式并不明确文件名开头部分是否有句点。
  • matchBase如果设置这个属性,缺少斜线的模式(意味着模式中不能使用斜线进行文件路径的匹配)将不会匹配包含在斜线中的文件名。 例如,a?b将匹配/xyz/123/acb但不匹配/xyz/acb/123。
  • expand 处理动态的src-dest文件映射,更多的信息请查看动态构建文件对象。
    其他的属性将作为匹配项传递给底层的库。 请查看node-glob 和minimatch 文档以获取更多信息。

简洁格式

这种形式允许每个目标对应一个src-dest文件映射。通常情况下它用于只读任务,比如grunt-contrib-jshint,它就只需要一个单一的src属性,而不需要关联的dest选项. 这种格式还支给每个src-dest文件映射指定额外的属性。

grunt.initConfig({
  jshint: {
    foo: {
      src: ['src/aa.js', 'src/aaa.js']
    },
  },
  concat: {
    bar: {
      src: ['src/bb.js', 'src/bbb.js'],
      dest: 'dest/b.js',
    },
  },
});

文件对象格式

这种形式支持每个目标对应多个src-dest形式的文件映射属性名就是目标文件,源文件就是它的值(源文件列表则使用数组格式声明)。可以使用这种方式指定数个src-dest文件映射, 但是不能够给每个映射指定附加的属性。

grunt.initConfig({
  concat: {
    foo: {
      files: {
        'dest/a.js': ['src/aa.js', 'src/aaa.js'],
        'dest/a1.js': ['src/aa1.js', 'src/aaa1.js'],
      },
    },
    bar: {
      files: {
        'dest/b.js': ['src/bb.js', 'src/bbb.js'],
        'dest/b1.js': ['src/bb1.js', 'src/bbb1.js'],
      },
    },
  },
});

文件数组格式

这种形式支持每个目标对应多个src-dest文件映射,同时也允许每个映射拥有额外属性:

grunt.initConfig({
  concat: {
    foo: {
      files: [
        {src: ['src/aa.js', 'src/aaa.js'], dest: 'dest/a.js'},
        {src: ['src/aa1.js', 'src/aaa1.js'], dest: 'dest/a1.js'},
      ],
    },
    bar: {
      files: [
        {src: ['src/bb.js', 'src/bbb.js'], dest: 'dest/b/', nonull: true},
        {src: ['src/bb1.js', 'src/bbb1.js'], dest: 'dest/b1/', filter: 'isFile'},
      ],
    },
  },
});

自定义过滤函数

filter属性可以给你的目标文件提供一个更高级的详细帮助信息。只需要使用一个有效的fs.Stats 方法名。下面的配置仅仅清理一个与模式匹配的真实的文件:

grunt.initConfig({
  clean: {
    foo: {
      src: ['tmp/**/*'],
      filter: 'isFile',
    },
  },
});

或者创建你自己的filter函数,根据文件是否匹配来返回true或者false。下面的例子将仅仅清理一个空目录:

grunt.initConfig({
  clean: {
    foo: {
      src: ['tmp/**/*'],
      filter: function(filepath) {
        return (grunt.file.isDir(filepath) && require('fs').readdirSync(filepath).length === 0);
      },
    },
  },
});

通配符模式

通常分别指定所有源文件路径是不切实际的,因此Grunt通过内置支持node-glob 和 minimatch 库来匹配文件名(又叫作globbing)。

然这并不是一个综合的匹配模式方面的教程,你只需要知道如何在文件路径匹配过程中使用它们即可:

  • * 匹配任意数量的字符,但不匹配 /
  • ? 匹配单个字符,但不匹配 /
  • ** 匹配任意数量的字符,包括 /,只要它是路径中唯一的一部分
  • {} 允许使用一个逗号分割的“或”表达式列表
  • ! 在模式的开头用于排除一个匹配模式所匹配的任何文件

每个人都需要知道的是:foo/*.js将匹配位于foo/目录下的所有的.js结尾的文件;而foo/**/*js将匹配foo/目录以及其子目录中所有以.js结尾的文件。

// 指定单个文件:
{src: 'foo/this.js', dest: ...}
// 指定一个文件数组:
{src: ['foo/this.js', 'foo/that.js', 'foo/the-other.js'], dest: ...}
// 使用一个匹配模式:
{src: 'foo/th*.js', dest: ...}

// 一个独立的node-glob模式:
{src: 'foo/{a,b}*.js', dest: ...}
// 也可以这样编写:
{src: ['foo/a*.js', 'foo/b*.js'], dest: ...}

// foo目录中所有的.js文件,按字母顺序排序:
{src: ['foo/*.js'], dest: ...}
// 首先是bar.js,接着是剩下的.js文件,并按字母顺序排序:
{src: ['foo/bar.js', 'foo/*.js'], dest: ...}

// 除bar.js之外的所有的.js文件,按字母顺序排序:
{src: ['foo/*.js', '!foo/bar.js'], dest: ...}
// 按字母顺序排序的所有.js文件,但是bar.js在最后。
{src: ['foo/*.js', '!foo/bar.js', 'foo/bar.js'], dest: ...}

// 模板也可以用于文件路径或者匹配模式中:
{src: ['src/<%= basename %>.js'], dest: 'build/<%= basename %>.min.js'}
// 它们也可以引用在配置中定义的其他文件列表:
{src: ['foo/*.js', '<%= jshint.all.src %>'], dest: ...}

动态构建文件对象

当你希望处理大量的单个文件时,这里有一些附加的属性可以用来动态的构建一个文件列表。这些属性都可以用于Compact和Files Array文件映射格式。

expand 设置为true用于启用下面的选项:

  • cwd 所有src指定的匹配都将相对于此处指定的路径(但不包括此路径) 。
  • src 相对于cwd路径的匹配模式。
  • dest 目标文件路径前缀。
  • ext 对于生成的dest路径中所有实际存在文件,均使用这个属性值替换扩展名。
  • extDot 用于指定标记扩展名的英文点号的所在位置。可以赋值 'first' (扩展名从文件名中的第一个英文点号开始) 或 'last' (扩展名从最后一个英文点号开始),默认值为 'first' [添加于 0.4.3 版本]
  • flatten 从生成的dest路径中移除所有的路径部分。
  • rename 对每个匹配的src文件调用这个函数(在重命名后缀和移除路径之后)。dest和匹配的src路径将被作为参数传入,此函数应该返回一个新的dest值。 如果相同的dest返回不止一次,那么,每个返回此值的src来源都将被添加到一个数组中作为源列表。

在下面的例子中,uglify 任务中的static_mappings和dynamic_mappings两个目标具有相同的src-dest文件映射列表, 这是因为任务运行时Grunt会自动展开dynamic_mappings文件对象为4个单独的静态src-dest文件映射--假设这4个文件能够找到。

可以指定任意静态src-dest和动态的src-dest文件映射相互结合。

grunt.initConfig({
  uglify: {
    static_mappings: {
      // Because these src-dest file mappings are manually specified, every
      // time a new file is added or removed, the Gruntfile has to be updated.
      files: [
        {src: 'lib/a.js', dest: 'build/a.min.js'},
        {src: 'lib/b.js', dest: 'build/b.min.js'},
        {src: 'lib/subdir/c.js', dest: 'build/subdir/c.min.js'},
        {src: 'lib/subdir/d.js', dest: 'build/subdir/d.min.js'},
      ],
    },
    dynamic_mappings: {
      // Grunt will search for "**/*.js" under "lib/" when the "uglify" task
      // runs and build the appropriate src-dest file mappings then, so you
      // don't need to update the Gruntfile when files are added or removed.
      files: [
        {
          expand: true,     // Enable dynamic expansion.
          cwd: 'lib/',      // Src matches are relative to this path.
          src: ['**/*.js'], // Actual pattern(s) to match.
          dest: 'build/',   // Destination path prefix.
          ext: '.min.js',   // Dest filepaths will have this extension.
          extDot: 'first'   // Extensions in filenames begin after the first dot
        },
      ],
    },
  },
});

模板

使用<% %>分隔符指定的模板会在任务从它们的配置中读取相应的数据时将自动扩展扫描。模板会被递归的展开,直到配置中不再存在遗留的模板相关的信息(与模板匹配的)。

整个配置对象决定了属性上下文(模板中的属性)。此外,在模板中使用grunt以及它的方法都是有效的,例如: <%= grunt.template.today('yyyy-mm-dd') %>

  • <%= prop.subprop %> 将会自动展开配置信息中的prop.subprop的值,不管是什么类型。像这样的模板不仅可以用来引用字符串值,还可以引用数组或者其他对象类型的值。
  • <% %> 执行任意内联的JavaScript代码。对于控制流或者循环来说是非常有用的。

下面以concat任务配置为例,运行grunt concat:sample时将通过banner中的/* abcde /连同foo/.js+bar/.js+bar/.js匹配的所有文件来生成一个名为build/abcde.js的文件。

grunt.initConfig({
  concat: {
    sample: {
      options: {
        banner: '/* <%= baz %> */\n',   // '/* abcde */\n'
      },
      src: ['<%= qux %>', 'baz/*.js'],  // [['foo/*.js', 'bar/*.js'], 'baz/*.js']
      dest: 'build/<%= baz %>.js',      // 'build/abcde.js'
    },
  },
  //用于任务配置模板的任意属性
  foo: 'c',
  bar: 'b<%= foo %>d', // 'bcd'
  baz: 'a<%= bar %>e', // 'abcde'
  qux: ['foo/*.js', 'bar/*.js'],
});

导入外部数据

在下面的Gruntfile中,项目的元数据是从package.json文件中导入到Grunt配置中的,并且grunt-contrib-uglify 插件中的 uglify 任务被配置用于压缩一个源文件以及使用该元数据动态的生成一个banner注释。

Grunt有grunt.file.readJSON和grunt.file.readYAML两个方法分别用于引入JSON和YAML数据。

Gruntfile 实例

下面就针对一个 Gruntfile 案例做简单分析,也可以作为一个实例使用:

module.exports = function(grunt) {

  grunt.initConfig({
    jshint: {
      files: ['Gruntfile.js', 'src/**/*.js', 'test/**/*.js'],
      options: {
        globals: {
          jQuery: true
        }
      }
    },
    watch: {
      files: ['<%= jshint.files %>'],
      tasks: ['jshint']
    }
  });

  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-watch');

  grunt.registerTask('default', ['jshint']);

};

在页面底部是这个 Gruntfile 实例的完整内容,如果你按顺序阅读本文的话,可以跟随我们一步步分析这个文件中的每一部分。我们会用到以下5个 Grunt 插件:

第一部分是"wrapper" 函数,它包含了整个Grunt配置信息。

module.exports = function(grunt) {
}

在这个函数中,我们可以初始化 configuration 对象:

grunt.initConfig({
});

接下来可以从package.json 文件读入项目配置信息,并存入pkg 属性内。这样就可以让我们访问到package.json文件中列出的属性了,如下:

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

到目前为止我们就可以看到如下配置:

module.exports = function(grunt) {
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json')
  });
};

现在我们就可以为我们的每个任务来定义相应的配置(逐个定义我们为项目定义的任务配置),然后每个任务的配置对象作为Grunt配置对象(即grunt.initConfig({})所接受的配置对象)的属性,并且这个属性名称与任务名相同。因此"concat"任务就是我们的配置对象中的"concat"键(属性)。下面便是我的"concat"任务的配置对象。

concat: {
  options: {
    // 定义一个用于插入合并输出文件之间的字符
    separator: ';'
  },
  dist: {
    // 将要被合并的文件
    src: ['src/**/*.js'],
    // 合并后的JS文件的存放位置
    dest: 'dist/<%= pkg.name %>.js'
  }
}

注意我是如何引用JSON文件(也就是我们在配置对象顶部引入的配置文件)中的name属性的。这里我使用pkg.name来访问我们刚才引入并存储在pkg属性中的package.json文件信息,它会被解析为一个JavaScript对象。Grunt自带的有一个简单的模板引擎用于输出配置对象(这里是指package.json中的配置对象)属性值,这里我让concat任务将所有存在于src/目录下以.js结尾的文件合并起来,然后存储在dist目录中,并以项目名来命名。

现在我们来配置uglify插件,它的作用是压缩(minify)JavaScript文件:

uglify: {
  options: {
    // 此处定义的banner注释将插入到输出文件的顶部
    banner: '/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> */\n'
  },
  dist: {
    files: {
      'dist/<%= pkg.name %>.min.js': ['<%= concat.dist.dest %>']
    }
  }
}

这里我们让uglifydist/目录中创建了一个包含压缩结果的JavaScript文件。注意这里我使用了<%= concat.dist.dest>,因此uglify会自动压缩concat任务中生成的文件。

QUnit插件的设置非常简单。 你只需要给它提供用于测试运行的文件的位置,注意这里的QUnit是运行在HTML文件上的。

qunit: {
  files: ['test/**/*.html']
},

JSHint插件的配置也很简单:

jshint: {
  // define the files to lint
  files: ['gruntfile.js', 'src/**/*.js', 'test/**/*.js'],
  // configure JSHint (documented at http://www.jshint.com/docs/)
  options: {
      // more options here if you want to override JSHint defaults
    globals: {
      jQuery: true,
      console: true,
      module: true
    }
  }
}

JSHint只需要一个文件数组(也就是你需要检测的文件数组), 然后是一个options对象(这个对象用于重写JSHint提供的默认检测规则)。你可以到JSHint官方文档站点中查看完整的文档。如果你乐于使用JSHint提供的默认配置,那么在Gruntfile中就不需要重新定义它们了.

最后,我们来看看watch插件:

watch: {
  files: ['<%= jshint.files %>'],
  tasks: ['jshint', 'qunit']
}

你可以在命令行使用grunt watch来运行这个任务。当它检测到任何你所指定的文件(在这里我使用了JSHint任务中需要检测的相同的文件)发生变化时,它就会按照你所指定的顺序执行指定的任务(在这里我指定了jshint和qunit任务)。

最后, 我们还要加载所需要的Grunt插件。 它们应该已经全部通过npm安装好了。

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');

最后设置了一些task。最重要的是default任务:

// 在命令行上输入"grunt test",test task就会被执行。
grunt.registerTask('test', ['jshint', 'qunit']);

// 只需在命令行上输入"grunt",就会执行default task
grunt.registerTask('default', ['jshint', 'qunit', 'concat', 'uglify']);

下面便是最终完成的 Gruntfile 文件:

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: {
        //这里是覆盖JSHint默认配置的选项
        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']);

};

创建任务

任务是Grunt的面包和奶油。就像你常用的工具,如: jshintnodeunit。每当运行Grunt时, 你可以为其指定一个或多个任务, 这些任务用于告诉Grunt你想要它做什么事情。

如果你没有指定一个任务,并且你已经定义一个名为 "default" 的任务,那么该任务将会默认被执行(不用诧异,总要做点儿什么啊!)。

任务别名

如果指定了一个任务列表,新任务将是这一个或多个指定任务的别名。当运行此 "任务别名" 时,在 taskList 中指定的每个任务都会按照其出现的顺序依次执行。taskList参数必须时一个任务数组。

grunt.registerTask(taskName, [description, ] taskList)

下面的任务别名案例中定义了一个 'default' 任务,如果运行Grunt时没有指定任何任务,它将自动执行'jshint'、'qunit'、'concat' 和 'uglify' 任务。

grunt.registerTask('default', ['jshint', 'qunit', 'concat', 'uglify']);

还可以给任务指定参数。在下面的案例中,别名 "dist" 将执行 "concat" 和 "uglify" 两个任务,并且它们都带有一个 "dist" 参数:

grunt.registerTask('dist', ['concat:dist', 'uglify:dist']);

多任务

当运行一个多任务时,Grunt会自动从项目的配置对象中查找同名属性。多任务可以有多个配置,并且可以使用任意命名的'targets'。

同时指定像grunt concat:foo或者grunt concat:bar这样的任务和目标,在运行时Grunt只会处理指定目标的配置;然而如果运行grunt concat,将会遍历所有的目标, 并按任务指定的顺序处理每个目标。注意,如果一个任务已经使用grunt.task.renameTask重命名过,Grunt将会自动在配置对象中查找新任务名称属性。

大部分的contrib任务(主要是指官方提供的任务),包括grunt-contrib-jshint插件的jshint任务,以及grunt-contrib-concat插件的concat任务都是多任务形式的。

grunt.registerMultiTask(taskName, [description, ] taskFunction)

对于指定的配置,这里有一个案例演示了如果通过grunt log:foo运行Grunt,它会输出foo: 1,2,3;如果通过grunt log:bar来运行Grunt, 它会输出bar: hello world。然而如果通过grunt log运行Grunt, 它会输出foo: 1,2,3,然后是bar: hello world,最后是baz: false(任务目标会按照指定的顺序进行处理)。

grunt.initConfig({
  log: {
    foo: [1, 2, 3],
    bar: 'hello world',
    baz: false
  }
});

grunt.registerMultiTask('log', 'Log stuff.', function() {
  grunt.log.writeln(this.target + ': ' + this.data);
});

"基本" 任务

当一个基本任务执行时,Grunt并不会检查配置和环境 -- 它仅仅执行指定的任务函数,并传递任何使用冒号分割的参数作为函数的参数。

grunt.registerTask(taskName, [description, ] taskFunction)

下面的案例中,如果执行 grunt foo:testing:123,将输出日志 foo, testing 123。 如果执行这个任务时不传递参数,只是执行 grunt foo,那么将输出日志 foo, no args

grunt.registerTask('foo', 'A sample task that logs stuff.', function(arg1, arg2) {
  if (arguments.length === 0) {
    grunt.log.writeln(this.name + ", no args");
  } else {
    grunt.log.writeln(this.name + ", " + arg1 + " " + arg2);
  }
});

自定义任务

你可以和任务一起疯狂。如果你的任务并没有遵循 "多任务" 结构,那就使用自定义任务。

grunt.registerTask('default', 'My "default" task description.', function() {
  grunt.log.writeln('Currently running the "default" task.');
});

在一个任务内部,你可以执行其他的任务。

grunt.registerTask('foo', 'My "foo" task.', function() {
  // Enqueue "bar" and "baz" tasks, to run after "foo" finishes, in-order.
  grunt.task.run('bar', 'baz');
  // Or:
  grunt.task.run(['bar', 'baz']);
});

任务也可以是异步的。

grunt.registerTask('asyncfoo', 'My "asyncfoo" task.', function() {
  // Force task into async mode and grab a handle to the "done" function.
  var done = this.async();
  // Run some sync stuff.
  grunt.log.writeln('Processing task...');
  // And some async stuff.
  setTimeout(function() {
    grunt.log.writeln('All done!');
    done();
  }, 1000);
});

任务也可以访问它们自身名称和参数。

grunt.registerTask('foo', 'My "foo" task.', function(a, b) {
  grunt.log.writeln(this.name, a, b);
});

// 用法:
// grunt foo foo:bar
//   logs: "foo", undefined, undefined
//   logs: "foo", "bar", undefined
// grunt foo:bar:baz
//   logs: "foo", "bar", "baz"

如果记录到任何错误,那么任务就会失败。

grunt.registerTask('foo', 'My "foo" task.', function() {
  if (failureOfSomeKind) {
    grunt.log.error('This is an error message.');
  }

  // 如果这个任务出现错误则返回false
  if (ifErrors) { return false; }

  grunt.log.writeln('This is the success message');
});

当任务失败时,所有后续任务都将终止,除非指定 --force

grunt.registerTask('foo', 'My "foo" task.', function() {
  // Fail synchronously.
  return false;
});

grunt.registerTask('bar', 'My "bar" task.', function() {
  var done = this.async();
  setTimeout(function() {
    // Fail asynchronously.
    done(false);
  }, 1000);
});

任务还可以依赖于其他任务的成功执行。注意 grunt.task.requires 并不会真正的运行其他任务,它仅仅检查其它任务是否已经执行,并且没有失败。

grunt.registerTask('foo', 'My "foo" task.', function() {
  return false;
});

grunt.registerTask('bar', 'My "bar" task.', function() {
  // 如果"foo"任务运行失败或者没有运行则任务失败。
  grunt.task.requires('foo');
  // 如果"foo"任务运行成功则执行这里的代码。
  grunt.log.writeln('Hello, world.');
});

// 用法:
// grunt foo bar
//   没有输出,因为"foo"失败。
// grunt bar
//   没有输出,因为"foo"从未运行。

如果任务需要的配置属性不存在,其也可能失败。

grunt.registerTask('foo', 'My "foo" task.', function() {
  // Fail task if "meta.name" config prop is missing
  // Format 1: String 
  grunt.config.requires('meta.name');
  // or Format 2: Array
  grunt.config.requires(['meta', 'name']);
  // Log... conditionally.
  grunt.log.writeln('This will only log if meta.name is defined in the config.');
});

任务还可以访问配置属性。

grunt.registerTask('foo', 'My "foo" task.', function() {
  // 记录属性值,如果属性未定义(undefined)则返回null。
  grunt.log.writeln('The meta.name property is: ' + grunt.config('meta.name'));
  // 同样的记录属性值,如果属性未定义(undefined)则返回null。
  grunt.log.writeln('The meta.name property is: ' + grunt.config(['meta', 'name']));
});

contrib tasks 中可以查看更多案例。

CLI 参数 / 环境

通过 process.env 来访问环境变量

请参考 使用命令行工具章节,查看完整的的命令行选项列表。

为什么我的异步task没有完成?

如果发生这种情况,可能是由于你忘记调用 this.async 方法来告诉Grunt你的任务是异步的。为了简单起见,Grunt使用同步的编码风格,可以在task体中通过调用 this.async() 将其转换为异步的。

注意,传递 falsedone() 函数就会告诉Grunt你的任务已经失败。

例如:

grunt.registerTask('asyncme', 'My asynchronous task.', function() {
  var done = this.async();
  doSomethingAsync(done);
});

额外参考资料

如果你需要更多参考资料来创建自己的 task ,请参考 API 文档 if you need extra reference to create your tasks.

使用命令行工具

安装命令行工具

执行 sudo npm install -g grunt-cli

grunt命令行接口提供了一系列选项。你可以在你的终端中使用grunt -h查看这个选项。

--help, -h

显示帮助信息。

--base, -b

指定一个基本路径。默认情况下,所有文件路径都是相对于Gruntfile的。

还可以调用 grunt.file.setBase(...)

--no-color

禁用彩色输出。

--gruntfile

指定 Gruntfile 文件。

默认情况下,grunt会从当前目录或者父目录中寻找最近的Gruntfile.js或者Gruntfile.coffee文件。

--debug, -d

对支持调试的任务启用调试模式。

--stack

因警告或者致命错误退出时打印堆栈跟踪信息。

--force, -f

一种强制跳过警告信息的方式。

如果像从警告中得到提示,就不要使用这个选项,可以根据提示信息修正代码。

--tasks

指定一个包含可加载的任务和“额外”文件的目录。

还可以调用 grunt.loadTasks(...)

--npm

在通过npm安装的插件中检查可加载的任何以及额外文件。

还可以调用 grunt.loadNpmTasks(...)

--no-write

禁用写文件操作(可用于演示)。

--verbose, -v

冗长模式(Verbose mode)。会输出很多的信息。

--version, -V

打印grunt版本。结合 --verbose 一起使用可以获取更多信息。

--completion

输出shell的自动补全规则。更多信息参考grunt-cli相关的文档。

转载于:https://www.cnblogs.com/stephenykk/p/5139667.html

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/dayanwu2842/article/details/101231734

智能推荐

解决windows下使用pycharm的anaconda安装numpy或tensorflow不成功的问题_anaconda 安装 skahare 不成功-程序员宅基地

文章浏览阅读770次。除了pycharm过期的问题,另外一个就是换源,这两个问题在我上一篇博客写过。现在又出现了无法安装pip库的问题了。试了很多方法,以为很大的问题,结果就是路径没添加导致的。使用anaconda可以很方便的使用其中的各种自带的库,而对于没有添加进去的,则需要从添加。当下载时就会遇到各种retry,,,各种ssl not available。其中一条提示是:pip is confi..._anaconda 安装 skahare 不成功

PHP中生成随机字符串,数字+大小写字母随机组合_php 随机字符串大小写字母加数组-程序员宅基地

文章浏览阅读1.7k次。简单的生成随机字符串: /* * 生成随机字符串 * * $length 字符串长度 */ function random_str($length) { // 密码字符集,可任意添加你需要的字符 $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMN..._php 随机字符串大小写字母加数组

自定义安装部署包_"在安装部署里,固定context[\"targetdir\"]"-程序员宅基地

文章浏览阅读869次。1:先建立一个类库项目2:类文件一个内容如下using System;using System.Collections;using System.ComponentModel;using System.Configuration.Install;using System.IO;using System.Diagnostics;using System.Win_"在安装部署里,固定context[\"targetdir\"]"

map与java对象相互转换_java map转对象-程序员宅基地

文章浏览阅读3.1w次,点赞13次,收藏79次。最近,研究map与java对象之间的相互转换,总结了5种方法:第一种:使用org.apache.commons.beanutils转换用到的主要jar包:commons-beanutils-1.9.3.jar//map转java对象public static Object mapToObject(Map&lt;String, Object&gt; map, Class&lt;?&g..._java map转对象

2020年G2电站锅炉司炉考试题库及G2电站锅炉司炉报名考试-程序员宅基地

文章浏览阅读361次。题库来源:安全生产模拟考试一点通公众号小程序2020年G2电站锅炉司炉考试题库及G2电站锅炉司炉报名考试,包含G2电站锅炉司炉考试题库答案和解析及G2电站锅炉司炉报名考试练习。由安全生产模拟考试一点通公众号结合国家G2电站锅炉司炉考试最新大纲及G2电站锅炉司炉考试真题汇总,有助于G2电站锅炉司炉多少钱考前练习。1、【判断题】特种设备作业人员是指操作、安装、维修及改造特种设备的人员,不包括特种设备管理人员。()(×)2、【判断题】燃料每小时输入炉膛的平均热量称为炉膛热负荷。()(√...

Win10系统中蓝牙串口功能启动方法_蓝牙串口 win10-程序员宅基地

文章浏览阅读8.2k次。1、背景本人主要从事嵌入式应用软件的开发工作,开发的产品多数为嵌入式设备软件,其中就包括仪器端需要通过蓝牙串口将测量数据导出的问题。在开发过程中,为了方便调试该功能,想到了PC端蓝牙串口功能。PC端启动蓝牙串口功能后,即可以方便的使用串口调试工具与第三方设备进行蓝牙串口通讯了。2、打开方法注:这里介绍的方法为仪器端为服务器端,PC端为客户端。所以首先需要将仪器端的蓝牙串口功能打开。然后等待客户端与之建立连接。1、打开仪器端蓝牙等待客户端与之配对连接。以我的开发设备为例,打开“蓝牙测量”界面。(在此_蓝牙串口 win10

随便推点

mysql数据库中常见错误提示及解决方法-程序员宅基地

文章浏览阅读465次。Quote: 开始=>所有程序=>附件=>命令提示符 输入 mysql 所在硬盘盘符 cd mysql 所在目录 cd bin 输入 myisamchk -f D:usr/local/mysql/data/bbs/PW_members.MYI ps : D:usr/local/mysql/data/bbs 是你论坛数据库的路径 -f ..._mysql 出现incorret index name rs_user怎么解决

nfs无法挂载:Loading: *** ERROR: Cannot mount_can't mount /mnt/p2-程序员宅基地

文章浏览阅读3.3k次。之前实验的时候通过nfs连接时仅仅会显示连接不上,在做实验六是还出现了 Loading: * ERROR: Cannot mount //仅仅提示挂载不上之前的博客里面就怀疑是nfs.c里面的timeout宏定义的问题查到有大佬也是这样解决的 修改了nfs.c之后重新编译U-BOOT,里面说是一开始不成功,后面成功, 但是我试了3456次还是挂载不上/(ㄒoㄒ)/_can't mount /mnt/p2

2021年安全员-A证-主要负责人(广东省)考试APP及安全员-A证-主要负责人(广东省)考试平台_广东安管a证客户端-程序员宅基地

文章浏览阅读450次。题库来源:安全生产模拟考试一点通公众号小程序2021年安全员-A证-主要负责人(广东省)考试APP为正在备考安全员-A证-主要负责人(广东省)操作证的学员准备的理论考试专题,每个月更新的安全员-A证-主要负责人(广东省)考试平台祝您顺利通过安全员-A证-主要负责人(广东省)考试。1、【判断题】对于发现的重大安全隐患,项目专职安全生产管理人员有权向企业安全生产管理机构报告。()(√)2、【判断题】风速大于8m/s时,应停止施工升降机安装作业。()(×)3、【判断题】公民、法..._广东安管a证客户端

【Earth Engine】基于GEE合成Landsat4/5/7/8/9影像并进行NDVI、NDWI和MNDWI等指数计算_landsat4—5tm影像波段组合-程序员宅基地

文章浏览阅读7.8k次,点赞30次,收藏119次。调用符合要求的所有Landsat系列影像合成,并计算各种基础指数。_landsat4—5tm影像波段组合

PostgreSQL之wal_keep_segments参数-程序员宅基地

文章浏览阅读6.8k次。wal_keep_segments:用于指定pg_wal目录中保存的过去的wal文件(wal 段)的最小数量,以防备用服务器在进行流复制时需要。(参数解释见:https://postgresqlco.nf/doc/zh/param/wal_keep_segments/)每个wal文件通常为16兆字节。如果连接到发送服务器的备用服务器滞后超过wal_keep_segment个文件,发送服务器可能会删除备用服务器仍然需要的WAL段,在这种情况下流复制连接将被终止。因此,下游连接最终也会失败。(但是,如果_wal_keep_segments

Filter过滤器登陆拦截_通过filter拦截/druid/login.html-程序员宅基地

文章浏览阅读837次。Filter过滤器Filter:过滤器,用来过滤网站的数据处理中文乱码登陆验证…使用过滤器实现登陆拦截,这样当我们点击注销后,登陆就无法直接进入结果页面了过滤器的使用导包servlet下的Filter包1 编写过滤器public class CharacterEncodingFilter implements Filter { @Override ..._通过filter拦截/druid/login.html