在Javascript的开发过程中,经常会遇到一些重复性的任务,比如合并文件、压缩代码、检查语法错误、将Sass代码转成CSS代码等等。通 常,我们需要使用不同的工具,来完成不同的任务,既重复劳动又非常耗时。Grunt就是为了解决这个问题而发明的工具,可以帮助我们自动管理和运行各种任 务。
简单说,Grunt是一个自动任务运行器,会按照预先设定的顺序自动运行一系列的任务。这可以简化工作流程,减轻重复性工作带来的负担。
Grunt基于Node.js,安装之前要先安装Node.js,然后运行下面的命令。
1
|
sudo npm install grunt-cli -g
|
grunt-cli表示安装的是grunt的命令行界面,参数g表示全局安装。
Grunt使用模块结构,除了安装命令行界面以外,还要根据需要安装相应的模块。这些模块应该采用局部安装,因为不同项目可能需要同一个模块的不同版本。
首先,在项目的根目录下,创建一个文本文件package.json,指定当前项目所需的模块。下面就是一个例子。
1
|
{
"name"
:
"my-project-name"
,
"version"
:
"0.1.0"
,
"author"
:
"Your Name"
,
"devDependencies"
: {
"grunt"
:
"0.x.x"
,
"grunt-contrib-jshint"
:
"*"
,
"grunt-contrib-concat"
:
"~0.1.1"
,
"grunt-contrib-uglify"
:
"~0.1.0"
,
"grunt-contrib-watch"
:
"~0.1.4"
} }
|
上面这个package.json文件中,除了注明项目的名称和版本以外,还在devDependencies属性中指定了项目依赖的grunt模 块和版本:grunt核心模块为最新的0.x.x版,jshint插件为最新版本,concat插件不低于0.1.1版,uglify插件不低于 0.1.0版,watch插件不低于0.1.4版。
然后,在项目的根目录下运行下面的命令,这些插件就会被自动安装在node_modules子目录。
1
|
npm install
|
上面这种方法是针对已有package.json的情况。如果想要自动生成package.json文件,可以使用npm init命令,按照屏幕提示回答所需模块的名称和版本即可。
1
|
npm init
|
如果已有的package.json文件不包括Grunt模块,可以在直接安装Grunt模块的时候,加上–save-dev参数,该模块就会自动被加入package.json文件。
1
|
npm install
|
比如,对应上面package.json文件指定的模块,需要运行以下npm命令。
1
|
npm install grunt --save-dev npm install grunt-contrib-jshint --save-dev npm install grunt-contrib-concat --save-dev npm install grunt-contrib-uglify --save-dev npm install grunt-contrib-watch --save-dev
|
模块安装完以后,下一步在项目的根目录下,新建脚本文件Gruntfile.js。它是grunt的配置文件,就好像package.json是npm的配置文件一样。Gruntfile.js就是一般的Node.js模块的写法。
1
|
module.exports =
function
(grunt) {
// 配置Grunt各种模块的参数 grunt.initConfig({ jshint: { /* jshint的参数 */ }, concat: { /* concat的参数 */ }, uglify: { /* uglify的参数 */ }, watch: { /* watch的参数 */ } }); // 从node_modules目录加载模块文件 grunt.loadNpmTasks('grunt-contrib-jshint'); grunt.loadNpmTasks('grunt-contrib-concat'); grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-contrib-watch'); // 每行registerTask定义一个任务 grunt.registerTask('default', ['jshint', 'concat', 'uglify']); grunt.registerTask('check', ['jshint']); };
|
上面的代码用到了grunt代码的三个方法:
上面的代码一共加载了四个模块:jshint(检查语法错误)、concat(合并文件)、uglify(压缩代码)和watch(自动执行)。接下来,有两种使用方法。
(1)命令行执行某个模块,比如
1
|
grunt jshint
|
上面代码表示运行jshint模块。
(2)命令行执行某个任务。比如
1
|
grunt check
|
上面代码表示运行check任务。如果运行成功,就会显示“Done, without errors.”。
如果没有给出任务名,只键入grunt,就表示执行默认的default任务。
下面通过cssmin模块,演示如何编写Gruntfile.js文件。cssmin模块的作用是最小化CSS文件。
首先,在项目的根目录下安装该模块。
1
|
npm install grunt-contrib-cssmin --save-dev
|
然后,新建文件Gruntfile.js。
1
|
module.exports =
function
(grunt) { grunt.initConfig({ cssmin: { minify: { expand:
true
, cwd:
'css/'
, src: [
'*.css'
,
'!*.min.css'
], dest:
'css/'
, ext:
'.min.css'
}, combine: { files: {
'css/out.min.css'
: [
'css/part1.min.css'
,
'css/part2.min.css'
] } } } }); grunt.loadNpmTasks(
'grunt-contrib-cssmin'
); grunt.registerTask(
'default'
, [
'cssmin:minify'
,
'cssmin:combine'
]); };
|
下面详细解释上面代码中的三个方法,下面一个个来看。
(1)grunt.initConfig
grunt.initConfig方法用于模块配置,它接受一个对象作为参数。该对象的成员与使用的同名模块一一对应。由于我们要配置的是cssmin模块,所以里面有一个cssmin成员(属性)。
cssmin(属性)指向一个对象,该对象又包含多个成员。除了一些系统设定的成员(比如options),其他自定义的成员称为目标 (target)。一个模块可以有多个目标(target),上面代码里面,cssmin模块共有两个目标,一个是“minify”,用于压缩css文 件;另一个是“combine”,用于将多个css文件合并一个文件。
每个目标的具体设置,需要参考该模板的文档。就cssmin来讲,minify目标的参数具体含义如下:
除了上面这些参数,还有一些参数也是grunt所有模块通用的。
关于通配符,含义如下:
比如,foo/*.js匹配foo目录下面的文件名以.js结尾的文件,foo/**/*.js匹配foo目录和它的所有子目录下面的文件名以.js结尾的文件,!*.css表示匹配所有后缀名不为“.css”的文件。
使用通配符设置src属性的更多例子:
1
|
{src:
'foo/th*.js'
}grunt-contrib-uglify {src:
'foo/{a,b}*.js'
} {src: [
'foo/a*.js'
,
'foo/b*.js'
]}
|
至于combine目标,就只有一个files参数,表示输出文件是css子目录下的out.min.css,输入文件则是css子目录下的part1.min.css和part2.min.css。
files参数的格式可以是一个对象,也可以是一个数组。
1
|
files: {
'dest/b.js'
: [
'src/bb.js'
,
'src/bbb.js'
],
'dest/b1.js'
: [
'src/bb1.js'
,
'src/bbb1.js'
], },
// or files: [ {src: ['src/aa.js', 'src/aaa.js'], dest: 'dest/a.js'}, {src: ['src/aa1.js', 'src/aaa1.js'], dest: 'dest/a1.js'}, ],
|
如果minify目标和combine目标的属性设置有重合的部分,可以另行定义一个与minify和combine平行的options属性。
1
|
grunt.initConfig({ cssmin: { options: {
/* ... */
}, minify: {
/* ... */
}, combine: {
/* ... */
} } });
|
(2)grunt.loadNpmTasks
grunt.loadNpmTasks方法载入模块文件。
(3)grunt.registerTask
grunt.registerTask方法定义如何调用具体的任务。“default”任务表示如果不提供参数,直接输入grunt命令,则先运行 “cssmin:minify”,后运行“cssmin:combine”,即先压缩再合并。如果只执行压缩,或者只执行合并,则需要在grunt命令后 面指明“模块名:目标名”。
1
|
grunt
# 默认情况下,先压缩后合并 grunt cssmin:minify # 只压缩不合并 grunt css:combine # 只合并不压缩
|
如果不指明目标,只是指明模块,就表示将所有目标依次运行一遍。
1
|
grunt cssmin
|
grunt的模块已经超过了2000个,且还在快速增加。下面是一些常用的模块(按字母排序)。
模块的前缀如果是grunt-contrib,就表示该模块由grunt开发团队维护;如果前缀是grunt(比如grunt-pakmanager),就表示由第三方开发者维护。
以下选几个模块,看看它们配置参数的写法,也就是说如何在grunt.initConfig方法中配置各个模块。
jshint用来检查语法错误,比如分号的使用是否正确、有没有忘记写括号等等。它在grunt.initConfig方法里面的配置代码如下。
1
|
jshint: { options: { eqeqeq:
true
, trailing:
true
}, files: [
'Gruntfile.js'
,
'lib/**/*.js'
] },
|
上面代码先指定jshint的检查项目,eqeqeq表示要用严格相等运算符取代相等运算符,trailing表示行尾不得有多余的空格。然后,指定files属性,表示检查目标是Gruntfile.js文件,以及lib目录的所有子目录下面的JavaScript文件。
concat用来合并同类文件,它不仅可以合并JavaScript文件,还可以合并CSS文件。
1
|
concat: { js: { src: [
'lib/module1.js'
,
'lib/module2.js'
,
'lib/plugin.js'
], dest:
'dist/script.js'
} css: { src: [
'style/normalize.css'
,
'style/base.css'
,
'style/theme.css'
], dest:
'dist/screen.css'
} },
|
js目标用于合并JavaScript文件,css目标用语合并CSS文件。两者的src属性指定需要合并的文件(input),dest属性指定输出的目标文件(output)。
uglify模块用来压缩代码,减小文件体积。
1
|
uglify: { options: { banner: bannerContent, sourceMapRoot:
'../'
, sourceMap:
'distrib/'
+name+
'.min.js.map'
, sourceMapUrl: name+
'.min.js.map'
}, target : { expand:
true
, cwd:
'js/origin'
, src :
'*.js'
, dest :
'js/'
} },
|
上面代码中的options属性指定压缩后文件的文件头,以及sourceMap设置;target目标指定输入和输出文件。
copy模块用于复制文件与目录。
1
|
copy: { main: { src:
'src/*'
, dest:
'dest/'
, }, },
|
上面代码将src子目录(只包含它下面的第一层文件和子目录),拷贝到dest子目录下面(即dest/src目录)。如果要更准确控制拷贝行为,比如只拷贝文件、不拷贝目录、不保持目录结构,可以写成下面这样:
1
|
copy: { main: { expand:
true
, cwd:
'src/'
, src:
'**'
, dest:
'dest/'
, flatten:
true
, filter:
'isFile'
, }, },
|
watch模块用来在后台运行,监听指定事件,然后自动运行指定的任务。
1
|
watch: { scripts: { files:
'**/*.js'
, tasks:
'jshint'
, options: { livereload:
true
, }, }, css: { files:
'**/*.sass'
, tasks: [
'sass'
], options: { livereload:
true
, }, }, },
|
设置好上面的代码,打开另一个进程,运行grunt watch。此后,任何的js代码变动,文件保存后就会自动运行jshint任务;任何sass文件变动,文件保存后就会自动运行sass任务。
需要注意的是,这两个任务的options参数之中,都设置了livereload,表示任务运行结束后,自动在浏览器中重载(reload)。这需要在浏览器中安装livereload插件。安装后,livereload的默认端口为localhost:35729,但是也可以用livereload: 1337的形式重设端口(localhost:1337)。
下面是另外一些有用的模块。
(1)grunt-contrib-clean
该模块用于删除文件或目录。
1
|
clean: { build: { src: [
"path/to/dir/one"
,
"path/to/dir/two"
] } }
|
(2)grunt-autoprefixer
该模块用于为CSS语句加上浏览器前缀。
1
|
autoprefixer: { build: { expand:
true
, cwd:
'build'
, src: [
'**/*.css'
], dest:
'build'
} },
|
(3)grunt-contrib-connect
该模块用于在本机运行一个Web Server。
1
|
connect: { server: { options: { port: 4000, base:
'build'
, hostname:
'*'
} } }
|
connect模块会随着grunt运行结束而结束,为了使它一直处于运行状态,可以把它放在watch模块之前运行。因为watch模块需要手动中止,所以connect模块也就会一直运行。
(4)grunt-htmlhint
该模块用于检查HTML语法。
1
|
htmlhint: { build: { options: {
'tag-pair'
:
true
,
'tagname-lowercase'
:
true
,
'attr-lowercase'
:
true
,
'attr-value-double-quotes'
:
true
,
'spec-char-escape'
:
true
,
'id-unique'
:
true
,
'head-script-disabled'
:
true
, }, src: [
'index.html'
] } }
|
上面代码用于检查index.html文件:HTML标记是否配对、标记名和属性名是否小写、属性值是否包括在双引号之中、特殊字符是否转义、HTML元素的id属性是否为唯一值、head部分是否没有script标记。
(5)grunt-contrib-sass模块
该模块用于将SASS文件转为CSS文件。
1
|
sass: { build: { options: { style:
'compressed'
}, files: {
'build/css/master.css'
:
'assets/sass/master.scss'
} } }
|
上面代码指定输出文件为build/css/master.css,输入文件为assets/sass/master.scss。
(6)grunt-markdown
该模块用于将markdown文档转为HTML文档。
1
|
markdown: { all: { files: [ { expand:
true
, src:
'*.md'
, dest:
'docs/html/'
, ext:
'.html'
} ], options: {
'templates/index.html'
, } } },
|
上面代码指定将md后缀名的文件,转为docs/html/目录下的html文件。template属性指定转换时采用的模板,模板样式如下。
1
|
|
转自:http://javascript.ruanyifeng.com/tool/grunt.html
同时推荐:http://www.w3cplus.com/tools/writing-awesome-build-script-grunt.html