[TOC]
在Javascript的開發過程中,經常會遇到一些重復性的任務,比如合并文件、壓縮代碼、檢查語法錯誤、將Sass代碼轉成CSS代碼等等。通常,我們需要使用不同的工具,來完成不同的任務,既重復勞動又非常耗時。Grunt就是為了解決這個問題而發明的工具,可以幫助我們自動管理和運行各種任務。
簡單說,Grunt是一個自動任務運行器,會按照預先設定的順序自動運行一系列的任務。這可以簡化工作流程,減輕重復性工作帶來的負擔。
## 安裝
Grunt基于Node.js,安裝之前要先安裝Node.js,然后運行下面的命令。
~~~
sudo npm install grunt-cli -g
~~~
grunt-cli表示安裝的是grunt的命令行界面,參數g表示全局安裝。
Grunt使用模塊結構,除了安裝命令行界面以外,還要根據需要安裝相應的模塊。這些模塊應該采用局部安裝,因為不同項目可能需要同一個模塊的不同版本。
首先,在項目的根目錄下,創建一個文本文件package.json,指定當前項目所需的模塊。下面就是一個例子。
~~~
{
"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子目錄。
~~~
npm install
~~~
上面這種方法是針對已有package.json的情況。如果想要自動生成package.json文件,可以使用npm init命令,按照屏幕提示回答所需模塊的名稱和版本即可。
~~~
npm init
~~~
如果已有的package.json文件不包括Grunt模塊,可以在直接安裝Grunt模塊的時候,加上--save-dev參數,該模塊就會自動被加入package.json文件。
~~~
npm install <module> --save-dev
~~~
比如,對應上面package.json文件指定的模塊,需要運行以下npm命令。
~~~
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
模塊安裝完以后,下一步在項目的根目錄下,新建腳本文件Gruntfile.js。它是grunt的配置文件,就好像package.json是npm的配置文件一樣。Gruntfile.js就是一般的Node.js模塊的寫法。
~~~
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代碼的三個方法:
* grunt.initConfig:定義各種模塊的參數,每一個成員項對應一個同名模塊。
* grunt.loadNpmTasks:加載完成任務所需的模塊。
* grunt.registerTask:定義具體的任務。第一個參數為任務名,第二個參數是一個數組,表示該任務需要依次使用的模塊。default任務名表示,如果直接輸入grunt命令,后面不跟任何參數,這時所調用的模塊(該例為jshint,concat和uglify);該例的check任務則表示使用jshint插件對代碼進行語法檢查。
上面的代碼一共加載了四個模塊:jshint(檢查語法錯誤)、concat(合并文件)、uglify(壓縮代碼)和watch(自動執行)。接下來,有兩種使用方法。
(1)命令行執行某個模塊,比如
~~~
grunt jshint
~~~
上面代碼表示運行jshint模塊。
(2)命令行執行某個任務。比如
~~~
grunt check
~~~
上面代碼表示運行check任務。如果運行成功,就會顯示“Done, without errors.”。
如果沒有給出任務名,只鍵入grunt,就表示執行默認的default任務。
## Gruntfile.js實例:grunt-contrib-cssmin模塊
下面通過cssmin模塊,演示如何編寫Gruntfile.js文件。cssmin模塊的作用是最小化CSS文件。
首先,在項目的根目錄下安裝該模塊。
~~~
npm install grunt-contrib-cssmin --save-dev
~~~
然后,新建文件Gruntfile.js。
~~~
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.loadNpmTasks
grunt.loadNpmTasks方法載入模塊文件。
~~~
grunt.loadNpmTasks('grunt-contrib-cssmin');
~~~
你需要使用幾個模塊,這里就要寫幾條grunt.loadNpmTasks語句,將各個模塊一一加載。
如果加載模塊很多,這部分會非常冗長。而且,還存在一個問題,就是凡是在這里加載的模塊,也同時出現在package.json文件中。如果使用npm命令卸載模塊以后,模塊會自動從package.json文件中消失,但是必須手動從Gruntfile.js文件中清除,這樣很不方便,一旦忘記,還會出現運行錯誤。這里有一個解決辦法,就是安裝load-grunt-tasks模塊,然后在Gruntfile.js文件中,用下面的語句替代所有的grunt.loadNpmTasks語句。
~~~
require('load-grunt-tasks')(grunt);
~~~
這條語句的作用是自動分析package.json文件,自動加載所找到的grunt模塊。
(2)grunt.initConfig
grunt.initConfig方法用于模塊配置,它接受一個對象作為參數。該對象的成員與使用的同名模塊一一對應。由于我們要配置的是cssmin模塊,所以里面有一個cssmin成員(屬性)。
cssmin(屬性)指向一個對象,該對象又包含多個成員。除了一些系統設定的成員(比如options),其他自定義的成員稱為目標(target)。一個模塊可以有多個目標(target),上面代碼里面,cssmin模塊共有兩個目標,一個是“minify”,用于壓縮css文件;另一個是“combine”,用于將多個css文件合并一個文件。
每個目標的具體設置,需要參考該模板的文檔。就cssmin來講,minify目標的參數具體含義如下:
* expand:如果設為true,就表示下面文件名的占位符(即*號)都要擴展成具體的文件名。
* cwd:需要處理的文件(input)所在的目錄。
* src:表示需要處理的文件。如果采用數組形式,數組的每一項就是一個文件名,可以使用通配符。
* dest:表示處理后的文件名或所在目錄。
* ext:表示處理后的文件后綴名。
除了上面這些參數,還有一些參數也是grunt所有模塊通用的。
* filter:一個返回布爾值的函數,用于過濾文件名。只有返回值為true的文件,才會被grunt處理。
* dot:是否匹配以點號(.)開頭的系統文件。
* makeBase:如果設置為true,就只匹配文件路徑的最后一部分。比如,a?b可以匹配/xyz/123/acb,而不匹配/xyz/acb/123。
關于通配符,含義如下:
* *:匹配任意數量的字符,不包括/。
* ?:匹配單個字符,不包括/。
* **:匹配任意數量的字符,包括/。
* {}:允許使用逗號分隔的列表,表示“or”(或)關系。
* !:用于模式的開頭,表示只返回不匹配的情況。
比如,foo/*.js匹配foo目錄下面的文件名以.js結尾的文件,foo/**/*.js匹配foo目錄和它的所有子目錄下面的文件名以.js結尾的文件,!*.css表示匹配所有后綴名不為“.css”的文件。
使用通配符設置src屬性的更多例子:
~~~
{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參數的格式可以是一個對象,也可以是一個數組。
~~~
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屬性。
~~~
grunt.initConfig({
cssmin: {
options: { /* ... */ },
minify: { /* ... */ },
combine: { /* ... */ }
}
});
~~~
(3)grunt.registerTask
grunt.registerTask方法定義如何調用具體的任務。“default”任務表示如果不提供參數,直接輸入grunt命令,則先運行“cssmin:minify”,后運行“cssmin:combine”,即先壓縮再合并。如果只執行壓縮,或者只執行合并,則需要在grunt命令后面指明“模塊名:目標名”。
~~~
grunt # 默認情況下,先壓縮后合并
grunt cssmin:minify # 只壓縮不合并
grunt css:combine # 只合并不壓縮
~~~
如果不指明目標,只是指明模塊,就表示將所有目標依次運行一遍。
~~~
grunt cssmin
~~~
## 常用模塊設置
grunt的[模塊](http://gruntjs.com/plugins)已經超過了2000個,且還在快速增加。下面是一些常用的模塊(按字母排序)。
* grunt-contrib-clean:刪除文件。
* grunt-contrib-compass:使用compass編譯sass文件。
* grunt-contrib-concat:合并文件。
* grunt-contrib-copy:復制文件。
* grunt-contrib-cssmin:壓縮以及合并CSS文件。
* grunt-contrib-imagemin:圖像壓縮模塊。
* grunt-contrib-jshint:檢查JavaScript語法。
* grunt-contrib-uglify:壓縮以及合并JavaScript文件。
* grunt-contrib-watch:監視文件變動,做出相應動作。
模塊的前綴如果是grunt-contrib,就表示該模塊由grunt開發團隊維護;如果前綴是grunt(比如grunt-pakmanager),就表示由第三方開發者維護。
以下選幾個模塊,看看它們配置參數的寫法,也就是說如何在grunt.initConfig方法中配置各個模塊。
### grunt-contrib-jshint
jshint用來檢查語法錯誤,比如分號的使用是否正確、有沒有忘記寫括號等等。它在grunt.initConfig方法里面的配置代碼如下。
~~~
jshint: {
options: {
eqeqeq: true,
trailing: true
},
files: ['Gruntfile.js', 'lib/**/*.js']
},
~~~
上面代碼先指定jshint的[檢查項目](http://www.jshint.com/docs/options/),eqeqeq表示要用嚴格相等運算符取代相等運算符,trailing表示行尾不得有多余的空格。然后,指定files屬性,表示檢查目標是Gruntfile.js文件,以及lib目錄的所有子目錄下面的JavaScript文件。
### grunt-contrib-concat
concat用來合并同類文件,它不僅可以合并JavaScript文件,還可以合并CSS文件。
~~~
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)。
### grunt-contrib-uglify
uglify模塊用來壓縮代碼,減小文件體積。
~~~
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目標指定輸入和輸出文件。
### grunt-contrib-copy
[copy模塊](https://github.com/gruntjs/grunt-contrib-copy)用于復制文件與目錄。
~~~
copy: {
main: {
src: 'src/*',
dest: 'dest/',
},
},
~~~
上面代碼將src子目錄(只包含它下面的第一層文件和子目錄),拷貝到dest子目錄下面(即dest/src目錄)。如果要更準確控制拷貝行為,比如只拷貝文件、不拷貝目錄、不保持目錄結構,可以寫成下面這樣:
~~~
copy: {
main: {
expand: true,
cwd: 'src/',
src: '**',
dest: 'dest/',
flatten: true,
filter: 'isFile',
},
},
~~~
### grunt-contrib-watch
[watch模塊](https://github.com/gruntjs/grunt-contrib-watch)用來在后臺運行,監聽指定事件,然后自動運行指定的任務。
~~~
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插件](http://livereload.com/)。安裝后,livereload的默認端口為localhost:35729,但是也可以用livereload: 1337的形式重設端口(localhost:1337)。
### 其他模塊
下面是另外一些有用的模塊。
(1)grunt-contrib-clean
該模塊用于刪除文件或目錄。
~~~
clean: {
build: {
src: ["path/to/dir/one", "path/to/dir/two"]
}
}
~~~
(2)grunt-autoprefixer
該模塊用于為CSS語句加上瀏覽器前綴。
~~~
autoprefixer: {
build: {
expand: true,
cwd: 'build',
src: [ '**/*.css' ],
dest: 'build'
}
},
~~~
(3)grunt-contrib-connect
該模塊用于在本機運行一個Web Server。
~~~
connect: {
server: {
options: {
port: 4000,
base: 'build',
hostname: '*'
}
}
}
~~~
connect模塊會隨著grunt運行結束而結束,為了使它一直處于運行狀態,可以把它放在watch模塊之前運行。因為watch模塊需要手動中止,所以connect模塊也就會一直運行。
(4)grunt-htmlhint
該模塊用于檢查HTML語法。
~~~
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文件。
~~~
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文檔。
~~~
markdown: {
all: {
files: [
{
expand: true,
src: '*.md',
dest: 'docs/html/',
ext: '.html'
}
],
options: {
template: 'templates/index.html',
}
}
},
~~~
上面代碼指定將md后綴名的文件,轉為docs/html/目錄下的html文件。template屬性指定轉換時采用的模板,模板樣式如下。
~~~
<!DOCTYPE html>
<html>
<head>
<title>Document</title>
</head>
<body>
<div id="main" class="container">
<%=content%>
</div>
</body>
</html>
~~~
## 參考鏈接
* Frederic Hemberger,?[A build tool for front-end projects](http://frederic-hemberger.de/artikel/grunt-buildtool-for-frontend-projects/)
* Mária Jur?ovi?ová,?[Building a JavaScript Library with Grunt.js](http://flippinawesome.org/2013/07/01/building-a-javascript-library-with-grunt-js/)
* Ben Briggs,[Speed Up Your Web Development Workflow with Grunt](http://sixrevisions.com/javascript/grunt-tutorial-01/)
* [Optimizing Images With Grunt](http://blog.grayghostvisuals.com/grunt/image-optimization/)
* Swapnil Mishra,?[Simplifying Chores with Grunt](http://howtonode.org/c4e0f8565942d5e6df45fb78b12d19435543c236/simplifying-chores-with-grunt)
* AJ ONeal,?[Moving to GruntJS](http://blog.coolaj86.com/articles/moving-to-grunt.html)
* Grunt Documentation,?[Configuring tasks](http://gruntjs.com/configuring-tasks)
* Landon Schropp,?[Writing an Awesome Build Script with Grunt](http://www.sitepoint.com/writing-awesome-build-script-grunt/)
* Mike Cunsolo,?[Get Up And Running With Grunt](http://coding.smashingmagazine.com/2013/10/29/get-up-running-grunt/)
* Matt Bailey,?[A Beginner’s Guide to Using Grunt With Magento](http://www.gpmd.co.uk/blog/a-beginners-guide-to-using-grunt-with-magento/)
* Paul Bakaus,?[Supercharging your Gruntfile](http://www.html5rocks.com/en/tutorials/tooling/supercharging-your-gruntfile/)
- 第一章 導論
- 1.1 前言
- 1.2 為什么學習JavaScript?
- 1.3 JavaScript的歷史
- 第二章 基本語法
- 2.1 語法概述
- 2.2 數值
- 2.3 字符串
- 2.4 對象
- 2.5 數組
- 2.6 函數
- 2.7 運算符
- 2.8 數據類型轉換
- 2.9 錯誤處理機制
- 2.10 JavaScript 編程風格
- 第三章 標準庫
- 3.1 Object對象
- 3.2 Array 對象
- 3.3 包裝對象和Boolean對象
- 3.4 Number對象
- 3.5 String對象
- 3.6 Math對象
- 3.7 Date對象
- 3.8 RegExp對象
- 3.9 JSON對象
- 3.10 ArrayBuffer:類型化數組
- 第四章 面向對象編程
- 4.1 概述
- 4.2 封裝
- 4.3 繼承
- 4.4 模塊化編程
- 第五章 DOM
- 5.1 Node節點
- 5.2 document節點
- 5.3 Element對象
- 5.4 Text節點和DocumentFragment節點
- 5.5 Event對象
- 5.6 CSS操作
- 5.7 Mutation Observer
- 第六章 瀏覽器對象
- 6.1 瀏覽器的JavaScript引擎
- 6.2 定時器
- 6.3 window對象
- 6.4 history對象
- 6.5 Ajax
- 6.6 同域限制和window.postMessage方法
- 6.7 Web Storage:瀏覽器端數據儲存機制
- 6.8 IndexedDB:瀏覽器端數據庫
- 6.9 Web Notifications API
- 6.10 Performance API
- 6.11 移動設備API
- 第七章 HTML網頁的API
- 7.1 HTML網頁元素
- 7.2 Canvas API
- 7.3 SVG 圖像
- 7.4 表單
- 7.5 文件和二進制數據的操作
- 7.6 Web Worker
- 7.7 SSE:服務器發送事件
- 7.8 Page Visibility API
- 7.9 Fullscreen API:全屏操作
- 7.10 Web Speech
- 7.11 requestAnimationFrame
- 7.12 WebSocket
- 7.13 WebRTC
- 7.14 Web Components
- 第八章 開發工具
- 8.1 console對象
- 8.2 PhantomJS
- 8.3 Bower:客戶端庫管理工具
- 8.4 Grunt:任務自動管理工具
- 8.5 Gulp:任務自動管理工具
- 8.6 Browserify:瀏覽器加載Node.js模塊
- 8.7 RequireJS和AMD規范
- 8.8 Source Map
- 8.9 JavaScript 程序測試
- 第九章 JavaScript高級語法
- 9.1 Promise對象
- 9.2 有限狀態機
- 9.3 MVC框架與Backbone.js
- 9.4 嚴格模式
- 9.5 ECMAScript 6 介紹
- 附錄
- 10.1 JavaScript API列表
- 草稿一:函數庫
- 11.1 Underscore.js
- 11.2 Modernizr
- 11.3 Datejs
- 11.4 D3.js
- 11.5 設計模式
- 11.6 排序算法
- 草稿二:jQuery
- 12.1 jQuery概述
- 12.2 jQuery工具方法
- 12.3 jQuery插件開發
- 12.4 jQuery.Deferred對象
- 12.5 如何做到 jQuery-free?
- 草稿三:Node.js
- 13.1 Node.js 概述
- 13.2 CommonJS規范
- 13.3 package.json文件
- 13.4 npm模塊管理器
- 13.5 fs 模塊
- 13.6 Path模塊
- 13.7 process對象
- 13.8 Buffer對象
- 13.9 Events模塊
- 13.10 stream接口
- 13.11 Child Process模塊
- 13.12 Http模塊
- 13.13 assert 模塊
- 13.14 Cluster模塊
- 13.15 os模塊
- 13.16 Net模塊和DNS模塊
- 13.17 Express框架
- 13.18 Koa 框架