開始使用Grunt
大多數開發人員都一致認為,JavaScript開發的速度和節奏在過去的幾年里已經相當驚人。不管是Backbone.js和Ember.js的框架還是JS Bin社區,這種語言的發展變化不僅提高我們網站的用戶體驗,而且我們也在開始使用他們。
當您在使用JavaScript,您可能需要定期執行多個任務。雖然這在大多項目中都存在,但切實是一個耗時的和重復的工作方式。在這樣一個活躍的社區,你會假設工具都可以自動化,加快這個過程。此時就出現了Grunt。
什么是Grunt
建立在Node.js之上,Grunt是一個基於命令行的工具,用於加快工作流程,減少用於生前之前所做的准備。它可以結速工作與自動編譯一同進行。基本上,你可以使用Grunt的大部分任務來幫你處理你認為乏味的,通常需要手工配置和運行的工作。
雖然早期版本附帶了JSHint和Uglyify插件,但最近的版本(版本0.4)依賴於插件做一切事情。
什么樣的任務?這有一個詳細的列表。我只想說,Grunt可以處理大部你想處理的事情,從壓縮到加載JavaScript。他也可以用於與JavaScript無關的一系列任務,比如說將Sass和LESS編譯成CSS。我們甚至使用blink當構建失敗時通知我們。
為什么要使用Grunt
最爽的事情之一就是Grunt讓團隊做到一致性。如果你的工作是協作型的,你將要知道在代碼中不一致性多么令人煩惱的。Grunt工作使用團隊具有 統一的命令,從而確保團隊中的每個人都使用相同的標准來編寫代碼。畢竟,因為在團隊中如何編寫代碼這樣的小問題引起項目失敗更讓人感到沮喪。
Grunt也有一個令人難以置信的活躍社區,開發人員會在社區中定期發布新插件。進入的門檻相對較低,因為一個廣泛的工具和自動執行的任務就可以使用。
設置
使用Grunt的首要事情就是設置Node.js。(如果你對Node.js一無所知,別擔心,它只是安裝為Grunt能夠運行)。
一量你安裝好了Node.js,你可以終端命令中輸入:
$ npm install -g grunt-cli
為了確保Grunt已經正確安裝,你可以運行下面的命令:
$ grunt --version
接下來在你項目的根目中創建一個package.json
和一個Gruntfile.js
文件。
//測試項目(SampleGrunt)目錄結構
+SampleGrunt +----Gruntfile.js +----package.json
創建package.json文件
JSON文件使我們能跟蹤和安裝我們所有開發所依賴的信息。然后,對項目工作的人會擁有當前開發依賴性,最終有助於保持同步的開發環境。
在你項目根據下創建一個文件,並且包含下面的信息:
{
"name":"SampleGrunt", "version":"0.1.0", "author":"Brandon Random", "private":true, "devDependencies":{ "grunt":"~0.4.0" } }
一旦你這樣做了,運行下面的命令:
$ npm install
這告訴npm
所需的依賴關系,然后把它們安裝在node_modules
目錄中。
這個時候,你的項目的根目錄下會新增加一個node_modules
的目錄:
+SampleGrunt +----Gruntfile.js +----node_modules +----package.json
創建Gruntfile.js文件
Gruntfile.js
本質上是一個函數,而且他的參數是grunt
。
module.exports = function(grunt){ grunt.initConfig({ pkg:grunt.file.readJSON('package.json') }); grunt.registerTask('default',[]); };
你現在可以在你的項目根根目錄下運行Grunt命令。但是在這個階段你這樣做,你將會看到以下的警告信息:
$ grunt > Task "default" not found. Use --force to continue.
得到這樣的信息是因為我們除了Grunt之外沒有指定任何任務和依賴。所以,讓我們這樣做。但首先,讓我們看看如何擴展package.json
文件。
擴展package.json文件
最好是通過Node.js來處理,它可以找到安裝包並將他們安裝在一起,而且只基於包文件的內容。安裝所有新的依賴關系,只需要在文件中添加下面的內容:
{
"name":"SampleGrunt", "version":"0.1.0", "author":"Brandon Random", "private":true, "devDependencies":{ "grunt":"~0.4.0", "grunt-contrib-cssmin":"*", "grunt-contrib-sass":"*", "grunt-contrib-uglify":"*", "grunt-contrib-watch":"*", "grunt-cssc":"*", "grunt-htmlhint":"*", "matchdep":"*" } }
在命令行中輸入下面命令可以完成安裝流程:
$ npm install
執行完上面的命令后,所有的依賴關系都將安裝在node_modules
目錄中:
+SampleGrunt +----Gruntfile.js +----node_modules +++----css-condense +++----grunt +++----grunt-contrib-cssmin +++----grunt-contrib-sass +++----grunt-contrib-uglify +++----grunt-contrib-watch +++----grunt-cssc +++----grunt-htmlhint +++----matchdep +----package.json
在Grunt中加載npm任務
現在任務包已安裝好了,他們已經加載到Grunt中,我們現在能用他們做些什么。使用matchdep
我們可以使用一行代碼加載所有的任務。這是很好的一點,因為現在依賴關系的列表都只包括了包文件。
在頂部的Gruntfile.js
的grunt.initConfig
上面粘貼下面的一段代碼:
require("matchdep").filterDev("grunt-*").forEach(grunt.loadNpmTasks);
沒有matchdep
,我們需要為每個依賴關系寫grunt.loadNpmTasks('grunt-task-name')
,這將很快增加和安裝其他插件。
因為插件加載到Grunt中,我們有可能會指定開始選項。首先是HTML文件(index.html),包含下面的內容:
<!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驗證
在grunt.initConfig
中添加這樣的配置:
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'] } }
一個插件的配置通常是這樣的:插件的名稱(不帶grunt-contrib-/grunt-
前綴)然后是你選擇的一個或更多的目標(可以用於創建自定義插件的文件),一個options
對象,和這個文件效果。現在你在終端運行grunt htmlhint
,它會查看源文件,並確保我們的HTML沒有錯誤!然而,一小時手動輸入這個命令數次,將讓你的工作很快變得乏味。
在命令的終端執行grunt htmlhint
命令后,如果index.html
沒有出錯時,在終端會有信息提出:
Running "htmlhint:build" (htmlhint) task >> 0 files lint free. Running "htmlhint:src" (htmlhint) task >> 1 file lint free.
每次保存文件自動化運行任務
watch
任務可以根據被保存的文件運行一個獨特的任務。在grunt.initConfig
加入下面的代碼:
watch: { html: { files:['index.html'], tasks:['htmlhint'] } }
然后在終端運行grunt watch
。你會在終端看到下面的提示信息:
Running "watch" task
Waiting...
現在嘗試在index.html
中添加一個注釋。你會注意到,當文件被保存,驗證是自動執行的!你會在終端看到對應的信息:
Running "watch" task Waiting...OK >> File "index.html" changed. Running "htmlhint:build" (htmlhint) task >> 0 files lint free. Running "htmlhint:src" (htmlhint) task >> 1 file lint free. Done, without errors. Completed in 0.538s at Fri Nov 01 2013 22:54:03 GMT+0800 (CST) - Waiting...
這對於開發者來說是一個很好的東西,因為它意味着,你在編寫代碼的時候他會默默的進行驗證,如果代碼沒有通過驗證測試的時候他會失敗(和它會告訴你是什么問題引起的錯誤)。
注意,grunt watch
將會一直運行,直到關閉終端或者直到它停止(在Mac上執行control + c
可以直接停止grunt watch
命令)。
盡可能讓JavaScript文件小
讓我們創建一個JavaScript文件來驗證一個用戶名。盡可能我可以分簡單,我們將檢查的只是非字母字符。我們也會利用JavaScript的strict
模式,它阻止我們品尼高有效但質量差勁的JavaScript。把下面的代碼粘貼到assets/js/base.js
:
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
中加入下面代碼:
uglify: { build: { files: { 'build/js/base.min.js' : ['assets/js/base.js'] } } }
UglifyJS壓縮我們源文件中所有的變量和函數名,讓我們文件盡可能少的占用空間,然后剪去空白和注釋——非常適合用於生產的JavaScript。接着,我們需要為我們的Uglify的JavaScript創建一個watch
任務。將下面的代碼添加到watch
配置中:
watch: { js: { files: ['assets/js/base.js'], tasks: ['uglify'] } }
從Sass源文件創建CSS
Sass用來處理CSS是令人難以置信的有用,特別是在一個團隊。通常在源文件中寫更少的代碼,因為Sass可以使用函數和變量產生大的CSS代碼 塊。如何使用Sass本身有點超出了本文的范圍,所以,在這個階段你還不習慣和學習一個預處理器,你可以跳過這一節。我們將介紹一個非常簡單的用例,使用 具有變量和混合的SCSS,這非常類似於CSS!
Grunt的Sass插件需要sass gem
。你將需要在你的系統上安裝Ruby。你可以在你的終端使用下面的命令檢查你的系統是否安裝了Ruby:
ruby -v
如果你的系統已經安裝了Ruby,在終端執行上面的命令會得到如下所示的提示信息:
ruby 1.9.3p392 (2013-02-22 revision 39386) [x86_64-darwin12.4.0]
接下來運行下面的命令安裝Sass:
gem install sass
有關於Sass的更多教程,你可以點擊這里查閱。——大漠
根據您的配置,你可以需要通過sudo
命令來運行這個命令——例如sudo gem install sass
——這個時候終端會要求你輸入你的電腦登錄密碼。當Sass安裝好后,創建一個新的目錄叫作assets
,並在里面創建另一個叫sass
目錄。在這個目錄中創建一個新的文件名master.scss
,並將下面的代碼粘貼進行:
@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的兩個特性:混合(mixins)和變量(variables)。Mixins
就像一個函數,可以將一些參數傳遞給它,用來構造CSS塊,然后重用。
變量是特別有用的十六進制顏色,我們可以是構建一個調色板,可以在一個地方改變,非常快的調整整個方面設計。Mixin可以用於前綴等規則上,例如appearance
和transition
等屬性,它可以減少寫很多重復性代碼。
在處理大型樣式表時,當一個團隊的其他成員想要更新一個樣式,什么方法可以減少行數使用文件易於閱讀。
除了Sass,Grunt-cssc和CSS規則結合在一起,確保生成的CSS有最和的重復代碼。在中大型項目是有很多風格是重復的,這顯得非常的有用。然而,輸出的文件並不總是總小的。這樣就出現了cssmin
任務。它不僅可以去掉代碼中的空格,而且琮可以將顏色轉換成最短值(因此,white
將轉換成#fff
)。在Gruntfile.js
中加入下面的任務:
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' } } }
現在,我們有一些在適當的地方處理樣式表,這些任務也應該自動運行。build
目錄是Grunt自動創建的,用來放置腳本,CSS和(如果這是一個完整的網站)壓縮圖像。這意味着assets
目錄是用於放置開發的文件,里面文件內容有可能有大量的注釋或很多文件,然而build
目錄,是用來放置用於生產環境所需的文件,里面只留下了心樣能優化后的文件。
我們將定義一組新的任務來處理CSS。在gruntfile.js
中添加下面的代碼,下面是默認的task
:
grunt.registerTask('buildcss', ['sass', 'cssc', 'cssmin']);
現在,當運行grunt buildcss
,所有CSS任務將一個接一個執行。這遠比運行grunt sass
,接着grunt cssc
,接着grunt cssmin
更快。我們可以通過配置watch
做所有更新,自動運行。
watch: { css: { files: ['assets/sass/**/*.scss'], task: ['buildcss'] } }
在運行grunt buildcss
命令后,命令終端運行有關於CSS方面的任務,命令端將顯示:
Running "sass:build" (sass) task
Running "cssc:build" (cssc) task
File "build/css/master.css" created.
Running "cssmin:build" (cssmin) task
File build/css/master.css created.
Done, without errors.
同時,項目根目錄下將自動創建一個build
目錄,此時整個項目目錄結構如下:
+SampleGrunt +----assets +++----js +++----sass +----build +++----css +----Gruntfile.js +----node_modules +++----css-condense +++----grunt +++----grunt-contrib-cssmin +++----grunt-contrib-sass +++----grunt-contrib-uglify +++----grunt-contrib-watch +++----grunt-cssc +++----grunt-htmlhint +++----matchdep +----package.json
這路徑你看起來有點奇怪。基本上,它會依次檢查assets/sass
目錄下所有目錄中的.scss
文件,它充行我們創建盡可能多的Sass文件,而無需在gruntfile.js
中添加路徑。添加這個之后,gruntfile.js
將會像下面這樣:
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。正如前面所提到的,一個令人難以置信的活躍的開發者社區在不斷的提供前端插件。我的建議是,請到插件庫查閱這300多個插件。
譯者手語:整個翻譯依照原文線路進行,並在翻譯過程略加了個人對技術的理解。如果翻譯有不對之處,還煩請同行朋友指點。謝謝!
如需轉載,煩請注明出處:
英文原文:http://coding.smashingmagazine.com/2013/10/29/get-up-running-grunt/