四、Grunt 实战

现在是把我们新发现的知识付诸行动的时候了。Grunt 有多种用途;然而,最常见的用例是静态网站。静态网站越来越受欢迎,因为 web 开发行业需要不断提高的可伸缩性。虽然使用内容管理系统CMS)是管理网站的常用方法,但它并不是服务网站的最有效方法。这是因为大多数 CMS,例如WordPress,需要 PHP 和一个附带的数据库。另一方面,静态文件可以非常便宜地托管在云服务上,比如 Amazon 的 S3。即使我们的网站需要服务器组件来提供身份验证,例如,我们也可以通过将尽可能多的逻辑移到前端来减少服务器上的负载。这为我们提供了更大的扩展能力,同时降低了成本。通过单页应用的概念,我们可以进一步理解可伸缩性的概念。传统上,我们查看的每个页面都要求服务器一次又一次地回答对同一组资产的请求,同时还为给定页面提供动态 HTML。顾名思义,在单页应用中,网站只由一个页面组成。此单一页面智能地响应用户交互,因此将其描述为应用而不是网站。在本章中,我们从头开始,仔细地介绍使用 Grunt 为优化的单页应用创建构建的过程。

创建构建

让我们看看使用 Grunt 为单页应用创建构建所涉及的各个步骤。

www.it-ebooks.info

Grunt 在起作用

步骤 1–初始目录设置

我们通过创建根目录 project 开始我们的项目。在这个目录中,我们创建一个 src 目录来存放我们的源文件。然后,初始化项目的 package.json 文件,安装本地 grunt 模块,最后创建一个空的 Grunfile.js 文件。我们可以在命令行上使用:$mkdir 项目来完成此操作

$cd 项目/

$mkdir src

$npm 初始

$npm 安装——保存开发 grunt

$echo“module.exports=function(grunt){};”>gruntile.js正如我们所期望的,echo 命令将提供的字符串回显到命令行(称为标准输出或“stdout”)。然而,箭头(>将标准文件重定向到一个文件。所以,最后一行只是创建和初始化文件的一个简短方法。无需在命令行上创建这些文件和目录,只要我们最终使用以下目录结构:

//代码示例 01 项目

计划/

├── Grunfile.js

├── 节点单元

│ └── 咕哝

├── package.json

└── src

此时,我们可以执行 grunt,因为我们没有任务;但是,我们应该看到以下命令:

$grunt

警告:未找到任务“默认”。使用--force 继续。

由于警告而中止。

步骤 2-初始配置

对于许多网站,包括单页应用,随着 JavaScript、CSS 和 HTML 复杂性的增加,它们的数量可能会越来越多。我们可以通过简单地将分布在一组有组织的文件夹中的任意数量的单个文件连接起来,从而提高代码的可管理性。除了将代码拆分为多个文件外,我们还可以改进代码本身。

[76]

www.it-ebooks.info

第四章

这是通过使用跨文件语言实现的。在这一步中,我们将使用 CoffeeScript、Stylus 和 Jade,因为它们都为其相应的语言提供了最简单的语法。这种极简主义通过使我们的代码更干净、更简洁来提高可读性。例如,通常会将产生相同结果所需的代码减半。除了更简洁的语法外,还添加了可以进一步提高生产率的语言功能。有关每种方法的更多信息和示例,请访问以下链接:

•咖啡脚本(http://gswg.io#coffeescript)

•触笔(http://gswg.io#stylus)

•翡翠(http://gswg.io#jade)

然而,我们应该记住,除了这三种跨文件语言,还有其他选择。我们可以轻松地将 CoffeeScript 替换为 TypeScript 或 Dart,将手写笔替换为 Sass 或更少,将 Jade 替换为 Haml 或 EJS。这种替换很容易,因为它们都定义了一个源代码转换,而且由于跨编译 Grunt 插件基本上是相似的,所以无论我们选择哪种语言,我们的配置也应该是相似的。

执行转写的程序称为预处理器。因此,执行跨编译的 Grunt 插件可以看作是给定预处理器周围的薄包装。现在,我们将为我们选择的每种语言及其相应的预处理器安装一个 Grunt 插件:$npm 安装——保存 dev Grunt contrib coffee Grunt contrib jade Grunt contrib stylus

手写笔和 Sass 都非常相似,但是,手写笔是我的 CSS

选择预处理器,因为手写笔预处理器是用 JavaScript 编写的,所以它在 Node.js 中运行;而 Sass 预处理器(http://gswg.io#grunt-contrib sass)需要安装 Ruby 和 Ruby sass 库。

应该注意的是,我们还可以选择根本不使用预处理器。

在下一节中,我们将介绍资产优化,这也可以看作是一种转换。因此,即使使用普通的 JavaScript、CSS 和 HTML,我们仍然需要 Grunt 来执行优化。

[77]

www.it-ebooks.info

Grunt 在起作用

在配置这些插件之前,让我们先创建并编译一些源文件。我们将把源文件分为三个子目录:脚本、样式和视图。

请注意,之所以选择这些目录名,是因为它们与语言无关。一旦我们在 src 目录中创建了每个子目录,我们就需要在每个子目录中创建一个初始文件,如下所示:

//代码示例 02 项目

//src/scripts/app.coffee

警报“你好,世界”

//src/styles/app.styl

html,正文

边距 0

填充 0

//src/views/app.jade

!!!5.

html

链接(rel=“stylesheet”,href=“css/app.css”)正文

h5 你好,世界

脚本(src=“js/app.js”)

在 HTML 中放置链接标记(样式表)和脚本标记时,最好将所有链接标记放在 head 元素的顶部,并将所有脚本放在 body 元素的最底部。这会导致浏览器首先加载样式表,让用户在加载页面时看到样式正确的页面版本。

现在,在 Gruntfile.js 文件中,我们将加载这些插件提供的任务,然后将每个插件配置为将相应的应用文件从 src 目录编译到 build 目录:

//代码示例 02 项目

//Grunfile.js

module.exports=函数(grunt){

//加载每个插件提供的任务

grunt.loadNpmTasks(“grunt contrib 咖啡”);grunt.loadNpmTasks(“grunt contrib 触控笔”);grunt.loadNpmTasks(“grunt contrib jade”);

//项目配置

[78]

www.it-ebooks.info

第四章

grunt.initConfig({

咖啡:{

建造:{

src:“src/scripts/app.coffee”,

dest:“build/js/app.js”

}

},

触笔:{

建造:{

src:“src/styles/app.styl”,

dest:“build/css/app.css”

}

},

杰德:{

建造:{

选项:{

真的吗

},

src:“src/views/app.jade”,

dest:“build/app.html”

}

}

});

//定义默认任务

registerTask('default',['coffee','stylus','jade']);

};

此时,我们的项目目录应该如下所示:

//代码示例 02 项目

计划/

├── Grunfile.js

├── 节点单元

│ ├── 咕哝

│ ├── 咕噜咖啡

│ ├── 咕噜咕噜翡翠

│ └── grunt contrib 触控笔

├── package.json

└── src

├── 剧本

│ └── app.coffee

├── 风格

│ └── 应用 styl

└── 意见

└── app.jade

[79]

www.it-ebooks.info

Grunt 在起作用

现在,我们已经准备好传输源文件。因为我们已经将默认任务别名为 coffee、stylus 和 jade 任务,所以我们可以简单地执行 grunt,结果是:$grunt

运行“咖啡:构建”(咖啡)任务文件 build/js/app.js 创建。

正在运行“手写笔:构建”(手写笔)任务文件 build/css/app.css 已创建。

运行“翡翠:建造”(翡翠)任务

已创建文件“build/app.html”。

完成,没有错误。

我们现在应该有一个新的构建目录,看起来像:build/

├── app.html

├── css

│ └── app.css

└── js

└── app.js

src 和 build 目录之间的这种分离很重要,因为 build 的内容将在没有警告的情况下被覆盖。因此,很明显,源文件是要修改的,而构建文件是临时的。为了进一步强调后者,我们应该将构建目录添加到版本控制系统的忽略列表中。这将迫使开发人员运行 Grunt 以生成构建目录,并帮助新开发人员适应 Grunt 工作流。它还有助于发现构建中的任何 bug。

打开新生成的 app.html 文件后,我们将看到以下窗口:

[80]

www.it-ebooks.info

第四章

步骤 3–组织我们的源文件

在上一步中,我们为每个任务配置了一对一映射。然而,在实践中,我们需要一个更稳健的解决方案。

脚本

让我们从我们的咖啡脚本文件开始,如第 1 章介绍 Grunt中提到的,在串联小节中。虽然将功能分离为单个文件很重要,但减少页面上包含的脚本数量也很重要。这两个目标都可以通过文件连接来实现。现在,让我们修改 coffee 任务的配置,以编译和连接脚本目录中的所有文件。幸运的是,我们的 coffee 任务允许我们选择多个源文件,使我们能够将它们连接到一个文件中:

//代码示例 03 项目

咖啡:{

建造:{

选项:{

加入:真的

},

src:“src/scripts/***/.coffee”,

dest:“build/js/app.js”

}

}

[81]

www.it-ebooks.info

Grunt 在起作用

join 选项告诉 coffee 任务在编译之前进行连接;这是有利的,我们很快就会看到。我们可以在上查看示例和咖啡任务选项的完整列表 http://gswg.io#grunt-contrib coffee 插件的 GitHub 存储库位于 http://gswg.io#grunt-康特里布咖啡。

全局字符串 src/scripts/***.coffee 用于匹配脚本及其子目录中的所有 CoffeeScript 文件,而不是列出单个文件。要了解这一点,我们将添加两个实用程序函数,每个函数都位于其自己的文件中:

//代码示例 03 项目

//src/scripts/util/add.coffee

加法=(a,b)->a+b

//src/scripts/util/subtract.coffee

减法=(a,b)->a–b

我们还将修改 app.coffee 以利用以下功能:

//src/scripts/app.coffee

警报加 7,减 4,1

现在,当我们运行咖啡任务时:

$grunt 咖啡

运行“咖啡:构建”(咖啡)任务文件 build/js/app.js 创建。

完成,没有错误。

然后,显示生成的 build/js/app.js 文件,我们应该看到:$cat build/js/app.js

(功能(){

var 加、减;

警报(加(7,减(4,1));

加法=函数(a,b){

返回 a+b;

};

减法=函数(a,b){

返回 a-b;

};

}).打电话(这个);

[82]

www.it-ebooks.info

第四章

编译代码周围的函数包装称为立即调用的函数表达式(IIFE)。默认情况下,已编译的 CoffeeScript 代码被包装在 IIFE 中,这本质上使我们的代码成为私有的。这有助于将我们的 JavaScript 与页面上的其他 JavaScript 分开,并被认为是最佳实践。我们可以在 Ben Alman 的博客上了解更多关于这一概念的信息 http://gswg.io#iife. 前面描述的 join 选项使一个 iLife 围绕所有文件放置,而不是包装每个单独的文件。

在上面的文件中,我们注意到 add 和 subtract 的用法出现在它们被定义之前。这将导致错误。我们通过在 coffee 任务的 src 属性中使用一个数组,并通过显式地将 app.coffee 放在 glob 字符串之后以匹配所有 CoffeeScript 文件来解决此问题:

咖啡:{

建造:{

选项:{

加入:真的

},

src:[

“src/scripts/***/.coffee”,

“!src/scripts/app.coffee”,

“src/scripts/app.coffee”

],

dest:“build/js/app.js”

}

}

为了在 Grunt 版本 0.4.x 中实现这一点,我们必须首先从文件集中排除 app.coffee(在文件路径前面加一个感叹号!),然后重新包含它。

运行 grunt coffee 并显示结果现在应该可以正确生成:$grunt coffee

...

$cat build/js/app.js

(功能(){

var 加、减;

加法=函数(a,b){

返回 a+b;

};

减法=函数(a,b){

[83]

www.it-ebooks.info

Grunt 在起作用

返回 a-b;

};

警报(加(7,减(4,1));

}).打电话(这个);

现在,当我们再次打开 app.html 文件时,我们应该会看到以下窗口:即使我们选择不使用 CoffeeScript 而只使用 JavaScript,将文件分离为单独的功能片段,然后将它们连接在一起仍然是有价值的。这可以使用 grunt contrib concat 插件以类似的方式完成。也就是说,代替 coffee 任务配置,我们将插入以下 concat 任务配置:

康卡特:{

建造:{

src:[

“src/scripts/***.js”,

“!src/scripts/app.js”,

“src/scripts/app.js”

],

dest:“build/js/app.js”

}

}

[84]

www.it-ebooks.info

第四章

这种技术允许我们自由地创建任意多的 CoffeeScript(或 JavaScript)文件和子目录。然后,当我们运行 grunt 时,src 中的所有脚本文件/

脚本将合并到一个文件 build/js/app.js 中,该文件表示应用的所有 JavaScript。

为了构建有凝聚力的单页应用,我建议使用 AngularJS(http://gswg.io#angular). 一组有用的 AngularJS 教程(以屏幕播放形式)可以在 http://gswg.io#angular-放映。接下来,我推荐 Ember.js(http://gswg.io#ember). 与简单地使用 jQuery 相反,这些框架提供了一种构造 JavaScript 的约定。每个项目的这种布局规范化成为一个强大的优势,因为团队中的每个开发人员都知道代码的每个部分应该在哪里。

视图

接下来,我们将给出一些结构。在这里,我们使用术语“视图”作为 Jade 代码的语言不可知名称。如前所述,我们也可以使用 Haml 或 EJS 来代替翡翠。因为我们正在构建一个单页应用,所以我们只需要 app.html 文件,所以我们的一对一编译就足够了。但是,我们仍然希望避免将整个应用放在一个文件中。为了将 Jade 代码分割成多个文件,我们将使用 include 指令。关于 Jade 的文档包括(http://gswg.io#jade-include)描述如何静态地包含 Jade 块或其他内容(如 CSS 或 HTML),这些内容位于单独的文件中。下面我们将通过创建单个页面的逻辑分隔来利用 include。单页应用的结构可以有很大的不同;然而,在本例中,我们假设有一个页眉部分、一个内容部分和一个页脚部分。我们将创建一个新的应用目录来存放三个新的 jade 文件,而不是为 app.jade 文件中的每个部分编写代码。完成后,我们应该有以下视图文件夹:

src/视图

├── 应用

│ ├── 玉石

│ ├── 玉足

│ └── 玉石

└── app.jade

现在,我们可以通过 include 指令使用 app.jade 中的新文件:

!!!5.

html

[85]

www.it-ebooks.info

Grunt 在起作用

链接(rel=“stylesheet”,href=“css/app.css”)正文

包括应用/标题

包括应用/内容

包括应用/页脚

脚本(src=“js/app.js”)

用 grunt jade 运行我们的 jade 任务应该给我们留下以下构建/

app.html 文件:

这是**惊人的**头段 上面有一些内容 这是在中间 T1 而这个在底部 这是页脚,有一个很棒的版权符号,旁边是 2013 年