前端开发人员经常被告知要执行某些操作
- 将 CSS 和 JavaScript 分成尽可能小的块,然后将它们合并到生产网站中。
- 压缩 CSS 和压缩 JavaScript,以使它们的文件大小尽可能小,以便用于生产网站。
- 优化图像,以减少文件大小而不影响质量。
- 使用 Sass 进行 CSS 编写,因为它允许使用许多有用的抽象。
当然,这并不是一个完整的列表,但这些是我们需要做的事情。你可以称之为任务。
我打赌你听说过 Grunt。Grunt 是一种任务运行器。Grunt 可以为你完成所有这些事情。一旦你设置好它,这些事情就可以自动发生,而你无需再考虑它们。
但让我们面对现实:Grunt 是那些所有酷孩子都在使用的新潮玩意儿之一,但乍一看却感觉很奇怪且令人生畏。我明白你的感受。这篇文章就是为你准备的。
让我们立即消除一些误解
也许你听说过 Grunt,但没有用过它。我相信你们中的很多人都是这样。也许你遇到了以下其中一个障碍。
我不需要 Grunt 做的事情
实际上,你可能需要。看看上面的列表。这些东西不是锦上添花。它们是如今网站开发中非常重要的组成部分。如果你已经完成了所有这些,那太棒了。也许你使用了各种不同的工具来完成它们。Grunt 可以帮助你将它们整合到一个屋檐下。如果你还没有完成所有这些,你可能应该去做,而 Grunt 可以提供帮助。然后,一旦你完成了这些,你可以继续使用 Grunt 为你做更多的事情,这基本上会让你更好地完成你的工作。
Grunt 运行在 Node.js 上——我不了解 Node
你不必了解 Node。就像你不必了解 Ruby 就能使用 Sass 一样。或者不了解 PHP 就能使用 WordPress。或者不了解 C++ 就能使用 Microsoft Word。
我还有其他方法可以做 Grunt 可以为我做的事情
它们是否都组织在一个地方,配置为在需要时自动运行,并在每个参与该项目的人员之间共享?我认为不太可能。
Grunt 是一个命令行工具——我只是一个设计师
我也是一名设计师。如果可以,我更喜欢带有图形界面的原生应用程序。但我认为 Grunt1不会提供这样的功能。
你需要使用命令行的程度是
- 导航到项目的目录。
- 键入
grunt
并按回车键。
当然,前提是设置完成,这并不困难。
好的。让我们安装 Grunt
Node 确实是 Grunt 的先决条件。如果你没有安装 Node,不用担心,这很容易。你只需下载安装程序并运行它。点击 Node 网站上的大安装按钮。
你可以在每个项目的基础上安装 Grunt。转到项目的文件夹。它需要在根目录下有一个名为package.json的文件。你可以创建一个并将其放在那里。

该文件的内容应如下所示
{
"name": "example-project",
"version": "0.1.0",
"devDependencies": {
"grunt": "~0.4.1"
}
}
随意更改项目名称和版本,但devDependencies
部分需要保持原样。
这就是 Node 处理依赖项的方式。Node 有一个名为 npm 的包管理器,用于管理 Node 依赖项(如果你熟悉 Ruby,可以将其视为 Ruby 的“gem”)。你甚至可以将其视为 WordPress 的插件。
一旦package.json文件到位,转到终端并导航到你的文件夹。像我这样的终端菜鸟会这样做

然后运行以下命令
npm install
运行此命令后,项目中将出现一个名为node_modules的新文件夹。

你看到的其他文件,README.md和LICENSE,是因为我将把这个项目放在 GitHub 上,而这些是 GitHub 上的标准配置。
最后一个安装步骤是安装 Grunt CLI(命令行界面)。它使终端中的grunt
命令能够工作。没有它,键入grunt
将导致“找不到命令”类型的错误。出于效率原因,它是一个单独的安装。否则,如果你有十个项目,你将拥有十个 Grunt CLI 的副本。
这又是一个单行命令。只需在终端中运行此命令
npm install -g grunt-cli
你还应该关闭并重新打开终端。这是一个通用的最佳实践,可以确保一切正常。就像过去安装新应用程序后需要重新启动电脑一样。
让我们让 Grunt 合并一些文件
也许在我们的项目中,有三个单独的 JavaScript 文件
- jquery.js – 我们正在使用的库。
- carousel.js – 我们正在使用的 jQuery 插件。
- global.js – 我们编写的 JavaScript 文件,用于配置和调用插件。
在生产环境中,出于性能原因,我们将把所有这些文件合并在一起(一个请求比三个请求更好)。我们需要告诉 Grunt 为我们做这件事。
但是等等。Grunt 本身实际上什么也不做。请记住,Grunt 是一个任务运行器。我们需要添加任务本身。我们实际上还没有设置 Grunt 做任何事情,所以让我们来做这件事。
用于合并文件的官方 Grunt 插件是 grunt-contrib-concat。如果你想了解更多信息,可以在 GitHub 上阅读,但要在你的项目中使用它,你只需在终端中运行以下命令(此后将不言而喻,你需要从项目的根文件夹运行给定的命令)
npm install grunt-contrib-concat --save-dev
这样做的好处是:你的package.json文件将自动更新以包含此新的依赖项。打开它并查看。你会看到一行新内容
"grunt-contrib-concat": "~0.3.0"
现在我们准备使用它了。要使用它,我们需要开始配置 Grunt 并告诉它该做什么。
你可以通过一个名为Gruntfile.js2的配置文件告诉 Grunt 该做什么。
就像我们的package.json文件一样,我们的Gruntfile.js也有一个非常特殊的格式,必须完全正确。我不必担心每个词的意思。只需查看格式即可
module.exports = function(grunt) {
// 1. All configuration goes here
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
concat: {
// 2. Configuration for concatinating files goes here.
}
});
// 3. Where we tell Grunt we plan to use this plug-in.
grunt.loadNpmTasks('grunt-contrib-concat');
// 4. Where we tell Grunt what to do when we type "grunt" into the terminal.
grunt.registerTask('default', ['concat']);
};
现在我们需要创建该配置。文档可能会让人不知所措。让我们只关注非常简单的 用法示例。
请记住,我们有三个要合并的 JavaScript 文件。我们将在一个文件路径数组(作为带引号的字符串)中,在src
下列出它们的文件路径,然后我们将一个目标文件列为dest
。目标文件不必事先存在。当此任务运行并将所有文件压缩到一起时,它将被创建。
我们的jquery.js和carousel.js文件都是库文件。我们很可能不会修改它们。因此,为了组织,我们将它们保存在/js/libs/文件夹中。我们的global.js文件是我们编写自己代码的地方,所以它将直接放在/js/文件夹中。现在让我们告诉 Grunt 找到所有这些文件,并将它们压缩到一个名为production.js的文件中,这样命名是为了表示它用于我们的真实网站。
concat: {
dist: {
src: [
'js/libs/*.js', // All JS in the libs folder
'js/global.js' // This specific file
],
dest: 'js/build/production.js',
}
}
注意:在本文中,会出现上面这样的少量配置代码片段。目的是专注于重要的部分,但一开始看到某个片段如何融入更大的文件可能会令人困惑。如果您感到困惑并且需要更多上下文,请参阅完整的文件。
有了上述 concat
配置,前往终端,运行命令
grunt
然后观察它发生!将创建 production.js 文件,它将是我们三个文件的完美连接。对我来说,这是一个重大的顿悟时刻。感受力量在你的血管中奔涌。让我们做更多的事情吧!
让 Grunt 压缩 JavaScript
我们现在已经完成了大量的准备工作,为 Grunt 添加新的任务相对容易。我们只需要
- 找到一个 Grunt 插件来完成我们想要做的事情
- 学习该插件的配置风格
- 编写该配置以与我们的项目一起使用
用于压缩代码的官方插件是 grunt-contrib-uglify。就像我们上次做的那样,我们只需运行一个 NPM 命令来安装它
npm install grunt-contrib-uglify --save-dev
然后我们修改 Gruntfile.js 来加载该插件
grunt.loadNpmTasks('grunt-contrib-uglify');
然后我们配置它
uglify: {
build: {
src: 'js/build/production.js',
dest: 'js/build/production.min.js'
}
}
让我们更新 default
任务以也运行压缩
grunt.registerTask('default', ['concat', 'uglify']);
超级类似于连接设置,对吧?
在终端运行 grunt
,您将获得一些经过美味压缩的 JavaScript

production.min.js 文件是我们将在 index.html 文件中加载以供使用的文件。
让 Grunt 优化我们的图片
我们已经掌握了诀窍。让我们按照步骤进行。Grunt 的官方图像压缩插件是 grunt-contrib-imagemin。安装它
npm install grunt-contrib-imagemin --save-dev
在 Gruntfile.js 中注册它
grunt.loadNpmTasks('grunt-contrib-imagemin');
配置它
imagemin: {
dynamic: {
files: [{
expand: true,
cwd: 'images/',
src: ['**/*.{png,jpg,gif}'],
dest: 'images/build/'
}]
}
}
确保它运行
grunt.registerTask('default', ['concat', 'uglify', 'imagemin']);
运行 grunt
并观察那个华丽的压缩过程

必须喜欢几乎零努力就能获得的性能提升。
让我们变得更聪明一点并实现自动化
我们到目前为止所做的一切都非常棒且非常有用。但是,我们可以变得更聪明,并为自己以及 Grunt 简化一些事情
- 在应该运行时自动运行这些任务
- 仅运行当时需要的任务
例如
- 当 JavaScript 发生更改时连接并压缩 JavaScript
- 当添加新图片或更改现有图片时优化图片
我们可以通过监视文件来做到这一点。我们可以告诉 Grunt 密切关注特定位置的更改,并在这些位置发生更改时运行特定任务。监视通过官方的 grunt-contrib-watch 插件进行。
我会让你安装它。这与我们安装的最后几个插件完全相同。我们通过为 watch
提供要监视的特定文件(或文件夹,或两者)来配置它。通过监视,我的意思是监控文件更改、文件删除或文件添加。然后我们告诉它我们希望在检测到更改时运行哪些任务。
当 /js/ 文件夹中的任何内容发生更改时,我们希望运行我们的连接和压缩操作。当它发生时,我们应该运行与 JavaScript 相关的任务。当其他地方发生事情时,我们不应该运行与 JavaScript 相关的任务,因为这将是不相关的。所以
watch: {
scripts: {
files: ['js/*.js'],
tasks: ['concat', 'uglify'],
options: {
spawn: false,
},
}
}
在这一点上感觉很舒服,对吧?那里唯一奇怪的部分是 spawn
东西。你知道吗?我甚至真的不知道它在做什么。据我从文档中了解到的,它是智能的默认值。这就是现实世界的开发。如果它工作正常,就不要管它,如果不正常,就了解更多。
注意:当教程中看起来很容易的东西对您不起作用时,是不是很令人沮丧?如果您在进行更改后无法让 Grunt 运行,很可能是 Gruntfile.js 中的语法错误。这在终端中可能如下所示

通常,Grunt 非常善于让您知道发生了什么,因此请务必阅读错误消息。在这种情况下,一个缺少逗号的语法错误让我失败了。添加逗号允许它运行。
让 Grunt 完成我们的预处理
文章开头列出的最后一件事是使用 Sass——这是 Grunt 非常适合为我们运行的另一项任务。但是等等?Sass 从技术上讲不是在 Ruby 中吗?确实如此。有一个 可在 Node 中运行的 Sass 版本,因此不会为我们的项目添加额外的依赖项,但它与主要的 Ruby 项目并不完全相同。因此,我们将使用官方的 grunt-contrib-sass 插件,该插件假设您已在机器上安装了 Sass。如果您没有,请按照命令行说明进行操作。
Sass 的一个巧妙之处在于它可以自行完成连接和压缩。因此,对于我们的小型项目,我们只需让它编译我们的主要 global.scss 文件即可
sass: {
dist: {
options: {
style: 'compressed'
},
files: {
'css/build/global.css': 'css/global.scss'
}
}
}
我们不想手动运行此任务。我们已经安装了 watch 插件,所以让我们使用它!在 watch
配置中,我们将添加另一个子任务
css: {
files: ['css/*.scss'],
tasks: ['sass'],
options: {
spawn: false,
}
}
这就行了。现在,每当我们更改任何 Sass 文件时,CSS 都会自动更新。
让我们更进一步(绝对值得)并添加 LiveReload。使用 LiveReload,您无需返回浏览器并刷新页面。页面刷新会自动发生,并且在 CSS 的情况下,新样式会在不刷新页面的情况下注入(对于高度基于状态的网站非常方便)。
设置非常简单,因为 LiveReload 功能内置在 watch 插件中。我们只需要
- 安装浏览器插件
- 添加到
watch
配置的顶部. watch: { options: { livereload: true, }, scripts: { /* etc */
- 重新启动浏览器并单击 LiveReload 图标以激活它。
- 更新一些 Sass 并观察它自动更改页面。

真棒。
更喜欢视频?
如果您喜欢通过观看学习,我制作了一个与本文配套的屏幕录像,已发布在 CSS-Tricks 上:Grunt 的初体验
提升水平
正如您可能想象的那样,您可以通过构建过程进行大量提升。在某些组织中,它肯定可以成为全职工作。
一些硬核 DevOps 极客可能会嘲笑我们在这里进行的简单设置。但我建议他们慢下来。即使我们到目前为止所做的事情也具有巨大的价值。并且不要忘记这一切都是免费和开源的,这太棒了。
您可以通过添加更多有用的任务来提升水平
- 通过 Autoprefixer(强烈推荐)而不是预处理器插件来运行您的 CSS。
- 编写和运行 JavaScript 单元测试(例如:Jasmine)。
- 自动构建您的图片精灵和 SVG 图标(例如:Grunticon)。
- 启动一个服务器,以便您可以使用正确的文件路径链接到资产并使用需要真实 URL 的服务(如 TypeKit 等),以及消除其他执行此操作的工具(如 MAMP)的必要性。
- 使用 HTML-Inspector、CSS Lint 或 JS Hint 检查代码问题。
- 在 CSS 发生更改时,将其自动注入到浏览器中。
- 帮助您提交或推送到像 GitHub 这样的版本控制存储库。
- 为您的资产添加版本号(缓存清除)。
- 帮助您部署到暂存或生产环境(例如:DPLOY)。
仅仅通过更多地了解 Grunt 本身,你就可以提升技能。
- 阅读 Mark McDonnell 编写的 Grunt 样板文件。
- 阅读 Nicolas Bevacqua 编写的 Grunt 提示和技巧。
- 通过 将 Gruntfile.js 拆分成更小的文件 来组织它。
- 查看其他人和项目的 Gruntfile.js。
- 通过深入研究 Grunt 的源代码并了解其 API 来了解更多关于 Grunt 的信息。
让我们分享
我认为一些小组分享将是结束本次讨论的一个好方法。如果你第一次安装 Grunt(或者记得安装过),请特别注意你遇到过的那些小烦恼,但最终你克服了它们。这些就是我们应该在评论中分享的东西。这样,我们就有了这个安全的地方和有用的资源,可以帮助我们克服那些令人困惑的时刻,而无需感到尴尬。我们都在一起经历这些!
- 也许有一天,有人会为你的操作系统开发一个漂亮的 Grunt 应用。但我并不确定那一天是否会到来。插件的配置是使用 Grunt 的重要部分。每个插件都略有不同,具体取决于它执行的操作。这意味着每个插件都需要一个经过精心设计的 UI,这几乎是不可能的。也许一个不错的折衷方案是这个 Grunt DevTools Chrome 扩展程序。↩
- 在文档和示例中,Gruntfile.js 通常被称为 Gruntfile。不要将其命名为 Gruntfile — 它不会起作用。↩