为那些觉得 Grunt 奇怪又难用的人准备的 Grunt 入门

Avatar of Chris Coyier
Chris Coyier 发布

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

前端开发人员经常被告知要执行某些操作

  • 将 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不会提供这样的功能。

你需要使用命令行的程度是

  1. 导航到项目的目录。
  2. 键入grunt并按回车键

当然,前提是设置完成,这并不困难。

好的。让我们安装 Grunt

Node 确实是 Grunt 的先决条件。如果你没有安装 Node,不用担心,这很容易。你只需下载安装程序并运行它。点击 Node 网站上的大安装按钮。

你可以在每个项目的基础上安装 Grunt。转到项目的文件夹。它需要在根目录下有一个名为package.json的文件。你可以创建一个并将其放在那里。

package.json at root
根目录下的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文件到位,转到终端并导航到你的文件夹。像我这样的终端菜鸟会这样做

Terminal rube changing directories
终端菜鸟更改目录

然后运行以下命令

npm install

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

Example of node_modules folder
node_modules文件夹示例

你看到的其他文件,README.mdLICENSE,是因为我将把这个项目放在 GitHub 上,而这些是 GitHub 上的标准配置。

最后一个安装步骤是安装 Grunt CLI(命令行界面)。它使终端中的grunt命令能够工作。没有它,键入grunt将导致“找不到命令”类型的错误。出于效率原因,它是一个单独的安装。否则,如果你有十个项目,你将拥有十个 Grunt CLI 的副本。

这又是一个单行命令。只需在终端中运行此命令

npm install -g grunt-cli

你还应该关闭并重新打开终端。这是一个通用的最佳实践,可以确保一切正常。就像过去安装新应用程序后需要重新启动电脑一样。

让我们让 Grunt 合并一些文件

也许在我们的项目中,有三个单独的 JavaScript 文件

  1. jquery.js – 我们正在使用的库。
  2. carousel.js – 我们正在使用的 jQuery 插件。
  3. 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.jscarousel.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 添加新的任务相对容易。我们只需要

  1. 找到一个 Grunt 插件来完成我们想要做的事情
  2. 学习该插件的配置风格
  3. 编写该配置以与我们的项目一起使用

用于压缩代码的官方插件是 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

Minified JavaScript
压缩后的 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 并观察那个华丽的压缩过程

Squished images<
压缩后的图片
/figure>

必须喜欢几乎零努力就能获得的性能提升。

让我们变得更聪明一点并实现自动化

我们到目前为止所做的一切都非常棒且非常有用。但是,我们可以变得更聪明,并为自己以及 Grunt 简化一些事情

  1. 在应该运行时自动运行这些任务
  2. 仅运行当时需要的任务

例如

  1. 当 JavaScript 发生更改时连接并压缩 JavaScript
  2. 当添加新图片或更改现有图片时优化图片

我们可以通过监视文件来做到这一点。我们可以告诉 Grunt 密切关注特定位置的更改,并在这些位置发生更改时运行特定任务。监视通过官方的 grunt-contrib-watch 插件进行。

我会让你安装它。这与我们安装的最后几个插件完全相同。我们通过为 watch 提供要监视的特定文件(或文件夹,或两者)来配置它。通过监视,我的意思是监控文件更改、文件删除或文件添加。然后我们告诉它我们希望在检测到更改时运行哪些任务。

/js/ 文件夹中的任何内容发生更改时,我们希望运行我们的连接和压缩操作。当它发生时,我们应该运行与 JavaScript 相关的任务。当其他地方发生事情时,我们不应该运行与 JavaScript 相关的任务,因为这将是不相关的。所以

watch: {
    scripts: {
        files: ['js/*.js'],
        tasks: ['concat', 'uglify'],
        options: {
            spawn: false,
        },
    } 
}

在这一点上感觉很舒服,对吧?那里唯一奇怪的部分是 spawn 东西。你知道吗?我甚至真的不知道它在做什么。据我从文档中了解到的,它是智能的默认值。这就是现实世界的开发。如果它工作正常,就不要管它,如果不正常,就了解更多。

注意:当教程中看起来很容易的东西对您不起作用时,是不是很令人沮丧?如果您在进行更改后无法让 Grunt 运行,很可能是 Gruntfile.js 中的语法错误。这在终端中可能如下所示

Errors running Grunt
运行 Grunt 时出错

通常,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 插件中。我们只需要

  1. 安装浏览器插件
  2. 添加到 watch 配置的顶部
    . watch: {
        options: {
            livereload: true,
        },
        scripts: {   
        /* etc */
  3. 重新启动浏览器并单击 LiveReload 图标以激活它。
  4. 更新一些 Sass 并观察它自动更改页面。
Live reloading browser
实时重新加载浏览器

真棒。

更喜欢视频?

如果您喜欢通过观看学习,我制作了一个与本文配套的屏幕录像,已发布在 CSS-Tricks 上:Grunt 的初体验

提升水平

正如您可能想象的那样,您可以通过构建过程进行大量提升。在某些组织中,它肯定可以成为全职工作

一些硬核 DevOps 极客可能会嘲笑我们在这里进行的简单设置。但我建议他们慢下来。即使我们到目前为止所做的事情也具有巨大的价值。并且不要忘记这一切都是免费和开源的,这太棒了。

您可以通过添加更多有用的任务来提升水平

  • 通过 Autoprefixer(强烈推荐)而不是预处理器插件来运行您的 CSS
  • 编写和运行 JavaScript 单元测试(例如:Jasmine)。
  • 自动构建您的图片精灵和 SVG 图标(例如:Grunticon)。
  • 启动一个服务器,以便您可以使用正确的文件路径链接到资产并使用需要真实 URL 的服务(如 TypeKit 等),以及消除其他执行此操作的工具(如 MAMP)的必要性。
  • 使用 HTML-InspectorCSS LintJS Hint 检查代码问题。
  • CSS 发生更改时,将其自动注入到浏览器中。
  • 帮助您提交或推送到像 GitHub 这样的版本控制存储库。
  • 为您的资产添加版本号(缓存清除)。
  • 帮助您部署到暂存或生产环境(例如:DPLOY)。

仅仅通过更多地了解 Grunt 本身,你就可以提升技能。

让我们分享

我认为一些小组分享将是结束本次讨论的一个好方法。如果你第一次安装 Grunt(或者记得安装过),请特别注意你遇到过的那些小烦恼,但最终你克服了它们。这些就是我们应该在评论中分享的东西。这样,我们就有了这个安全的地方和有用的资源,可以帮助我们克服那些令人困惑的时刻,而无需感到尴尬。我们都在一起经历这些!


  1. 也许有一天,有人会为你的操作系统开发一个漂亮的 Grunt 应用。但我并不确定那一天是否会到来。插件的配置是使用 Grunt 的重要部分。每个插件都略有不同,具体取决于它执行的操作。这意味着每个插件都需要一个经过精心设计的 UI,这几乎是不可能的。也许一个不错的折衷方案是这个 Grunt DevTools Chrome 扩展程序。
  2. 在文档和示例中,Gruntfile.js 通常被称为 Gruntfile。不要将其命名为 Gruntfile — 它不会起作用。