黑人的命也是命

示例 Gruntfile

在本页中,我们将引导您创建一个涵盖简单项目常见需求的 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']);

};

要求

每个项目都有自己的需求,但大多数项目都有一些共同点。在本指南中,我们将向您介绍一些 Grunt 插件来自动化基本需求。最终目标是教您如何配置这些 Grunt 插件,以便您可以在项目中使用它们。

为了举例说明,假设您正在创建一个 JavaScript 库。典型的文件夹结构包含以下文件夹:srcdisttestsrc 文件夹(有时称为 app)包含您编写的库的源代码。dist 文件夹(有时称为 build)包含发行版,即源代码的缩小版。缩小文件是指删除了所有不必要的字符(例如空格、换行符、注释)的文件,而不会影响源代码的功能。缩小后的源代码对项目的使用者特别有用,因为它减少了需要传输的数据量。最后,test 文件夹包含用于测试项目的代码。在下一节中创建 Gruntfile 配置时,将使用此设置。

在开发库和发布新版本时,您需要定期执行一些任务。例如,您可能希望确保您编写的代码符合最佳实践,或者您编写的代码不会导致意外行为。为此,您可以使用名为 JSHint 的工具。Grunt 有一个官方插件,名为 grunt-contrib-jshint,我们将在本例中采用它。特别是,您可能希望确保在修改代码时,不会破坏任何规则或最佳实践。因此,一个好的策略是在每次更改时检查代码。为此,我们将介绍一个名为 grunt-contrib-watch 的 Grunt 插件。每当添加、更改或删除文件时,后者都会运行预定义的任务,例如 grunt-contrib-jshint

检查源代码是否遵循最佳实践不足以保证其稳定且不包含错误。要创建一个健壮的项目,您需要对其进行测试。您可以采用多个库,例如 QUnitJasmine。在本指南中,我们将介绍如何配置 QUnit,特别是 grunt-contrib-qunit,来测试您的代码。

在分发您的工作成果时,您希望提供一个尽可能小的版本。要创建缩小版本,您需要一个 Grunt 插件,例如 grunt-contrib-uglify。此外,除非您正在开发的项目非常小,否则您很可能已将代码拆分到多个文件中。虽然这对开发人员来说是一个好习惯,但您希望用户只包含一个文件。因此,在缩小代码之前,您应该连接源文件以创建一个文件。为了实现此目标,您需要一个 Grunt 插件,例如 grunt-contrib-concat

总而言之,在本指南中,我们将使用以下五个 Grunt 插件

如果您对最终结果感到好奇,可以在本页底部找到完整的 Gruntfile

设置 Gruntfile

第一部分是“包装器”函数,它封装了您的 Grunt 配置。

module.exports = function(grunt) {
};

在该函数中,我们可以初始化配置对象

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-contrib-concat 的配置位于 concat 键下的配置对象中,如下所示

concat: {
  options: {
    // define a string to put between each file in the concatenated output
    separator: ';'
  },
  dist: {
    // the files to concatenate
    src: ['src/**/*.js'],
    // the location of the resulting JS file
    dest: 'dist/<%= pkg.name %>.js'
  }
}

请注意,在上面的代码段中,我们如何引用 JSON 文件中的 name 属性。我们通过使用 pkg.name 来访问它,因为之前我们将 pkg 属性定义为加载 package.json 文件的结果,然后将其解析为 JavaScript 对象。Grunt 有一个简单的模板引擎来输出配置对象中属性的值。在这里,我们告诉 concat 任务连接 src/ 中存在的所有以 .js 结尾的文件。

现在让我们配置 grunt-contrib-uglify 插件,它可以缩小 JavaScript 代码

uglify: {
  options: {
    // the banner is inserted at the top of the output
    banner: '/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> */\n'
  },
  dist: {
    files: {
      'dist/<%= pkg.name %>.min.js': ['<%= concat.dist.dest %>']
    }
  }
}

此代码段告诉 grunt-contrib-uglifydist/ 中创建一个文件,该文件包含缩小 JavaScript 文件的结果。在这里,我们使用 <%= concat.dist.dest %>,因此 uglify 将缩小 concat 任务生成的文件。

到目前为止,我们已经配置了插件来创建库的发行版本。现在是时候使用 grunt-contrib-qunit 来自动化代码测试了。为此,我们需要指定测试运行程序文件的位置,这些文件是 QUnit 运行的 HTML 文件。生成的代码如下所示

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

完成后,就可以设置配置以确保项目的代码符合最佳实践。JSHint 是一个可以检测问题或潜在问题的工具,例如高循环复杂度、使用相等运算符而不是严格相等运算符,以及定义未使用的变量和函数。

我们建议您使用 grunt-contrib-jshint 分析项目的所有 JavaScript 文件,包括 Gruntfile 和测试文件。grunt-contrib-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 网站上有记录。如果您对插件默认值感到满意,则无需在 Gruntfile 中重新定义它们。

最后一个要配置的插件是 grunt-contrib-watch。我们将使用它在添加、删除或修改 JavaScript 文件后立即运行 jshintqunit 任务。当它检测到指定的任何文件已更改时(此处,我们使用与告诉 JSHint 检查的文件相同的文件),它将按出现的顺序运行您指定的任务。这可以在命令行中使用 grunt watch 运行。

将前面的描述转换为 grunt-contrib-watch 的配置会生成以下代码段

watch: {
  files: ['<%= jshint.files %>'],
  tasks: ['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');

最后设置一些任务。这些任务中最重要的是默认任务

// this would be run by typing "grunt test" on the command line
grunt.registerTask('test', ['jshint', 'qunit']);

// the default task can be run just by typing "grunt" on the command line
grunt.registerTask('default', ['jshint', 'qunit', 'concat', 'uglify']);

当您调用 Grunt 而不指定要执行的任务(grunt)时,将执行默认任务。

生成的 Gruntfile

如果您已正确按照本指南进行操作,则应该具有以下 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: {
        // options here to override JSHint defaults
        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']);

};