组织你的 Grunt 任务

Avatar of Jason Witt
Jason Witt

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费额度!

将代码分解成更小的、易于管理的块的想法,创造了一个易于工作和维护的环境。这通常被认为是模块设计,并且是如今 Web 开发的标准。我将向您展示一种可以使用模块设计来更好地组织 Grunt 任务的方法。

我将假设您已经了解使用 Grunt 的基础知识。如果您不了解,这里有一篇 Chris 的文章可以帮助您入门:Grunt for People Who Think Things Like Grunt are Weird and Hard

如果您使用 Grunt,您可能习惯于看到您的 Gruntfile 如下所示。

module.exports = function(grunt) {
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    sass: {
      dist: {
        options: {
          style: 'expanded',
          sourcemap: 'none'
        },
        files: {
          'style.css': 'sass/global.scss',      
          'css/dev.style.css': 'sass/global.scss',
          'css/ie9.style.css': 'sass/ie9.scss',
        }
      }
    },
    postcss: {
      options: {
        processors: [
          require('autoprefixer')(),
          require('rucksack-css')({ fallbacks: true })
        ]
      },
      dist: {
        src: 'style.css',
        dest: 'style.css'
      },
      dev: {
        src: 'css/dev.style.css',
        dest: 'css/dev.style.css'
      },
    },
    cssmin: {
      target: {
        files: {
          'style.css': 'style.css'
        }
      }
    },
    concat: {
      dist: {
        src: [
          'js/lib/no-conflict.js',
          'js/lib/skip-navigation.js',
        ],
        dest: 'js/scripts.js'
      },
    },
    jshint: {
      files: [
        'js/scripts.js',
        'js/ie.js',
      ],
      options: {
        scripturl: true,
        globals: {
          jQuery: true
        }
      }
    },
    uglify: {
      options: {
        mangle: false,
        compress: true,
        quoteStyle: 3
      },
      dist: {
        files: {
          'js/head.min.js': 'js/head.js',
          'js/scripts.min.js': 'js/scripts.js',
          'js/ie.min.js'     : 'js/ie.js',
        }
      }
    },
    watch: {
      scripts: {
        files: ['js/**/*.js'],
        tasks: ['concat', 'uglify'],
        options: {
          spawn: false
        }
      },
      css: {
        files: ['sass/**/*.scss'],
        tasks: ['sass', 'postcss', 'cssmin']
      }
    },
  });
  grunt.loadNpmTasks('grunt-postcss');
  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-contrib-cssmin');
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-jsvalidate');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-contrib-sass');
  grunt.registerTask('default', ['watch']);
};

这是一个看起来很正常的 Gruntfile。实际上,这是一个相当小的 Gruntfile。我见过一些 Gruntfile 的大小是这个的三倍。查看那些大型的 Gruntfile 会让我头痛。模块设计已经深入我的脑海,以至于看到如此大的 Gruntfile 会让我分心。为什么我的 Gruntfile 不能看起来更像这样呢?

module.exports = function(grunt) {
  var tasks = {scope: ['devDependencies', 'dependencies' ]};
  var options = {config: { src: "grunt/*.js" }};
  var configs = require('load-grunt-configs')(grunt, options);
  require('load-grunt-tasks')(grunt, tasks);
  grunt.initConfig(configs);
  grunt.registerTask('default', ['watch']);
};

它可以!我将向您展示如何做到。

为了实现这一点,我们将安装两个 Grunt 包。

  1. load-grunt-tasks
  2. load-grunt-configs

继续,像安装任何其他 Grunt 包一样安装这些包。

$ npm install --save-dev load-grunt-tasks
$ npm install --save-dev load-grunt-configs

现在您的 `package.json` 文件应包含这两个包,如下所示。

{
  "name": "your-project",
  "version": "1.0.0",
  "description": "",
  "main": "Gruntfile.js",
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "grunt-contrib-cssmin": "^1.0.1",
    "load-grunt-configs": "^1.0.0",
    "load-grunt-tasks": "^3.5.0"
  }
}

为了本文的目的,我还包含了 grunt-contrib-cssmin。您的 `package.json` 文件应包含项目所需的任何 Grunt 包。

现在,让我们从一个新的 `Gruntfile.js` 开始。创建一个新的 `Gruntfile.js` 文件并添加以下内容。

module.exports = function(grunt) {

}

首先,我们将设置 load-grunt-tasks。load-grunt-tasks 的作用是为您创建所有 grunt.loadNpmTasks()。这样就无需手动编写每个 grunt.loadNpmTasks()

让我们创建一个名为 tasks 的变量,它将定义 load-grunt-tasks 包的选项。我们只需要定义范围并告诉它为 `package.json` 文件中定义的所有 devDependenciesdependencies 包创建 grunt.loadNpmTasks()

module.exports = function(grunt) {
  var tasks = {scope: ['devDependencies', 'dependencies']};
}

我们需要使用 require() 加载它并将 tasks 变量作为第二个参数添加到其中。让我们再添加两个变量 optionsconfigs 用于 load-grunt-configs。

module.exports = function(grunt) {
  var tasks = {scope: ['devDependencies', 'dependencies' ]};
  var options = {config: { src: "grunt/*.js" }};
  var configs = require('load-grunt-configs')(grunt, options);
  require('load-grunt-tasks')(grunt, tasks);
}

options 变量中,我们告诉 load-grunt-configs 在哪里查找包含 Grunt 任务选项的文件。configs 变量只是加载 load-grunt-configs 并将 options 作为第二个变量添加。

最后,我们将 configs 变量添加到 grunt.initConfig() 函数中,并注册一个将运行 grunt-contrib-cssmin 的基本任务。

module.exports = function(grunt) {
  var tasks = {scope: ['devDependencies', 'dependencies' ]};
  var options = {config: { src: "grunt/*.js" }};
  var configs = require('load-grunt-configs')(grunt, options);
  require('load-grunt-tasks')(grunt, tasks);
  grunt.initConfig(configs);
  grunt.registerTask('default', ['cssmin']);
}

这就是我们将放入 Gruntfile 中的所有内容。您可能想要添加的唯一其他内容是您想要注册的其他任务。例如 watch 任务或 build 任务。

options 变量中,我们将 src 定义为 grunt/*.js。这告诉 load-grunt-configs 在名为 grunt 的目录中查找并包含其中的所有 JavaScript 文件。

让我们创建一个名为 grunt 的目录,并通过创建一个名为 `cssmin.js` 的文件来添加 cssmin 的任务选项。现在您的项目的目录结构应该类似于此。

让我们将 cssmin 选项添加到 `cssmin.js` 文件中。您要添加的第一件事是 Grunt 模块的 exports 变量,就像在 Gruntfile 中一样,以便 Grunt 知道在运行 Grunt 任务时要加载此文件。然后添加 cssmin 的选项

module.exports = {
  target: {
    files: {
      'style.css': 'styles.css'
    }
  }
};

如果您注意到我没有添加 'cssmin: {}' 包装器,就像在基本 Gruntfile 中一样。这是因为 load-grunt-configs 使用文件名来识别正在运行的任务。例如,如果您使用 grunt-contrib-uglify,则文件名将为 `uglify.js`。如果您使用 grunt-postcss,则文件名将为 `postcss.js`。

这就是我们将模块设计概念添加到 Grunt 任务中的方式。每个任务都将有自己的文件包含任务的选项。这使得添加新任务变得很容易,并且多个开发人员可以进行更改而无需担心意外地在巨大的 Gruntfile 中弄乱另一个任务。

现在,当您运行 Grunt 任务时,Grunt 将在 `grunt` 文件夹中查找所有任务文件,并使用您在每个文件中定义的选项运行这些任务。

这是一个关于此技术的简单示例。load-grunt-tasksload-grunt-configs 包提供了更多可用的选项,可以为您提供对 Grunt 任务的更多控制。

我希望这可以帮助您控制 Gruntfile 并使用 Grunt 为您的项目添加更多灵活性。