原文地址:http://www.smashingmagazine.com/2013/10/29/get-up-running-grunt/
這篇文章中,我們將介紹如何在項目使用Grunt讓你的網站更快。我們簡要分析下grunt能夠做什麼,如何創建和使用各種插件完成各種繁重的任務。我們將看看如何構建一個簡單的表單驗證器,使用Sass預處理器,如何使用grunt-cssc和CssMin結合壓縮我們的CSS,如何使用HTMLHint確保我們的HTML書寫正確,以及如何快速地構建我們的壓縮資源,最後,也會看看使用UglifyJS減小我們JavaScript的大小和確保我們的網站使用儘可能少的帶寬。
Grunt.js是一個JavaScript任務構建器,他可以幫助你執行重複性任務,比如壓縮,編譯,單元測試等。
開始使用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文件。
創建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的目錄:
創建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": "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": "*" } }
輸入以下命令:
npm install
在Grunt中加載npm任務
現在任務包已安裝好了,他們已經加載到Grunt中。使用matchdep我們可以使用一行代碼加載所有的任務。這是很好的一點,因爲現在依賴關係的列表都只包括了包文件。 在Gruntfile.js的頂部grunt.initConfig上面粘貼下面的一段代碼:
require("matchdep").filterDev("grunt-*").forEach(grunt.loadNpmTasks);
沒有matchdep,我們需要爲每個依賴關係寫grunt.loadNpmTasks('grunt-task-name'), matchdep提高了增加和安裝其他插件的速度。插件加載到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沒有錯誤!然而,一小時手動輸入這個命令數次,會讓你的工作很快變得乏味。
每次保存文件自動化運行任務
watch任務是一個可以根據被保存的文件運行獨特的任務。在grunt.initConfig加入下面的代碼:
watch: { html: { files: ['index.html'], tasks: ['htmlhint'] } }
現在,在終端中運行grunt watch。給index.html中添加一個註釋,就會發現當保存文件是,驗證的任務會自動執行。這對開發者來說是一個很好的工具,因爲當你寫代碼時watch會默默的執行,如果沒有通過相應的測試,就會報錯(還會告訴你錯誤的原因是什麼)。
注意 grunt watch會繼續運行除非你手動停止(Mac上使用control + C)
儘可能讓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(Mac 上已經預裝了)。你可以在你的終端使用下面的命令檢查你的系統是否安裝了Ruby:
ruby -v
通過命令安裝Sass
gem install 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'], tasks: ['buildcss'] } }
這路徑你看起來有點奇怪。基本上,它會依次檢查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多個插件。