最近开始整理grunt相关知识,本文主要是作为指引文档,具体操作均引用其他文章。文中如有引用之处会注明。
grunt简介
grunt是什么?
Grunt 是一个基于任务的JavaScript工程命令行构建工具。
为什么要使用grunt?
一句话:自动化。对于需要反复重复的任务,例如压缩(minification)、编译、单元测试、linting等,自动化工具可以减轻你的劳动,简化你的工作。当你在 Gruntfile 文件正确配置好了任务,任务运行器就会自动帮你或你的小组完成大部分无聊的工作。
grunt使用前置准备
Grunt和 Grunt 插件是通过 npm 安装并管理的,npm是 Node.js 的包管理器,所以你需要先下载node.js。传送门
npm install -g grunt
这一步也可以省略,如省略需要在package.json中写明此依赖,下面会涉及到。
为了在命令行中使用grunt,你需要安装Grunt命令行(CLI)。
-g表示全局安装,部分系统需要sudo权限。
每次运行grunt 时,他就利用node提供的require()系统查找本地安装的 Grunt。正是由于这一机制,你可以在项目的任意子目录中运行grunt 。如果找到一份本地安装的 Grunt,CLI就将其加载,并传递Gruntfile中的配置信息,然后执行你所指定的任务。
使用grunt时,一般需要在你的项目中添加两份文件:package.json 和 Gruntfile。
package.json写法
用过node的人都知道项目根目录有个package.json文件,来描述项目基本信息(作者,项目名称,版本号等)和项目需要的各种模块。
使用grunt前需要将grunt和相关的插件安装到项目目录中,在根目录中写好package.json,将grunt和相关插件写在devDependencies一项中,并执行安装:
没错,不指定npm安装的模块时,npm会自动搜索本目录的package.json,然后自动安装里面的devDependencies和dependencies里注明的模块。
这一点阮一峰菊苣已经写的很详细了,不班门弄斧了。传送门
注意: 运行项目前需要到根目录执行
npm install
,要不然项目运行时会报错
Gruntfile写法
可参考http://www.hulufei.com/post/grunt-introduction
此文件被命名为 Gruntfile.js 或 Gruntfile.coffee,用来配置或定义任务(task)并加载Grunt插件的。 此文档中提到的 Gruntfile 其实说的是一个文件,文件名是 Gruntfile.js 或 Gruntfile.coffee。
grunt执行时会寻找项目里的Gruntfile文件。关于Gruntfile文件的命名,据实测和大小写关系不大,gruntfile, Gruntfile, GruntFile都是可以的,但按官网上的写法是Gruntfile。
Gruntfile由以下几部分构成:
- “wrapper” 函数
- 项目与任务配置
- 加载grunt插件和任务
- 自定义任务
比较常用的插件有:
- jshint:简单的js语法检查,具体配置后面会讲到
- uglify:压缩代码,网站优化方案之一
- concat:代码合并,网站优化方案之一
- watch:实时监控,当代码发生变动时自动重新编译,再也不用经常保存再刷新页面了
示例:
module.exports = function(grunt) {
// load tasks
[
'grunt-contrib-jshint',
'grunt-contrib-qunit',
'grunt-contrib-watch',
'grunt-contrib-clean',
'grunt-contrib-copy',
'grunt-contrib-concat',
'grunt-contrib-uglify',
'grunt-contrib-cssmin',
'grunt-contrib-concat',
'grunt-contrib-less',
'grunt-contrib-coffee',
'grunt-usemin',
'grunt-filerev'
].forEach(function(task) { grunt.loadNpmTasks(task); });
// setup init config
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
// clean up the `dist/` directory, i.e., delete files
clean: {
dist: {
src: [
'dist/*',
// funny dance to keep old versioned dist/css/*.pkg.*.css
'!dist/css/**',
'dist/css/*',
'!dist/css/*.pkg.*.css',
// funny dance to keep old versioned dist/css/*.pkg.*.js
'!dist/js/**',
'dist/js/*',
'!dist/js/*.pkg.*.js'
]
}
},
// copy over `src/` files to `dist/`
copy: {
dist: {
files: [{
expand: true,
dot: true,
cwd: 'src/',
dest: 'dist/',
src: [
'*',
'css/**',
'js/**',
'ico/**',
'img/**'
],
filter: 'isFile'
}]
}
},
// compile LESS files in `src/less/` into CSS files
less: {
css: {
options: {
paths: ["src/less"]
},
files: [
{
expand: true,
cwd: 'src/less',
src: ['*.less'],
dest: 'src/css/',
ext: '.css'
}
]
}
},
// compile coffeescript files in `/src/coffee/` into JS files
coffee: {
glob_to_multiple: {
expand: true,
// flatten: true,
cwd: 'src/coffee',
src: ['**/*.coffee'],
dest: 'src/js',
ext: '.js'
}
},
// prep call for usemin (target all html files)
useminPrepare: {
html: [
'dist/*.html'
]
},
// final call for usemin (target all html files)
usemin: {
html: [
'dist/*.html'
],
options: {
dirs: ['dist/']
}
},
// revision a specific set of static files, this can be
// extended to do more files and images too
filerev: {
files: {
src: [
'dist/css/*.pkg.css',
'dist/js/*.pkg.js'
]
}
},
// TODO - support qunit
qunit: {
files: ['test/**/*.html']
},
// validate JS files using jshint (great for catching simple bugs)
jshint: {
files: ['gruntfile.js', 'src/**/*.js', 'test/**/*.js'],
options: {
// options here to override JSHint defaults
globals: {
jQuery: true,
console: true,
module: true,
document: true
},
ignores: [
// enter paths to ignore here, e.g., 'src/js/jquery.js'
]
}
},
// watch command to auto-compile files that have changed
watch: {
coffee: {
files: ['src/**/*.coffee'],
tasks: ['coffee', 'jshint']
},
less: {
files: ['src/**/*.less'],
tasks: ['less']
}
}
});
// Composite tasks...
// run tests
grunt.registerTask('test', ['jshint', 'qunit']);
// like watch, but build stuff at start too!
grunt.registerTask('dev', ['less', 'coffee', 'watch']);
// full build of project to `dist/`
grunt.registerTask('default', ['less', 'coffee', 'jshint', 'clean', 'copy',
'useminPrepare',
'concat', 'uglify', 'cssmin',
'filerev',
'usemin']);
};
每个任务需要添加registerTask,registerTask函数第一个参数是任务名,第二个参数是执行的子任务,依次执行。
具体写法看官网文档会比较好一些。传送门
jshint语法检查
笔者写代码的时候,由于遵循的风格和jshint的默认规则不符,各种报错,让我很是不爽。工具变成绊脚石,也是挺痛苦的。所以,需要根据个人习惯去配置一下jshint,减少不必要的报错。
推荐一篇博文:JSHint 使用说明
在grunt中使用时,将文中的选项作为Gruntfile中jshint.options的key值,配置作为value就好。例如:
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
jshint: {
files: ['Gruntfile.js'],
options: {
//这里是覆盖JSHint默认配置的选项
globals: {
jQuery: true,
console: true,
module: true,
document: true
},
asi: true,
eqeqeq: false
}
}
});
grunt.loadNpmTasks('grunt-contrib-jshint');
};