本节书摘来自异步社区《JavaScript开发框架权威指南》一书中的第2章,第2.3节,作者:【美】Tim Ambler , Nicholas Cloud著,更多章节内容可以访问云栖社区“异步社区”公众号查看
2.3 将Grunt添加到项目中
在本章前面,为了添加Grunt命令行工具,我们将npm包grunt-cli作为全局模块进行了安装。现在我们应该已经可以在命令行中使用Grunt命令,但是对于每个要使用Grunt的工程,仍然需要为其配置Grunt本地依赖。为此,只需在工程根目录下运行以下命令即可。本例假设npm已经针对示例所用项目进行了初始化,package.json文件也已经存在。
$ npm install grunt --save-dev
现在,我们项目的package.json文件应该已经包含与清单中的示例代码相仿的Grunt条目。
清单2-5 更新后的package.json文件
// example-tasks/package.json
{
"name": "example-tasks",
"version": "1.0.0",
"devDependencies": {
"grunt": "0.4.5"
}
}
将Grunt与项目进行整合的最后一个步骤是创建Gruntfile(见清单),并保存在工程根目录下。我们的Gruntfile只调用了一个函数loadTasks(),其作用将在接下来的小节进行讨论。
清单2-6 我们的Gruntfile
// example-tasks/Gruntfile.js
module.exports = function(grunt) {
grunt.loadTasks('tasks');
};
保持合理的Grunt模式
我们希望你在读完本章之后发现,Grunt的确是一件处理日常工作流程中遇到的乏味的重复性工作的利器。但是即便如此,还是不得不承认我们对Grunt的第一反应绝对算不上积极。事实上,刚开始我们对它兴趣不大。为了了解其中的原因,我们看一下放在Grunt官方文档显著位置的Gruntfile示例(见清单)。
清单2-7 Grunt官方文档提供的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: '/*! <%= 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']);
};
清单中略显臃肿的Gruntfile还是为相对简单的项目准备的。对于规模更大的项目,Gruntfile会像吹气球一样膨胀好几倍,最终结果就是一大堆根本无法阅读又难以维护的东西。有经验的开发者绝不会在写代码的时候,将不相关的功能模块揉在一起。否则,我们为什么还要在任务运行器上搞点儿花样出来呢?
正如清单中的代码所示,使Grunt模式保持合理的秘密就藏在Grunt的loadTasks()方法上。该例中task参数指向task文件夹,其路径以工程的Gruntfile为参考位置。loadTask()方法一被调用,Grunt就会加载并执行在该文件夹下发现的所有Node模块,并且每次都将Grunt对象的引用作为参数传进去。这样的行为使我们可以将项目的Grunt配置划分为一系列相互独立的模块,每个模块负责一个单独的任务或插件的加载及配置工作。清单展示了其中一个小型模块,模块中定义的任务可以通过在命令行中执行grunt uglify来运行。
清单2-8 task文件夹中的示例模块(uglify.js)
// example-tasks/tasks/uglify.js
module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.config('uglify', {
'options': {
'banner': '/*! <%= grunt.template.today("dd-mm-yyyy") %> */\n'
},
'dist': {
'files': {
'dist/app.min.js': ['src/index.js']
}
}
});
};