将代码分解成更小的、易于管理的块的想法,创造了一个易于工作和维护的环境。这通常被认为是模块设计,并且是如今 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 包。
继续,像安装任何其他 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` 文件中定义的所有 devDependencies
和 dependencies
包创建 grunt.loadNpmTasks()
。
module.exports = function(grunt) {
var tasks = {scope: ['devDependencies', 'dependencies']};
}
我们需要使用 require() 加载它并将 tasks
变量作为第二个参数添加到其中。让我们再添加两个变量 options 和 configs 用于 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-tasks 和 load-grunt-configs 包提供了更多可用的选项,可以为您提供对 Grunt 任务的更多控制。
我希望这可以帮助您控制 Gruntfile 并使用 Grunt 为您的项目添加更多灵活性。
还有人在非遗留项目中使用 grunt 吗?
是的。https://github.com/LotusTM/Kotsu/tree/release/1.0.0
我仍然在使用它——我还没有真正有说服力的理由切换到如今流行的东西(是 webpack 吗?NPM 脚本吗?我们现在讨厌 Gulp 了吗?再也记不住了)。
我确定有很多理由切换到其他东西,但 Grunt 在编译 scss/压缩文件/等等方面做得很好。你对 Grunt 的主要抱怨是什么?是样板文件太多还是有什么问题实际上会影响最终用户?
我也仍然在使用它。我还没有找到一个令人信服的理由来改变我团队的工作流程。但我一直都在关注。
相关
@Alex – 我个人从 Grunt 切换到 Gulp 的原因是我觉得 Gulp 在运行任务方面明显更快。这很可能是因为我编写了一个糟糕的 Gruntfile——我没有对其进行任何真正的严格测试。
嗯,这很公平。我在性能方面遇到的唯一重大问题是编译 SCSS。对我来说,从
grunt-contrib-sass
(使用 Ruby Sass)切换到grunt-sass
(使用 LibSass)帮助很大。如果我注意到速度变慢太多,我可能会将其作为切换到其他东西的信号,谢谢。
我也在使用 Ruby Sass,当我切换时,我还切换到了 PostCSS,这两者都肯定有助于解决性能问题。
我也是。我发现 grunt-contrib-sass 非常慢。一旦我迁移到 postcss + grunt-sass,速度就大大提高了。
我同时使用 Grunt 和 Gulp,因为 Grunt 可用的包比 Gulp 多得多。这样就可以获得两者的优势。
好的文章——大型的 Grunt/Gulpfile 确实很难看。我在 Gulp 中也采用了同样的方法。我的 gulpfile 现在看起来像
在 ./gulp-tasks 目录中将各个任务放在它们自己的 Javascript 文件中。更容易管理了!
我认为这种 require dir 的风格不是最佳实践,并且在您升级到 gulp 4 时会导致问题
感谢这篇文章,我们公司非常喜欢像这样拆分任务。
实际上可以进一步简化——您不需要安装
load-grunt-tasks
,因为load-grunt-config
包含了它。并且 Gruntfile.js 文件可以简化为
load-grunt-config 默认会在
grunt/
文件夹中查找您的任务,如果您需要,您仍然可以将选项传递给 load-grunt-tasks。很棒的文章,感谢分享。有谁知道使用
load-grunt-tasks
而不是matchdep
自动加载任务的优缺点吗?require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);
很棒的文章,我实际上想知道这是否适用于 jit-grunt。
是的,绝对可以。
我强烈推荐
jit-grun
而不是load-grunt-tasks
,因为它初始化任务的速度明显更快。此外,您不需要
load-grunt-configs
来加载任务文件,因为 Grunt 已经有一个用于外部配置的加载器——grunt.loadTasks
(文档)。这是我们 配置文件 中的示例