這篇文章將帶領你用Grunt來提速和優化網站開發的流程。首先咱們會簡短介紹Grunt的功能,而後咱們直接上手,介紹如何用Grunt的不一樣插件來替你完成網站項目開發中的不少繁冗工做。javascript
接着咱們會建立一個簡單的input校驗器,用 Sass 來完成CSS的預處理,咱們會學習如何用grunt-cssc 和CssMin來合併和壓縮CSS,如何用 HTMLHint 來保證咱們的HTML正確無誤,以及如何實如今運行時部署和壓縮咱們的文件。最後,咱們會學習如何用UglifyJS 來極簡化咱們的JavaScript以儘量地節約帶寬。css
Grunt.js 是一個JavaScript 任務運行工具,它能替你完成重複性的任務,如極簡化、 編譯、單元測試和linting。html
Grunt入門前端
過去幾年JavaScript的發展速度使人震驚,不論是像Backbone.js和Ember.js這樣的開發框架,仍是JS Bin這樣的開發社區,這個語言的發展都不只改變了咱們做爲用戶對網站的體驗,還改變了咱們做爲開發者對網站的開發方式。java
使用JavaScript,你每每須要按期去執行一系列的任務, 在大部分項目里人們可能對此已習覺得常了,但它們仍然是重複的、浪費時間的活計。身處一個如此活躍的開發社區,你大概已經猜到,有現成的工具能夠幫你自動化和加速完成這些任務了——這就是Grunt的用武之地。node
Grunt 是什麼?git
Grunt基於Node.js之上,是一個以任務處理爲基礎的命令行工具,能夠減小優化開發版本爲發佈版本所需的人力和時間,從而加速開發流程。它的工做原理是把這些工做整合爲不一樣的任務,在你開發時自動執行。基本上,你可讓Grunt完成任何讓你厭煩的任務:那些你須要重複進行的手工設置和運行發佈的任務。github
早期版本的Grunt自帶JSHint和Uglify等plugin,最新的版本(version 0.4)則徹底依賴用戶指定plugin來運行任務。到底有哪些任務能夠運行呢?這個單子但是長得很,能夠說,Grunt能幹任何你扔給它的活,從極簡化(minifying) 到合併JavaScript (concatenating)。它還能夠完成一些跟JavaScript無關的任務,好比從LESS和Sass編譯CSS。咱們甚至還用過它跟 blink(1) 來作編譯失敗的提醒。web
爲何要用Grunt ?npm
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。
資料轉自:http://blog.jobbole.com/51586/