【转】自动化任务运行器 Grunt 迅速上手
原文转自:http://blog.jobbole.com/51586/
这篇文章将带领你用Grunt来提速和优化网站开发的流程。首先我们会简短介绍Grunt的功能,然后我们直接上手,介绍如何用Grunt的不同插件来替你完成网站项目开发中的很多繁冗工作。
接着我们会创建一个简单的input校验器,用 Sass 来完成CSS的预处理,我们会学习如何用grunt-cssc 和CssMin来合并和压缩CSS,如何用 HTMLHint 来保证我们的HTML正确无误,以及如何实现在运行时部署和压缩我们的文件。最后,我们会学习如何用UglifyJS 来极简化我们的JavaScript以尽可能地节约带宽。

Grunt.js 是一个JavaScript 任务运行工具,它能替你完成重复性的任务,如极简化、 编译、单元测试和linting。
Grunt入门
过去几年JavaScript的发展速度令人震惊,不管是像Backbone.js和Ember.js这样的开发框架,还是JS Bin这样的开发社区,这个语言的发展都不仅改变了我们作为用户对网站的体验,还改变了我们作为开发者对网站的开发方式。
使用JavaScript,你往往需要定期去执行一系列的任务, 在大部分项目里人们可能对此已习以为常了,但它们仍然是重复的、浪费时间的活计。身处一个如此活跃的开发社区,你大概已经猜到,有现成的工具可以帮你自动化和加速完成这些任务了——这就是Grunt的用武之地。
Grunt 是什么?
Grunt基于Node.js之上,是一个以任务处理为基础的命令行工具,可以减少优化开发版本为发布版本所需的人力和时间,从而加速开发流程。它的工作原理是把这些工作整合为不同的任务,在你开发时自动执行。基本上,你可以让Grunt完成任何让你厌烦的任务:那些你需要重复进行的手工设置和运行发布的任务。
早期版本的Grunt自带JSHint和Uglify等plugin,最新的版本(version 0.4)则完全依赖用户指定plugin来运行任务。到底有哪些任务可以运行呢?这个单子可是长得很,可以说,Grunt能干任何你扔给它的活,从极简化(minifying) 到合并JavaScript (concatenating)。它还可以完成一些跟JavaScript无关的任务,比如从LESS和Sass编译CSS。我们甚至还用过它跟 blink(1) 来做编译失败的提醒。
为什么要用Grunt ?
Grunt最大的优势之一是给团队带来一致性。如果你和别人一起工作过,你肯定知道代码风格的不一样有多让人伤神。Grunt能让团队使用一套统一的命令,从而保证每个人写的代码符合统一标准。说到底,如果因为团队中几个人代码风格的微小不同而导致编译失败,那可是最烦人的事了。
Grunt还有一个极其活跃的开发者社区,定期发布新的plugin。使用Grunt的门槛也相对较低,因为很多工具和自动化任务都是直接可用的。
设置安装
要使用Grunt,第一件事是安装Node.js。(即使你没用过Node.js也不用担心——你只需安装它让Grunt能运行。)
安装了Node.js之后,用命令行工具执行以下命令:
|
1
|
$ npm install -g grunt-cli |
要确认Grunt是否正确安装,可以使用以下命令:
|
1
|
$ grunt --version |
下一步是在你的项目的根目录下创建package.json和gruntfile.js两个文件。
创建package.json文件
这个JSON文件让我们指定我们的开发环境所依赖的必须模块。有了它,项目的所有开发者都能保证安装上一致的必须模块,从而保证所有人拥有一样的开发环境。
在项目根目录下的pacakge.json文件中写上:
|
1
2
3
4
5
6
7
8
9
10
|
{ "name" : "SampleGrunt", "version" : "0.1.0", "author" : "Brandon Random", "private" : true, "devDependencies" : { "grunt" : "~0.4.0" } } |
然后在命令行工具运行:
|
1
|
$ npm install |
该命令告诉npm 需要安装的必须模块,npm会安装它们,自动保存在项目根目录下一个叫做 node_modules 的文件夹里。
创建gruntfile.js文件
gruntfile.js 文件本质上就是一个wrapper函数,接受grunt作为参数:
|
1
2
3
4
5
6
7
8
9
|
module.exports = function(grunt){ grunt.initConfig({ pkg: grunt.file.readJSON('package.json') }); grunt.registerTask('default', []);}; |
现在你已经可以在项目根目录下运行grunt命令行工具了。
|
1
2
|
$ grunt> Task "default" not found. Use --force to continue. |
这里我们只指定了Grunt作为必须模块,还没定义任何任务。接下来我们就要指定任务和必须模块。首先来看如何拓展package.json文件。
拓展package.json文件
使用Node.js最好的一点,就是它可以根据package.json文件的内容,一次性查找和安装多个package。要安装我们项目的所有必须任务,只须在package.json文件中加上以下内容:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
{ "name" : "SampleGrunt", "version" : "0.1.0", "author" : "Mike Cunsolo", "private" : true, "devDependencies" : { "grunt" : "~0.4.0", "grunt-contrib-cssmin": "*", "grunt-contrib-sass": "*", "grunt-contrib-uglify": "*", "grunt-contrib-watch": "*", "grunt-cssc": "*", "grunt-htmlhint": "*", "matchdep": "*" }} |
那么如何实现安装?你肯定已经猜到了:
|
1
|
$ npm install |
使用Grunt载入任务
package安装好后,还必须被Grunt载入才能为我们所用。使用 matchdep,我们用一行代码就可以自动载入所有任务。这是开发流程的一大优化,因为现在我们只须把必须任务列表写在package.json一个文件里,便于管理。
在gruntfile.js里,grunt.initConfig之上,写上以下代码:
|
1
|
require("matchdep").filterDev("grunt-*").forEach(grunt.loadNpmTasks); |
要是没有matchdep,我们就必须为每一个必须任务写一次grunt.loadNpmTasks(“grunt-task-name”); ,随着我们使用的任务的增加,这些载入任务的代码很快就会变得相当繁冗。在Grunt载入这些任务前,我们还可以指定设置选项。
现在我们需要创建我们的HTML文件(index.html):
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
<!DOCTYPE html><html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0;"> <title>Enter your first name</title> <link rel="stylesheet" href="build/css/master.css"> </head> <body> <label for="firstname">Enter your first name</label> <input id="firstname" name="firstname" type="text"> <p id="namevalidation" class="validation"></p> <script type="text/javascript" src="build/js/base.min.js"></script> </body></html> |
用HTMLHint验证HTML
在grunt.initConfig里加入下列设置代码:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
htmlhint: { build: { options: { 'tag-pair': true, 'tagname-lowercase': true, 'attr-lowercase': true, 'attr-value-double-quotes': true, 'doctype-first': true, 'spec-char-escape': true, 'id-unique': true, 'head-script-disabled': true, 'style-disabled': true }, src: ['index.html'] }} |
一般来说,一个plugin的设置方法如下:plugin的名称(去掉grunt-contrib-或grunt-前缀),选择使用此plugin的一个或多个对象(在这里可以给不同文件设置此plugin 的不同选项),一个选项object,以及plugin要作用的对象。现在,如果我们用命令行工具运行grunt htmlhint,该plugin就会检查我们在src里指定的HTML文件,验证其中有没有错误!但是每个小时都要手动运行几次这个任务,很快就让人觉得很繁琐了。
自动化任务运行
watch是一个特殊的任务,它可以在目标文件保存时自动触发一系列任务的运行。在grunt.initConfig里加入以下设置:
|
1
2
3
4
5
6
|
watch: { html: { files: ['index.html'], tasks: ['htmlhint'] }} |
然后,在命令行工具中运行grunt watch命令。现在,你可以试试在index.html里加一行注释,保存文件。你会注意到,保存文件时会自动触发HTML的验证!这是对开发流程的一大优化:在你写代码时,watch任务就会默默同时为你验证代码,如果验证失败任务就会报告失败(它还会告诉你问题在哪)。
注意grunt watch任务会一直运行,直到命令行工具关闭,或手动停止(control+c在Mac中)。
保持JavaScript极简
让我们来写一个验证用户输入的名字的JavaScript文件。简便起见,我们这里只检查其中是否含有非字母的字符。我们的JavaScript会使用strict模式,这可以防止我们写可用但低质量的JavaScript。创建assets/js/base.js文件并在其中写上:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
function Validator(){ "use strict";}Validator.prototype.checkName = function(name){ "use strict"; return (/[^a-z]/i.test(name) === false);};window.addEventListener('load', function(){ "use strict"; document.getElementById('firstname').addEventListener('blur', function(){ var _this = this; var validator = new Validator(); var validation = document.getElementById('namevalidation'); if (validator.checkName(_this.value) === true) { validation.innerHTML = 'Looks good! :)'; validation.className = "validation yep"; _this.className = "yep"; } else { validation.innerHTML = 'Looks bad! :('; validation.className = "validation nope"; _this.className = "nope"; } });}); |
让我们用UglifyJS来极简化这个源代码,在grunt.initConfig中加上以下设置:
|
1
2
3
4
5
6
7
|
uglify: { build: { files: { 'build/js/base.min.js': ['assets/js/base.js'] } }} |
UglifyJS会替换所有的变量和函数名,剔除所有空白和注释,从而生成占据最小空间的JavaScript文件,对发布非常有用。同样地,我们需要设置一个watch任务来使用它,在watch的设置里加入以下代码:
|
1
2
3
4
5
6
|
watch: { js: { files: ['assets/js/base.js'], tasks: ['uglify'] }} |
从Sass源文件生成CSS
Sass对CSS相关工作非常有用,特别是在团队中。使用Sass可以大量减少代码量,因为Sass可通过变量、mixin函数生成CSS代码。Sass的具体使用方法并不在本教程探讨的范围内,所以如果你还不想使用Sass这样的CSS预处理器,可以跳过这一段。但我们这里会介绍一个很简单的用例,使用变量、一个mixin函数和Sass式CSS语法(SCSS)。
Grunt的Sass plugin需要使用Sass gem,为此你需要安装Ruby(OS X中已经预装了Ruby)。用以下命令你可以测试系统中是否已安装Ruby:
|
1
|
ruby -v |
使用以下命令安装Sass gem:
|
1
|
gem install sass |
根据系统设置的不同,你可能需要用sudo来运行此命令——即sudo gem install sass——这里你会被要求输入管理者密码。安装好Sass,在assets文件夹里创建sass文件夹,并在其中创建文件master.sass,然后写上:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
|
@mixin prefix($property, $value, $prefixes: webkit moz ms o spec) { @each $p in $prefixes { @if $p == spec { #{$property}: $value; } @else { -#{$p}-#{$property}: $value; } }}$input_field: #999;$input_focus: #559ab9;$validation_passed: #8aba56;$validation_failed: #ba5656;$bg_colour: #f4f4f4;$box_colour: #fff;$border_style: 1px solid;$border_radius: 4px;html { background: $bg_colour;}body { width: 720px; padding: 40px; margin: 80px auto; background: $box_colour; box-shadow: 0 1px 3px rgba(0, 0, 0, .1); border-radius: $border_radius; font-family: sans-serif;}input[type="text"] { @include prefix(appearance, none, webkit moz); @include prefix(transition, border .3s ease); border-radius: $border_radius; border: $border_style $input_field; width: 220px;}input[type="text"]:focus { border-color: $input_focus; outline: 0;}label,input[type="text"],.validation { line-height: 1; font-size: 1em; padding: 10px; display: inline; margin-right: 20px;}input.yep { border-color: $validation_passed;}input.nope { border-color: $validation_failed;}p.yep { color: $validation_passed; }p.nope { color: $validation_failed;} |
你会注意到SCSS比起普通的Sass更像CSS。这个样式表使用了Sass的两个特性:mixin和变量。一个mixin根据给它的参数生成CSS代码块,很像函数。而一个变量可以用来定义一段CSS代码片段,然后在很多地方重用。变量对定义Hex颜色尤其有用,我们可以用它建立一个色表,然后在尝试不同设计时,只须修改一处代码,从而大大提高了效率。这里的mixin则用来给CSS3的apperance和transition等属性生成前缀,减少了冗余代码。编写一个很长的样式表文件时,任何减少代码量的方法,都会让团队中日后更新此样式表的人受益。
在Sass之外,grunt-cssc任务可以整合CSS文件中定义的样式规则,最大限度削减所生成的CSS文件中的重复内容。在中到大型项目中经常出现重复的样式规则,使用这个任务就很有益处。但是,由此生成的CSS文件也不一定就是最简的,所以我们还需要使用cssmin任务,它既能剔除所有空白,还能把颜色值替换为可能的最简形式(比如white会被替换为#fff)。在gruntfile.js中加入如下内容:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
cssc: { build: { options: { consolidateViaDeclarations: true, consolidateViaSelectors: true, consolidateMediaQueries: true }, files: { 'build/css/master.css': 'build/css/master.css' } }},cssmin: { build: { src: 'build/css/master.css', dest: 'build/css/master.css' }},sass: { build: { files: { 'build/css/master.css': 'assets/sass/master.scss' } }} |
现在我们设置好了处理样式表的任务,还要让它们自动运行。Grunt自动创建了build文件夹来存放所有的发布用script、CSS和(如果这是一个完整的网站项目的话)压缩后的图片文件。这意味着assets文件夹里可以包含为开发而做的详细的注释文件甚至说明文档,而build文件夹里则只会包含极简化代码和优化压缩过的图像文件。
我们给CSS相关的工作定义一套新的任务,在gruntfile.js里的default task下面加上以下内容:
|
1
|
grunt.registerTask('buildcss', ['sass', 'cssc', 'cssmin']); |
现在,运行grunt buildcss任务就会按顺序运行所有CSS相关的任务,比起分别运行grunt sass、grunt cssc然后grunt cssmin来,这样简洁多了。最后我们需要做的就是更新watch任务的设置,让这些CSS相关任务也能自动运行:
|
1
2
3
4
5
6
|
watch: { css: { files: ['assets/sass/**/*.scss'], tasks: ['buildcss'] }} |
这个路径可能看起来有点奇怪,它的用途是递归地遍历我们assets/sass文件夹里的所有文件和子文件夹,来查找.scss文件。如此一来,我们就可以创建任意多的.scss文件,而不需要在gruntfile.js里添加它们的路径。现在,你的gruntfile.js应该是下面这样:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
|
module.exports = function(grunt){ "use strict"; require("matchdep").filterDev("grunt-*").forEach(grunt.loadNpmTasks); grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), cssc: { build: { options: { consolidateViaDeclarations: true, consolidateViaSelectors: true, consolidateMediaQueries: true }, files: { 'build/css/master.css': 'build/css/master.css' } } }, cssmin: { build: { src: 'build/css/master.css', dest: 'build/css/master.css' } }, sass: { build: { files: { 'build/css/master.css': 'assets/sass/master.scss' } } }, watch: { html: { files: ['index.html'], tasks: ['htmlhint'] }, js: { files: ['assets/js/base.js'], tasks: ['uglify'] }, css: { files: ['assets/sass/**/*.scss'], tasks: ['buildcss'] } }, htmlhint: { build: { options: { 'tag-pair': true, // Force tags to have a closing pair 'tagname-lowercase': true, // Force tags to be lowercase 'attr-lowercase': true, // Force attribute names to be lowercase e.g. <div id="header"> is invalid 'attr-value-double-quotes': true, // Force attributes to have double quotes rather than single 'doctype-first': true, // Force the DOCTYPE declaration to come first in the document 'spec-char-escape': true, // Force special characters to be escaped 'id-unique': true, // Prevent using the same ID multiple times in a document 'head-script-disabled': true, // Prevent script tags being loaded in the for performance reasons 'style-disabled': true // Prevent style tags. CSS should be loaded through }, src: ['index.html'] } }, uglify: { build: { files: { 'build/js/base.min.js': ['assets/js/base.js'] } } } }); grunt.registerTask('default', []); grunt.registerTask('buildcss', ['sass', 'cssc', 'cssmin']);}; |
现在我们有了一个静态HTML页面,一个存放Sass和JavaScript源文件的assets文件夹,一个存放优化后的CSS和JavaScript的build文件夹,以及package.json文件和gruntfile.js文件。
至此你已经有了一个不错的基础来进一步探索Grunt。像之前提到的,一个非常活跃的开发者社区在为Grunt开发前端plugin。我建议你现在就到plugin library 去看看那300个以上的plugin。
【转】自动化任务运行器 Grunt 迅速上手的更多相关文章
- JavaScript自动化构建工具入门----grunt、gulp、webpack
蛮荒时代的程序员: 做项目的时候,会有大量的js 大量的css 需要合并压缩,大量时间需要用到合并压缩 在前端开发中会出现很多重复性无意义的劳动 自动化时代的程序员: 希望一切都可以自动完成 ...
- PintJS – 轻量,并发的 GruntJS 运行器
PintJS 是一个小型.异步的 GruntJS 运行器,试图解决大规模构建流程中的一些问题. 典型的Gruntfile 会包括 jsHint,jasmine,LESS,handlebars, ugl ...
- java testng框架的windows自动化-自动运行testng程序上篇
本文旨在让读者简单了解testng的自动运行 怎么说呢,在网上已经有了各个前辈进行代码演示以及分享,我力争说到点子上 接上文,之前讲的大部分是juint的自动化代码运行,从未涉及到testng,但是在 ...
- 网页HTML代码在线运行器
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- 6.4hadoop idea本地运行器测试
1.1 本地运行器进行本地测试 写一个MapReduce驱动程序,执行job,实现tool接口,所以可以通过hadoop的命令行去设置为本地运行模式.实现tool的run函数,在run函数中创建jo ...
- 【转载】自动化魔方求解器的Bug——选择合适的色彩空间
目录 1. 准备工作-- 2. RGB颜色空间 3. LAB色彩空间 4. YCrCb色彩空间 5. HSV色彩空间 Color spaces in OpenCV (C++ / Python) 几天前 ...
- Grunt.js 上手
Official Site gruntjs.org/docs/getting-started.html 或者看http://tgideas.qq.com/webplat/info/news_versi ...
- python自动化之装饰器
1 高阶函数 满足下列条件之一就可成函数为高阶函数 某一函数当做参数传入另一个函数中 函数的返回值包含n个函数,n>0 高阶函数示范 def bar(): print 'in the bar' ...
- java的windows自动化-自动运行java程序
那么在一些工具齐全并且已经有了一定的写好的java程序的情况下(环境变量和软件见上一章http://www.cnblogs.com/xuezhezlr/p/7718273.html),如何自动化运行j ...
随机推荐
- 在Handler.ashx文件中使用session
使用jquery调用handler文件中的方法,需要使用session,默认生成的文件中,不可以直接使用session.按照以下步骤,即可以通过session与其他的aspx页面的session进行数 ...
- JS比较好用的一些方法搜集
JS比较好用的一些方法搜集 Math.ceil(x) -- 返回大于等于数字参数的最小整数(取整函数),对数字进行上舍入 Math.floor(x)--返回小于等于数字参数的最大整数,对数字进行下舍入 ...
- BZOJ3696 化合物
我们可以树形dp... 令f[p][d]表示以p为根的子树,与p距离为d的结点数 然后我们计算答案: 一种是从某个节点q到根p的方案,对和为d的贡献是1 另一种是p的一个子树中的节点x到另一个子树中的 ...
- 解决一个报表EdmFunction报错问题
最近测试组提了一个bug,说是某个报表点击查询报错,查看错误log,错误信息如下. 类型"Ticket.Data.SqlFuns"上指定的方法"Boolean C ...
- 模拟QQ侧滑控件 实现三种界面切换效果(知识点:回调机制,解析网络json数据,fragment用法等)。
需要用到的lib包 :解析json gson包,从网络地址解析json数据成String字符串的异步网络解析工具AsyncHttpClient等 下载地址:点击下载 Xlistview 下拉上拉第三 ...
- 免费提供一些公开的SOCK4/5/HTTP/HTTPS代理服务器(经测试可以用)
Last update IP address Port Country Type Anonymity 54 secs 116.228.55.217 8000 flag China HTTP High ...
- bzoj 2434: [Noi2011]阿狸的打字机
#include<cstdio> #include<iostream> #include<cstring> #define M 100008 using names ...
- ZOJ 3329 One Person Game 概率DP 期望 难度:2
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=3754 本题分数为0的概率不确定,所以不能从0这端出发. 设E[i]为到达成功所 ...
- $modal
$scope.open = function (size,data) {var modalInstance = $modal.open({ templateUrl: 'myModalContent.h ...
- [开发笔记]-Visual Studio 2012中为创建的类添加注释的模板
为类文件添加注释,可以让我们在写代码时能够方便的查看这个类文件是为了实现哪些功能而写的. 一:修改类文件模板 找到类模版的位置:C:\Program Files (x86)\Microsoft Vis ...