當時學習 Grunt 的時候,真是很頭疼。分了兩個時間段,學習了兩次才硬啃下來,以後才能用在項目中。主要緣由我認爲是學習資料和文檔上面寫的過高端了。這類的文檔或者資料有個顯著特色,上來先簡單介紹一下這個玩意(Grunt 是一個 JavaScript 任務運行器),而後就是如何安裝,直接給你配置文件的語法,如何使用插件,新手每每看完還不知因此然。css

 

就像我第一次學習的時候,只是大致知道 Grunt 很火,你們都在用,但耐着心看文檔和一些別人的學習總結,仍是困惑,這究竟是個什麼東西?究竟幹什麼用?爲何要這麼麻煩配置這些東西?html

到如今應用在項目中也有一小段時間了,稍微有一點點經驗,好吧,我決定寫一篇即使是新手也能立刻看懂的文章。前端

用途和使用場景

先不要管 Grunt,咱們先來看下微硬公司小明和小紅的平常前端開發工做:node

小明在開發一個 JS 插件,寫了好多代碼,終於寫完了,放在 HTML 文件裏調用一看,console 裏面好多 error,因而就挨個調整修改。爲了排除深層潛在問題,他還打開了 http://www.jshint.com/ 這個網站,把本身的代碼複製進去用 jshint 檢測了,結果發現了好多細節問題和不規範的地方,依次修改。最後要發佈了,他又打開了 http://tool.css-js.com/ 網站,把本身的代碼複製進去,使用 Uglify 來壓縮了一下,提供一個壓縮版本。而後上傳到了 Github 上託管。jquery

這時候,Github 忽然有一個 Issue,他看了一下,原來有個疏忽的地方,又進行了修改,而後打開 jsHint 和 Uglify 在線壓縮網站進行檢查和壓縮,再次發佈上去。git

於此同時,小紅在作一個活動頁面的前端重構工做。她打開了正在作的 HTML 頁面,因爲使用了 Sass,因此她打開了 Koala 在後臺幫她自動編譯成 CSS,可是每次保存一下,想要看到效果,仍是須要切換到瀏覽器,刷新一下。作交互處理的時候,她寫了一些 JS,爲了規範,也使用 jshint 檢查了一下。終於作完了,這時候作了一下最後的優化處理準備上線,她把 icons 在 Photoshop 中合併成了一張圖片,並在 CSS 的對應位置修改了一下,而後用在線壓縮工具把 CSS 和 JS 都壓縮了一遍,提交測試去了。github

這就是他倆的工做,日復一日年復一年,有一天,小明終於受夠了,朝小紅髮牢騷:哥要是再來回複製粘貼到網站上排錯壓縮,哥就是逗逼!小紅滿眼淚花的說,你想好辦法了,幫我解決一下自動刷新問題,我也受夠了。算法

因而小明工做都不作了,開始苦思冥想怎麼開發一個能夠自動把寫的代碼發送到遠程網站,讓他們檢查一下錯誤而且壓縮好,再反回來生成一個文件。npm

開發一個任務自動處理器

固然,小明很快就不那麼想了,由於依賴外部網站有不少意外因素,並且受限於網絡和網速,其次他們也沒提供有關接口來調用。可是他們都提供了一些算法之類的工具等。這樣能夠在本地調用這些工具,來完成這些操做,甚至連網絡也不須要。json

小明愉快的開始在筆記本上構思這個工具的開發方法和須要的功能:

首先我須要先開發一個工具,能夠調用這個工具對個人某個項目目錄裏面的項目文件作一些操做,好比壓縮、查錯、合併等。

若是要作成一個工具,可能不太好,或許別人還須要更多功能,可是我無法開發這麼多功能啊。要不我就作個框架把,而後每一個功能作成一個插件,好比壓縮插件、合併插件。若是有人須要在他的項目裏壓縮某個文件,他安裝一下我這個工具而後再安裝壓縮插件就行了。這樣有更多需求的人,能夠本身編寫功能插件,而後配合個人工具使用。

慢着,他安裝完了工具和插件以後,要怎麼來調用這個插件來處理項目文件?在程序界面上選擇文件,而後勾選選項?個人天,我就會寫點 JS,哪裏可能開發帶有界面的程序?慢着,用 JS ?他能夠在項目文件夾中編寫一個 JS 來設置任務啊!而後個人工具會讀取這個 JS,解析以後得到他要執行的任務(好比壓縮某某文件並更名成某某),而後調用插件完成任務。

太棒了。可是插件這麼多,放在項目裏確定很大,並且又是不相關代碼,要不等他發佈的時候自動刪除這些插件文件把?不行,若是他要發給別人,別人要繼續開發,還得從新安裝依次安裝這些插件,而後執行任務。那怎麼辦?要不我再用個文件記錄一下當前項目中安裝或者須要的插件把!這樣只須要把這個文件和 JS 任務文件放在項目目錄裏面,有須要的人,直接輸入一條命令安裝一下,而後馬上就能夠執行了。

我太聰明瞭!

贊

因而小明興奮的跑過去給小紅講了一下他的工具的開發思路,而後告訴她,他能夠幫忙寫一個自動刷新的插件。小紅反而淡定的說:等等,我好像見過這種東西,這不是 Grunt 嘛?

小明抓緊搜了一下 Grunt,看了一下文檔以後,對着小紅:尼瑪,你爲何不早說!

開始學習 Grunt

Grunt 就是小明想的這樣一種自動化任務處理工具,它就是一個工具框架,有不少插件擴展它的功能。

Grunt 基於 Node.js ,用 JS 開發,這樣就能夠藉助 Node.js 實現跨系統跨平臺的桌面端的操做,例如文件操做等等。此外,Grunt 以及它的插件們,都做爲一個 包 ,能夠用 NPM 安裝進行管理。

因此 NPM 生成的 package.json 項目文件,裏面能夠記錄當前項目中用到的 Grunt 插件,而 Grunt 會調用 Gruntfile.js 這個文件,解析裏面的任務(task)並執行相應操做。

若是你對 Node.js、NPM 這些名詞不太熟悉,建議先去搜索瞭解一下,由於下面的命令會涉及到它們,可是本文不會過多介紹。

安裝 Grunt

Grunt 依賴 Node.js 因此在安裝以前確保你安裝了 Node.js。而後開始安裝 Grunt。

實際上,安裝的並非 Grunt,而是 Grunt-cli,也就是命令行的 Grunt,這樣你就可使用 grunt 命令來執行某個項目中的 Gruntfile.js 中定義的 task 。可是要注意,Grunt-cli 只是一個命令行工具,用來執行,而不是 Grunt 這個工具自己。

安裝 Grunt-cli 須要使用 NPM,使用下面一行便可在全局範圍安裝 Grunt-cli ,換句話說,就是你能夠在任何地方執行 grunt 命令:

npm install -g grunt-cli

須要注意,由於使用 -g 命令會安裝到全局,可能會涉及到系統敏感目錄,若是用 Windows 的話,可能須要你用管理員權限,若是用 OS X / Linux 的話,你可能須要加上 sudo 命令。

下面咱們新建一個項目目錄,並新建一些文件,這裏我準備了一份很簡單的項目,放在了 Github 上,下面操做將以此來操做,你能夠下載或者 clone 下來: https://github.com/yujiangshui/gruntxx

生成 package.json 文件

這個 package.json 文件實際上是 Node.js 來描述一個項目的文件,JSON 格式。生成這個文件超級簡單,推薦用命令行交互式的生成一下:

打開命令行,cd gruntxx 文件夾下面,輸入指令 npm init 以後,就出來不少信息,而後開始填寫項目名稱,填寫好了以後回車便可。其實這裏你一路回車下去也無妨,可是建議你細細的填一下,不明白的跳過好了。

npm init 填寫項目信息

填寫好了以後,查看目錄就會發現生成 package.json 文件了,這樣就算生成好了。

其實就是一個文件而已,你以爲這種方式麻煩,徹底能夠新建一個文件,而後將相似下面的代碼複製進去,改一下對應選項,保存成 package.json 文件就能夠:

{
  "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 和所須要的插件

就如今的這個示例項目而言,我打算讓 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 文件夾?打開看下,裏面就是我們剛安裝的插件。

配置 Gruntfile.js 的語法

插件也裝好了,開始寫任務吧!既然是要程序來讀取執行,必要要有必定的語法規範,下面來簡單的說一下:

首先要明白,這是一個 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 的參數好像不對,反正我以前學習的時候,無法運行這個配置,下面咱們來根據示例項目和咱們的需求配置一下。

配置 Gruntfile.js

先從簡單的入手,咱們先來配置一下編譯 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 語法正確,若是有問題就不會成功。

grunt 執行任務

下面咱們打算先把 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 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 會提示你出錯了。

grunt watch task

看到這裏,小明和小紅相擁而泣。。。。

項目文件傳輸與協做

項目開發完成以後,每每須要 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 而後就能夠直接執行那些命令。