編寫可維護的 Gruntfile.js

使用Grunt已經有很長一段時間了,不得不感嘆其社區的壯大,各類插件層出不窮。而在這期間我也換過幾種方式來組織Gruntfile.js,但都不是很理想,直到前段時間看到load-grunt-tasks這個插件以及More maintainable Gruntfiles這篇文章後,我就把項目中的Gruntfile.js都按照該文章做者所述的方式從新組織了一遍。javascript

我就暫且把這種方式用本身的文字記錄一下並分享給正在使用Grunt的同窗們吧,不過本文也不算是對《More maintainable Gruntfiles》的翻譯吶,畢竟我E文太差~html

load-grunt-tasks 插件

首先介紹下load-grunt-tasks這個插件。java

咱們通常都會把全部用到的插件以及插件的配置寫到Gruntfile.js裏面,對於小項目來講這個文件最終或許不是很大,可是對於大項目、有不少配置或者不少自定義任務的項目來講,最後這個文件都會變得愈來愈長,維護起來就成了麻煩。好比下面這樣:node

module.exports = function(grunt) {
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    concat: {
      options: {
        separator: ';'
      },
      dist: {
        src: ['src/**/*.js'],
        dest: 'dist/<%= pkg.name %>.js'
      }
    },
    uglify: {
      options: {
        banner: '/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> */\n'
      },
      dist: {
        files: {
          'dist/<%= pkg.name %>.min.js': ['<%= concat.dist.dest %>']
        }
      }
    },
    qunit: {
      files: ['test/**/*.html']
    },
    jshint: {
      files: ['gruntfile.js', 'src/**/*.js', 'test/**/*.js'],
      options: {
        globals: {
          jQuery: true,
          console: true,
          module: true,
          document: true
        }
      }
    },
    watch: {
      files: ['<%= jshint.files %>'],
      tasks: ['jshint', 'qunit']
    }
  });

  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-qunit');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-contrib-concat');

  grunt.registerTask('test', ['jshint', 'qunit']);
  grunt.registerTask('default', ['jshint', 'qunit', 'concat', 'uglify']);
};

這是一個很標準的Gruntfile.js,顯然也算是很簡短的了,可是看起來也有點累覺不愛。因而load-grunt-tasks出來幫咱們解決了一部分問題。git

它會自動讀取並加載項目packge.json文件中devDependencies配置下以grunt-*開頭的依賴庫。因而乎咱們就能夠用一行代碼來搞定上面代碼中不少行的loadNpmTasks了。github

require('load-grunt-tasks')(grunt);
// 就代替瞭如下所有
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-qunit');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-concat');

Gruntfile.js 繼續廋身

load-grunt-tasks插件替Gruntfile.js省去了那些反覆書寫的方法調用,接下來就是將整個Gruntfile.js變得乾淨清爽的步驟了。那就是把上面的各類config分離出去,讓它們各自表明本身是屬於哪一個插件,而不是一口氣全寫在一塊兒。固然,還有各類用registerTask方法定義的自定義任務,也該單獨放到相應的文件中。npm

自定義任務遷移

首先,在項目根目錄下建一個名爲tasks的目錄,在這個目錄下來編寫各類自定義任務。能夠一個任務一個 js 文件,也能夠多個簡單任務在一個 js 文件,看我的喜愛吧。而後在Gruntfile.js中用一行代碼來載入這些自定義任務:json

grunt.loadTasks('tasks');  // 即剛剛新建目錄的名稱

配置項遷移

而後再在這個目錄下新建一個名爲options的子目錄(tasks/options),來存放以前說的那些config們。爲每一類config建一個 js 文件,並以配置項節點名做爲文件名稱,好比下面這樣:函數

tasks
└── options
    └── concat.js
    └── uglify.js
    └── qunit.js
    └── jshint.js

而後在每一個文件中導出對應的配置項,拿concat.js來講:grunt

module.exports = exports = {
  options: {
    separator: ';'
  },
  dist: {
    src: ['src/**/*.js'],
    dest: 'dist/<%= pkg.name %>.js'
  }
};

最後在Gruntfile.js裏用require將配置逐個引入便可,也能夠封裝一個函數來作這件事情。

function loadConfig(configPath) {
  var config = {};

  glob.sync('*', { cwd: configPath })
    .forEach(function(configFile) {
      var prop = configFile.replace(/\.js$/, '');
      config[prop] = require(path.join(__dirname, configPath, configFile));
    });

  return config;
}

再改寫Gruntfile.jsinitConfig的調用便可。

var _ = require('lodash');
var config = {
  pkg: grunt.file.readJSON('package.json')
};
_.extend(config, loadConfig('./tasks/options/'));
grunt.initConfig(config);

寫在最後

因而乎在每一個項目中Gruntfile.js幾乎一致,並且也幾乎不會再變動。Gruntfile.js、自定義任務、任務配置項各司其職,須要變化時只需對相應文件作出調整便可。

就在前些天,又一位 GitHuber 將這個思路封裝成了一個庫:load-grunt-config,感興趣的同窗能夠看看。

最終的Gruntfile.js能夠查看這個例子:https://github.com/heroicyang/cnodeclub/blob/master/Gruntfile.js

參考資料

load-grunt-tasks: https://npmjs.org/package/load-grunt-tasks
More maintainable Gruntfiles: http://www.thomasboyt.com/2013/09/01/maintainable-grunt.html

相關文章
相關標籤/搜索