grunt學習是我從網上看到的,感受不錯,也試驗過,便想整下來以便後期翻閱css
Grunt 依賴 Node.js 因此在安裝以前確保你安裝了 Node.js。而後開始安裝 Grunt。html
實際上,安裝的並非 Grunt,而是 Grunt-cli,也就是命令行的 Grunt,這樣你就可使用 grunt 命令來執行某個項目中的 Gruntfile.js 中定義的 task 。可是要注意,Grunt-cli 只是一個命令行工具,用來執行,而不是 Grunt 這個工具自己。node
安裝 Grunt-cli 須要使用 NPM,使用下面一行便可在全局範圍安裝 Grunt-cli ,換句話說,就是你能夠在任何地方執行 grunt 命令:jquery
npm install -g grunt-cli
須要注意,由於使用 -g 命令會安裝到全局,可能會涉及到系統敏感目錄,若是用 Windows 的話,可能須要你用管理員權限,若是用 OS X / Linux 的話,你可能須要加上 sudo 命令。git
下面咱們新建一個項目目錄,並新建一些文件,這裏我準備了一份很簡單的項目,放在了 Github 上,下面操做將以此來操做,你能夠下載或者 clone 下來: https://github.com/yujiangshui/gruntxxgithub
這個 package.json 文件實際上是 Node.js 來描述一個項目的文件,JSON 格式。生成這個文件超級簡單,推薦用命令行交互式的生成一下:npm
打開命令行,cd gruntxx
文件夾下面,輸入指令 npm init
以後,就出來不少信息,而後開始填寫項目名稱,填寫好了以後回車便可。其實這裏你一路回車下去也無妨,可是建議你細細的填一下,不明白的跳過好了。json
填寫好了以後,查看目錄就會發現生成 package.json 文件了,這樣就算生成好了。瀏覽器
其實就是一個文件而已,你以爲這種方式麻煩,徹底能夠新建一個文件,而後將相似下面的代碼複製進去,改一下對應選項,保存成 package.json 文件就能夠:sass
{ "name": "my-project-name", "version": "0.1.0", "devDependencies": { } }
最後我生成的代碼以下:
{ "name": "gruntxx", "version": "0.0.1", "description": "學習 grunt", "repository": { "type": "git", "url": "https://github.com/yujiangshui/gruntxx.git" }, "author": "Jiangshui", "license": "MIT", "bugs": { "url": "https://github.com/yujiangshui/gruntxx/issues" }, "homepage": "https://github.com/yujiangshui/gruntxx" }
但這時咱們尚未在項目文件中安裝 Grunt 以及相關任務插件。
就如今的這個示例項目而言,我打算讓 Grunt 幫忙實現下面幾個功能:檢查每一個 JS 文件語法、合併兩個 JS 文件、將合併後的 JS 文件壓縮、將 SCSS 文件編譯、新建一個本地服務器監聽文件變更自動刷新 HTML 文件。
差很少就是這些,根據這些任務需求,須要用到:
它們的命名和文檔都很規範,由於這些是官方提供的比較經常使用的插件。這些插件同時都是 NPM 管理的包,好比 grunt-contrib-concat - npm 你也能夠在這上面看到用法等。
下面咱們就要在這個項目中安裝這些插件,執行命令:
npm install grunt --save-dev
表示經過 npm 安裝了 grunt 到當前項目,同時加上了 –save-dev 參數,表示會把剛安裝的東西添加到 package.json 文件中。不信你打開 package.json 文件看下,是否是多了
"devDependencies": { "grunt": "^0.4.5" }
沒錯,這個的意思就是當前項目依賴 grunt,後面是它的版本,我們不用管。若是安裝的時候沒有添加 –save-dev 參數,這裏就不會出現了,你須要自行添加上去。
下面咱們來安裝 Grunt 的插件,固然,不須要一個個的安裝,太麻煩了,咱們能夠:
npm install --save-dev grunt-contrib-concat grunt-contrib-jshint grunt-contrib-sass grunt-contrib-uglify grunt-contrib-watch grunt-contrib-connect
等待一大串亂七八糟的下載狀態,咱們把 grunt 和相關插件都安裝好了,不信看下是否是多了一個 node_modules 文件夾?打開看下,裏面就是我們剛安裝的插件。
插件也裝好了,開始寫任務吧!既然是要程序來讀取執行,必要要有必定的語法規範,下面來簡單的說一下:
首先要明白,這是一個 JS 文件,你能夠寫任意的 JS 代碼,好比聲明一個 對象 來存儲一會要寫任務的參數,或者是一個變量看成開關等等。
而後,全部的代碼要包裹在
module.exports = function(grunt) { ... };
裏面。沒有爲何。
在這裏面的代碼,除去你本身寫的亂七八糟的 JS,與 Grunt 有關的主要有三塊代碼:任務配置代碼、插件加載代碼、任務註冊代碼。
顧名思義,這三塊代碼,任務配置代碼就是調用插件配置一下要執行的任務和實現的功能,插件加載代碼就是把須要用到的插件加載進來,任務註冊代碼就是註冊一個 task,裏面包含剛在前面編寫的任務配置代碼。
這樣,就能夠用 grunt 來執行註冊的一個 task 從而根據任務配置代碼調用須要的插件來執行相應的操做。
下面來分別看一下這三塊代碼的寫法。
例以下面代碼:
grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), uglify: { options: { banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n' }, build: { src: 'src/<%= pkg.name %>.js', dest: 'build/<%= pkg.name %>.min.js' } } });
能夠看出,具體的任務配置代碼以對象格式放在 grunt.initConfig
函數裏面,其中先寫了一句 pkg: grunt.file.readJSON('package.json')
功能是讀取 package.json 文件,並把裏面的信息獲取出來,方便在後面任務中應用(例以下面就用了 <%= pkg.name %>
來輸出項目名稱),這樣能夠提升靈活性。以後就是 uglify 對象,這個名字是固定的,表示下面任務是調用 uglify 插件的,首先先配置了一些全局的 options 而後新建了一個 build 任務。
也就是說,在 Uglify 插件下面,有一個 build 任務,內容是把 XX.js 壓縮輸出到 xx.min.js 裏面。若是你須要更多壓縮任務,也能夠參照 build 多寫幾個任務。
至於怎麼寫出來 options 裏面的參數和 build 裏面的參數內容,這纔是 grunt 學習的難點,你須要查看每一個插件的用法,根據用法來編寫任務,能夠看下 grunt-contrib-uglify 的官方文檔,往下面拉你就能夠看到參數和使用方法了。
這樣,咱們就新建了一個基於 uglify 的任務 build,功能是把 src/<%= pkg.name %>.js
壓縮輸出 build/<%= pkg.name %>.min.js
。
這個就超級簡單了,因爲上面任務須要用到 grunt-contrib-uglify,當 grunt-contrib-uglify 安裝到咱們的項目以後,寫下下面代碼便可加載:
grunt.loadNpmTasks('grunt-contrib-uglify');
插件也加載了,任務也佈置了,下面咱們得註冊一下任務,使用
grunt.registerTask('default', ['uglify']);
來註冊一個任務。上面代碼意思是,你在 default 上面註冊了一個 Uglify 任務,default 就是別名,它是默認的 task,當你在項目目錄執行 grunt 的時候,它會執行註冊到 default 上面的任務。
也就是說,當咱們執行 grunt 命令的時候,uglify 的全部代碼將會執行。咱們也能夠註冊別的 task,例如:
grunt.registerTask('compress', ['uglify:build']);
若是想要執行這個 task,咱們就不能只輸入 grunt 命令了,咱們須要輸入 grunt compress
命令來執行這條 task,而這條 task 的任務是 uglify 下面的 build 任務,也就是說,咱們只會執行 uglify 裏面 build 定義的任務,而不會執行 uglify 裏面定義的其餘任務。
這裏須要注意的是,task 的命名不能與後面的任務配置同名,也就是說這裏的 compress 不能命名成 uglify,這樣會報錯或者產生意外狀況
OK,加上這三塊代碼,咱們的示例 Gruntfile.js 就是這樣子的:
module.exports = function(grunt) { // Project configuration. grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), uglify: { options: { banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n' }, build: { src: 'src/<%= pkg.name %>.js', dest: 'build/<%= pkg.name %>.min.js' } } }); // Load the plugin that provides the "uglify" task. grunt.loadNpmTasks('grunt-contrib-uglify'); // Default task(s). grunt.registerTask('default', ['uglify']); };
這就是官方那個坑爹示例,貌似 uglify 的參數好像不對,反正我以前學習的時候,無法運行這個配置,下面咱們來根據示例項目和咱們的需求配置一下。
先從簡單的入手,咱們先來配置一下編譯 Scss 文件的 task。先新建一個 Gruntfile.js 文件,把大致的配置結構複製進去:
module.exports = function(grunt) { var sassStyle = 'expanded'; grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), sass: { } }); grunt.loadNpmTasks('grunt-contrib-sass'); grunt.registerTask('outputcss',['sass']); grunt.registerTask('default'); };
應該能夠看懂把?這裏再也不贅述了,咱們來根據 Sass 文檔,編寫一個 Sass 任務 output :
module.exports = function(grunt) { var sassStyle = 'expanded'; grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), sass: { output : { options: { style: sassStyle }, files: { './style.css': './scss/style.scss' } } } }); grunt.loadNpmTasks('grunt-contrib-sass'); grunt.registerTask('outputcss',['sass']); grunt.registerTask('default'); };
意思就是將 ./scss/style.scss
這個文件以 sassStyle 變量存儲的方式編譯成 根目錄下面的 style.css 文件。
下面拿起命令行,cd 到當前文檔目錄,執行一下 grunt 命令,結果報錯 undefined,沒錯,由於咱們的 default task 裏面沒有定義任何任務,而後執行 grunt outputcss
命令,提示編譯 Scss 文件成功,固然前提是你的 Scss 語法正確,若是有問題就不會成功。
下面咱們打算先把 src 目錄下面的兩個 JS 文件合併起來,而後再用 jshint 檢測一下是否有語法問題,若是正確,再用 uglify 對合並起來的文件進行壓縮。
參照 grunt-contrib-concat 的官方文檔,咱們能夠寫出下面的任務:
module.exports = function(grunt) { var sassStyle = 'expanded'; grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), sass: { output : { options: { style: sassStyle }, files: { './style.css': './scss/style.scss' } } }, concat: { options: { separator: ';', }, dist: { src: ['./src/plugin.js', './src/plugin2.js'], dest: './global.js', }, } }); grunt.loadNpmTasks('grunt-contrib-sass'); grunt.loadNpmTasks('grunt-contrib-concat'); grunt.registerTask('outputcss',['sass']); grunt.registerTask('concatjs',['concat']); grunt.registerTask('default'); };
執行 grunt concatjs
以後,就會發現根目錄多了一個 global.js 文件,裏面是兩個文件合併起來的。而後相似的繼續看 uglify 和 jshint 的文檔,咱們就能夠根據需求寫出下面任務:
module.exports = function(grunt) { var sassStyle = 'expanded'; grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), sass: { output : { options: { style: sassStyle }, files: { './style.css': './scss/style.scss' } } }, concat: { options: { separator: ';', }, dist: { src: ['./src/plugin.js', './src/plugin2.js'], dest: './global.js', }, }, uglify: { compressjs: { files: { './global.min.js': ['./global.js'] } } }, jshint: { all: ['./global.js'] } }); grunt.loadNpmTasks('grunt-contrib-sass'); grunt.loadNpmTasks('grunt-contrib-concat'); grunt.loadNpmTasks('grunt-contrib-jshint'); grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.registerTask('outputcss',['sass']); grunt.registerTask('concatjs',['concat']); grunt.registerTask('compressjs',['concat','jshint','uglify']); grunt.registerTask('default'); };
其中註冊了一個 compressjs 任務 grunt.registerTask('compressjs',['concat','jshint','uglify']);
意思就是依次執行 合併、檢查、壓縮 任務。咱們把剛生成的 global.js 文件刪掉,在命令行執行 grunt compressjs
任務,結果 jshint 報錯了:
grunt 遇到錯誤就退出了,就無法繼續執行下面的任務。經過錯誤提示能夠發現,是由於 concat 裏面設置的參數——在兩個文件合併間插入一個「;」——這原本是爲了防止兩個文件之間相互干擾設置的,結果沒法被 jshint 驗證經過,咱們能夠刪掉這個參數,或者設置 jshint 驗證這兩個文件,而後再進行合併。
爲了方便,我刪掉了這個參數。再執行一下,成功了,項目目錄裏面多了 global.js 和 global.min.js 文件。
小明看到這裏,痛哭流淚,本身每次打開好幾個網站,辛苦挨個粘貼複製新建,沒想到輸入一條命令就能夠了。不過讓他更傷心的還在後面,連這些命令都不用重複輸入。
咱們能夠經過 watch 來監聽文件變更,當文件變化了(咱們編寫保存了),自動執行某些任務。此處爲了節約版面,我連自動刷新的任務一塊寫上去。根據 grunt-contrib-watch 和 grunt-contrib-connect 這倆文檔,咱們能夠寫出下面的任務:
module.exports = function(grunt) { var sassStyle = 'expanded'; grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), sass: { output : { options: { style: sassStyle }, files: { './style.css': './scss/style.scss' } } }, concat: { dist: { src: ['./src/plugin.js', './src/plugin2.js'], dest: './global.js', }, }, uglify: { compressjs: { files: { './global.min.js': ['./global.js'] } } }, jshint: { all: ['./global.js'] }, watch: { scripts: { files: ['./src/plugin.js','./src/plugin2.js'], tasks: ['concat','jshint','uglify'] }, sass: { files: ['./scss/style.scss'], tasks: ['sass'] }, livereload: { options: { livereload: '<%= connect.options.livereload %>' }, files: [ 'index.html', 'style.css', 'js/global.min.js' ] } }, connect: { options: { port: 9000, open: true, livereload: 35729, // Change this to '0.0.0.0' to access the server from outside hostname: 'localhost' }, server: { options: { port: 9001, base: './' } } } }); grunt.loadNpmTasks('grunt-contrib-sass'); grunt.loadNpmTasks('grunt-contrib-concat'); grunt.loadNpmTasks('grunt-contrib-jshint'); grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-contrib-watch'); grunt.loadNpmTasks('grunt-contrib-connect'); grunt.registerTask('outputcss',['sass']); grunt.registerTask('concatjs',['concat']); grunt.registerTask('compressjs',['concat','jshint','uglify']); grunt.registerTask('watchit',['sass','concat','jshint','uglify','connect','watch']); grunt.registerTask('default'); };
添加了 connect 任務,用來新建一個本地服務器,以當前目錄做爲服務器根目錄,而後添加 watch 任務,監聽 Scss 文件變更,若是變了,執行一下 sass 任務,監聽那倆 JS,若是變了,執行 合併、檢查、壓縮 任務,監聽 html、css、js 文件,若是變更,livereload 自動刷新打開的頁面。
而註冊的 watchit task 就是咱們的終極 task,第一次執行,先編譯 sass、再合併、檢查、壓縮、開啓服務器、監聽文件變更。咱們執行一下 grunt watchit
就能夠看到效果了,你能夠修改一下 scss 文件,把字體設置大一點,切換到瀏覽器的時候,就看到了實時刷新效果。也能夠修改一下 JS,故意改錯一下,會發現 jshint 會提示你出錯了。
看到這裏,小明和小紅相擁而泣。。。。
項目開發完成以後,每每須要 push 到 Github 或者上傳 FTP 等。或許其餘人會接手你的項目繼續開發,或者你會換臺電腦進行開發。
當小明用 git 上傳 Github 的時候,傻了眼,項目裏 node_modules 文件夾下面的東西要十幾M呢,這比我項目自己還大,上傳下載都不方便。
其實這些插件和 grunt 不須要上傳,由於有 package.json 這個文件記錄了你這個項目中依賴的 grunt 插件,你只須要上傳這個文件便可。下載下來以後,只須要在這個項目文件夾下面,輸入命令 npm install
,NPM 會自動讀取 package.json 文件,將 grunt 和有關插件給你下載下來,很方便的。
也不須要在本地上傳的時候刪除,用 git 的話,可使用 .gitignore 文件來過濾掉這個文件夾,禁止 git 追蹤。
Grunt 就是這樣一種任務自動運行器,應用好它能夠減輕不少沒必要要的人工操做,只須要專一 coding 就能夠。甚至還有Grunt 插件幫你自動完成 CSS Sprite,更多功能還須要你本身去摸索。
新手看完本文,再看一下 Grunt 官方文檔 應該沒有太多疑問了,那就再看一遍把。固然也有中文版。
除了 Grunt 以外,同類型比較火的還有 Gulp 這個工具。其實兩個東西的功能是同樣的,只不過是任務配置 JS 的語法不一樣,Gulp 的 Gulpfile.js 的寫法更加通俗易懂,上手更快。可是 Gulp 的插件等感受不如 Grunt,Grunt 官方提供了一些常見的插件,知足大部分平常工做,並且可靠值得信賴,而 Gulp 好像沒有太多官方出品,各類插件不太規範。簡單的說,Grunt 和 Gulp 就像 iPhone 與 Android 同樣,一個質量高學習難一點,一個學起來簡單可是有點那個,你懂得。
此外,能夠看一些高手的項目,你會發現更好的 Grunt 用法,好比 Yeoman 生成的項目,就有很完善的 Grunt 任務和插件,此外,jQuery 等也用 Grunt 進行打包,這些 Grunt 文件你均可以查看研究一下他們的寫法和用法,受益不淺。
最後,若是你懶得跟着文章一點點的配置示例項目,你也能夠跳轉到示例項目的 grunt 分支,這裏面是我配置好的,你須要先 npm install
而後就能夠直接執行那些命令。