Grunt入門教程(自動任務運行器),grunt入門教程
在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.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屬性的更多例子:
(2)grunt.loadNpmTasks
grunt.loadNpmTasks方法載入模組檔案。 (3)grunt.registerTask
grunt.registerTask方法定義如何調用具體的任務。“default”任務表示如果不提供參數,直接輸入grunt命令,則先運行 “cssmin:minify”,後運行“cssmin:combine”,即先壓縮再合并。如果只執行壓縮,或者只執行合并,則需要在grunt命令後 面指明“模組名:目標名”。
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,就表示該模組由gruntTeam Dev維護;如果首碼是grunt(比如grunt-pakmanager),就表示由第三方開發人員維護。
以下選幾個模組,看看它們配置參數的寫法,也就是說如何在grunt.initConfig方法中配置各個模組。
### grunt-contrib-jshint
jshint用來檢查語法錯誤,比如分號的使用是否正確、有沒有忘記寫括弧等等。它在grunt.initConfig方法裡面的配置代碼如下。
/.js'] },
上面代碼先指定jshint的檢查項目,eqeqeq表示要用嚴格相等運算子取代相等運算子,trailing表示行尾不得有多餘的空格。然後,指定files屬性,表示檢查目標是Gruntfile.js檔案,以及lib目錄的所有子目錄下面的JavaScript檔案。
### grunt-contrib-concat
concat用來合并同類檔案,它不僅可以合并JavaScript檔案,還可以合并CSS檔案。
.js', dest : ‘js/‘ } },
上面代碼中的options屬性指定壓縮後檔案的檔案頭,以及sourceMap設定;target目標指定輸入和輸出檔案。
### grunt-contrib-copy
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模組用來在後台運行,監聽指定事件,然後自動運行指定的任務。
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
該模組用於刪除檔案或目錄。
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>
{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: { / … / } } });
grunt # 預設情況下,先壓縮後合并 grunt cssmin:minify # 只壓縮不合并 grunt css:combine # 只合并不壓縮
如果不指明目標,只是指明模組,就表示將所有目標依次運行一遍。
grunt cssmin
## 常用模組設定
grunt的模組已經超過了2000個,且還在快速增加。下面是一些常用的模組(按字母排序)。
*
jshint: { options: { eqeqeq: true, trailing: true }, files: [‘Gruntfile.js', ‘lib/## 參考連結
- Frederic Hemberger, A build tool for front-end projects
- Mária Jurčovičová, Building a JavaScript Library with Grunt.js
- Ben Briggs,Speed Up Your Web Development Workflow with Grunt
- Optimizing Images With Grunt
- Swapnil Mishra, Simplifying Chores with Grunt
- AJ ONeal, Moving to GruntJS
- Grunt Documentation, Configuring tasks
- Landon Schropp, Writing an Awesome Build Script with Grunt
- Mike Cunsolo, Get Up And Running With Grunt
- Matt Bailey, A Beginner's Guide to Using Grunt With Magento