三、理解 JavaScript 构建系统

在本章中,我们将学习 JavaScript 构建系统及其在 JavaScript 性能测试和部署方面的优势。 我们还将使用我们在上一章中获得的关于 JSLint 的知识,将 JavaScript 代码测试合并到我们的构建系统中。

简而言之,本章我们将涵盖以下主题:

  • 什么是构建系统?
  • 建立我们的构建系统
  • 创建一个分布

什么是构建系统?

通常,一个构建系统是一个自动化过程,它帮助开发人员编写干净的优化代码。 我们可能认为这样的东西在所有编程语言中都是标准的。 编译语言通常有一个编译器; 编译器接受按照语言规范编写的程序,并创建与目标机器兼容的输出代码。

通过实例编译代码

当代码文件被发送进行处理时,编译器通常通过规范工作。 为了防止编译器因为错误代码而崩溃,编译器被设置了许多错误检查器,这些检查器预先停止编译器并显示警报,从而阻止编译过程。 现在,一些 ide 允许您在尝试运行代码之前查看一些错误。 下面的截图显示了一个简单的 Xcode Swift 文件正在检查,而编辑:

Compiling code by example

在 iOS 开发中,不需要太多的技术,我们可以看到,在 Swift 中赋值常量变量时,如果我试图像前面的截图那样改变变量,我的代码会标记一个错误。

现在,如果我将let authors_name常量更改为动态var变量(就像在 JavaScript 中),错误本身就会纠正,如下面的截图所示,并删除 IDE 中显示的错误:

Compiling code by example

JavaScript 构建系统中的错误检查

在过去,用于 JavaScript 和 HTML 内容的 HTML 编辑器,如 Dreamweaver,自早期的 web 代码编辑器创建以来就这样做了。

在编译语言的 Xcode 中完成的工作和在 JavaScript IDE 中完成的工作略有不同。 对于编译语言,必须在代码文件运行之前修复错误; 这通常被认为是静态类型检查。 然而,JavaScript 可以运行时出现错误,即使是用try-catch块覆盖。 简单地说,正如在第二章中所述,使用 JSLint提高代码性能,JavaScript 是一种解释型语言,并且只有语言在运行时真正进行了错误测试。

考虑到这一点,Dreamweaver、WebStorm 或 Visual Studio 等编辑器如何检查错误呢? 如果你还记得第二章使用 JSLint提高代码性能,我们看到了检测工具是如何对 JavaScript 代码中潜在的或可验证的 bug 提供反馈的; 这将返回一个错误列表。

在 IDE 中,编辑器在编写代码时要记住这一点,并在 JavaScript 文件中使用相关的行和列显示每个错误。

因此,为了创建一个构建系统,我们将需要合并这种错误检查,就像使用http://jslint.com/一样,但是以一种更自动化的方式。 这允许轻量级编辑器使用与更昂贵和更重的 ide 中使用的相同的检查工具。

增加编码标准之外的优化

就像本章前面的 Xcode 例子一样,我们希望我们的最终输出能够针对我们的项目进行优化; 为了做到这一点,我们将在构建系统中添加缩微功能,允许我们将开发人员版本或源项目保存在一个目录中,而发行版则保存在另一个目录中。 简单地说,最小化允许我们压缩 JavaScript 代码,从而使我们的 web 应用下载更快,运行更高效。

如果我们使用源代码控制来维护我们的项目,这将是很有帮助的,它允许我们快速获取一个稳定的优化的发行版,但不容易调试,并使用源目录的文件来调试它。

现在,作为 JavaScript 开发人员,我们甚至可以为项目添加其他可能需要的缩小构建选项,比如为项目的图像目录添加图像优化器,或者缩小 CSS 文件并在 JavaScript 文件上添加信息评论块。 通过压缩 JavaScript, JavaScript 解释器不必猜测代码中的空格距离,从而创建更高效、性能更好的代码。

使用 Gulp.js 从头开始创建构建系统

现在我们已经介绍了构建系统以及使用的原因,接下来让我们创建一个简单的构建系统。 我们的目标是从我们的源目录创建一个发行版构建,这是一个经过优化并可以投入生产的副本。 我们还将集成 JSLint,正如我们在上一章中学到的那样,在创建构建时检查代码,以发现在开发过程中可能错过的任何潜在问题。

在本章中,我们将创建一个构建系统来测试我们的 JavaScript 项目。 我们还将把缩微合并到构建系统中,并将文件复制到构建目录中。 因此,当我们准备部署时,我们的代码库已经设置为要部署。

在开始这个项目之前,我们需要了解一些特定于 JavaScript 的技术,特别是我们想要考虑的构建系统; 我们将特别讨论 Node.js、NPM、Grunt 和 Gulp 等技术。 如果你之前只听说过这些,或者只是对其中的一些进行了修补,但从未真正深入过,不要担心; 我们将逐一介绍这些方法,并了解它们的优缺点。

Node.js

Node.js是一个用于您的操作系统的 JavaScript 解释器。 对于 JavaScript 开发人员来说,JavaScript 代码作为后端代码库(如 Java 或 c#)的概念可能看起来很奇怪,但它已经以一种新的创造性方式发挥了作用。 例如,Node.js 社区开发人员已经创建了一些插件来为桌面创建定制的、基于 javascript 的应用。

这将 JavaScript 置于一个全新的位置。 当传统的应用开发人员抱怨 JavaScript 时,主要的抱怨之一是 JavaScript 不能读写文件到硬盘驱动器,而这通常是编程语言非常基本的特性。 js 允许自定义对象与操作系统交互。 其中包括像FSFileSystem这样的对象,它们允许读写文件,工作原理类似于 web 浏览器中的控制台。

对于这个项目,我们不会深入讨论 Node.js(那是另一本书),但我们会将 Node.js 安装到我们的操作系统中,这样我们就可以运行和测试我们的构建系统。 让我们下载 Node.js 开始吧。 首先,进入到http://nodejs.org/,点击绿色INSTALL按钮,如下图所示:

Node.js

Node.js 是跨平台的,所以大多数指令应该适合你。 我将使用带有 OS X 的 Mac 进行安装介绍。 对于大多数平台,Node.js 会自带.pkg.exe安装向导,如下截图所示:

Node.js

从这里开始,遵循向导,接受用户许可证并为所有用户安装。 通过为所有用户安装,我们允许 Node.js 拥有我们想要的完全系统访问权限,因为 Node.js 的一些插件可能需要某些单个用户或非管理员无法访问的特性。

当你完成 Node.js 的安装时,请记住安装程序设置的路径; 如果你想在未来删除 Node.js,请查看下面的截图,看看安装程序在哪里添加了 Node.js:

Node.js

测试 Node.js 安装

为了确保 Node.js 已正确安装,我们需要检查两件事。 第一件事是检查 Node.js 是否在终端中工作。 为了验证安装,我们将检查已安装的 Node.js 的当前版本。

首先,打开终端(如果使用 Windows,则称为命令提示符),插入node --version命令,如下图所示,按回车:

Testing a Node.js installation

如果成功,我们应该看到版本号(在我的例子中,它是v0.10.32; 你的版本可能比我的版本号更大),如下截图显示在终端的下一行:

Testing a Node.js installation

测试节点包管理器的安装

伟大的工作! 现在,要检查的下一件事是是否也安装了 Node Package Manager。 在测试之前,让我解释一下什么是 Node Package Manager,特别是对那些可能不知道它是什么以及为什么我们需要它的人。

关于 Node Package Manager

Node Package Manager(NPM)连接到 NPM 注册表,这是一个 Node.js 软件库的在线存储库。 通过使用 NPM,我们可以快速建立一个 JavaScript 构建系统,并自动安装基于 html 的 JavaScript 项目的库,这允许我们确保我们的 JavaScript 库与每个库的最新版本保持一致。

NPM 也有一个网站,你可以使用https://www.npmjs.org来研究各种 JavaScript 库。 这也显示在下面的截图:

About Node Package Manager

检查终端中的 NPM 安装

现在,要检查 NPM 的安装,我们将直接调用 NPM,它应该返回一个help目录,用于安装 NPM 模块。 要做到这一点,只需打开终端并插入npm命令。 现在,我们应该看到我们的终端窗口填充了一堆 NPM 帮助文档和示例终端命令,如下面的截图所示:

Checking NPM installation in the Terminal

使用 NPM 的基础知识

使用 NPM 是一个非常简单的学习过程。 在为一个项目建立 NPM 之前,我们需要做的第一件事是创建一个项目根目录; 对于第一个项目,我将标记为npm_01,但你可以将你的根命名为任何你想要的名称。 现在,我们要打开 Terminal 并更改我们的bash目录以匹配我创建的目录的路径。

在终端中更改工作目录,命令为Change Directorycd。 使用cd非常简单; 只需输入以下命令:

cd [~/path/to/project_dir]

这里有几点需要注意:在 Mac 和 Linux 上,终端总是指向您的用户主目录,而 tilda 键(或~)是指向您的路径的快捷方式。 例如,如果您的文件夹在您的用户名下的 documents 目录中,使用 cd 的示例路径将是cd ~/Documents/[your_project_path]

提示

如果终端的信息变得杂乱,你可以使用“clear”命令来清除终端的内容,而不需要更改目录。

使用 NPM 安装 jQuery

一个常见的 JavaScript 库是 jQuery,一个 NPM 上非常流行的库。 我们甚至可以在npmjs.orghttps://www.npmjs.org/package/jquery上查看其知识库信息。

Installing jQuery with NPM

如果我们查看这个页面,我们可以看到一个用于我们的终端npm install jQuery的命令。 所以,让我们把它输入到终端,然后按,输入,看看会发生什么。

提示

如果你是 Mac 或 Linux 用户,你可以拖放一个文件夹到终端,它会在你输入cd命令后自动为你写文件夹的路径。

在终端中,看起来有些文件被下载了,如下截图所示:

Installing jQuery with NPM

现在,如果我们打开项目目录,我们可以看到一个名为node_modules的新文件夹已经创建。 在这个文件夹中,创建了另一个名为jquery的文件夹。 jquery文件夹内容如下截图:

Installing jQuery with NPM

jquery文件夹内,有一些有趣的文件。 我们有一个README.md(.md是 markdown 的缩写,一种文本格式)文件解释 jQuery。

该文件夹有两个 JSON 文件,一个叫bower.json,另一个叫package.jsonpackage.json文件处理 NPM 包信息,而bower.json文件处理任何依赖包,并通知 NPM 在安装请求时也包含这些包。

如果您想知道bower.json文件做什么,它本质上是从存储库更新源代码的另一种方法。 像 NPM 注册表一样,bower.json文件使用自己的; 不同之处在于,它可以在项目中使用 JSON 文件,并基于 JSON 文件中存储的设置进行更新。

最后,最重要的两个文件夹是src文件夹(或源文件夹)和dist文件夹(或分发文件夹)。 这种文件结构是 NPM 的通用约定,其中带有调试信息的项目源文件保存在src文件夹中,而最终测试的输出文件保存在dist文件夹中。

因为我们不是在调试 jQuery 的源代码,所以我们真正需要担心的是dist文件夹,在那里我们可以找到jquery.js文件和jquery.min.js文件——通常在 jQuery 项目中使用的库文件。 了解这一点对于我们的构建系统很重要,因为我们希望将它们复制到构建系统的分发文件夹中。

搭建搭建系统

现在我们已经学习了 Node.js 和 NPM 的基础知识,让我们来构建一个构建系统。 我们希望将终端指向项目的根目录,然后安装构建系统(也称为 Task Runner)。

关于 Grunt.js 和 Gulp.js

Node.js 构建系统属于两个主要的构建系统库:Grunt 和 Gulp。 在很多情况下,Grunt 是 Node.js 项目的默认构建系统。

任务运行器

Grunt 的设计最初用于 JavaScript 和 web 开发中的自动化任务,由于它的可用性,许多开发人员创建了插件; 你可以在 Grunt 的插件库中查看它们,如下图所示:

Grunt Task Runner

关于 Gulp

Gulp 是 Node.js 的另一个构建系统; 使用 Gulp 的优势在于它是异步的,并且通常比 Grunt 运行自动化任务要快得多。 由于这本书是关于性能的,我们将使用 Gulp 作为构建系统的例子。 这并不意味着 Grunt 是坏的; 它可以像 Gulp.js 一样创建构建系统,但可能没有 Gulp 快。

像 Grunt 一样,Gulp 也有一个插件参考页面,可以在http://gulpjs.com/plugins/找到,如下截图所示:

About Gulp

安装 Gulp

为了安装 Gulp,我们将打开终端并在提示符中输入以下内容:

sudo npm install --global gulp

这将全局安装 Gulp 到我们的 Node.js 和 NPM 资源路径。 如果我们运行的是 Windows 系统,sudo不在 Windows 的 Shell 中,所以我们需要以管理员身份运行命令提示符。 现在,如果一切都成功了,我们应该看到一堆文件的 URL 请求,我们的终端应该返回到如下截图所示的提示符:

Installing Gulp

随着 Gulp 的全局(“global”的意思是安装了我们系统中的所有文件夹)依赖项的安装,我们可以安装我们的开发人员依赖项,这使得我们的构建系统在上传到源代码控制时更加便携。 本质上,这些依赖项需要在根项目文件中,以使构建系统能够在项目目录中运行。

我们可以通过输入以下代码到我们的终端(同样,sudo为 Mac/Linux 用户,为管理员为 Windows 用户):

sudo npm install --save-dev gulp

如果成功,你的bash提示将再次显示,拉出许多 URL 源,并将它们安装到项目gulp下的node_modules目录下,如下所示:

Installing Gulp

创建 gulpfile

gulpfile是 Gulp 检查的一个文件,在项目目录的根目录下运行任务列表。 要创建一个,我们将创建一个简单的 JavaScript 文件gulpfile.js(注意文件名的情况)。 在该文件中,我们将引用 Gulp 作为变量,并创建一个默认任务Default

这是我们需要为每个gulpfile.js运行的主要任务; 在它内部,我们可以包括其他任务或输出日志信息,就像在 web 浏览器中一样。 下面的截图显示了 Gulp 任务的一个简单代码示例:

Creating a gulpfile

运行 Gulp 项目

运行 Gulp 项目很容易。 在项目的根目录下,在终端中输入gulp,然后按回车。 你应该在你的终端上看到如下截图所示的输出:

Running a Gulp project

这很好。 如果我们查看终端输出的第四行,我们应该看到输出消息为Default 任务运行了。 好工作! 这与我们在gulpfile.js中为Default任务创建的console.log消息相同。

所以您可能会问,所有这些如何帮助优化 JavaScript 代码? 如果你还记得第 2 章使用 JSLint提高代码性能,我们使用 JSLint 来检查 JavaScript 代码,做出改进,并优化文件。 如果我们可以运行那个测试工具,同时通过 JSLint 复制文件并缩小(甚至测试缩小的代码),那会怎么样呢? 我们可以,这就是使用构建系统的意义。

有了构建系统,我们在修改代码之前,甚至在它作为 web 应用部署之前,都在改进它和优化我们的代码。

将 JSLint 集成到 Gulp

之前,我们讨论了关于 Gulp 的插件页面; 其中一个插件是 JSLint 插件,安装过程非常简单。 首先,在https://www.npmjs.org/package/gulp-jslint/找到 JSLint 插件页面,如下图所示:

Integrating JSLint into Gulp

因此,以安装 Gulp 的相同方式,我们将运行页面上显示的npm命令,但将包括sudo用于管理员权限和-g命令。 这是将 JSLint 安装到整个系统的全局标志,如下所示:

sudo npm install -g gulp-jslint

接下来,我们将为我们的项目安装开发依赖项,所以我们将再次在终端中指向我们的根项目目录,然后输入我们的npm命令,但这次带有-save-dev标志,如下所示:

sudo npm install -save-dev gulp-jslint

为了验证安装,我们可以检查我们项目目录下的node_modules文件夹,看到gulp-jslint文件夹,如下截图所示:

Integrating JSLint into Gulp

测试示例文件

现在,我们的构建系统需要一个源文件,我写了一个例子,同时将它添加到在 Finder 中创建的一个新的src项目目录。 我还没有测试这个,它显示在下面的截图:

Testing our example file

我们有一个简单的 Toddler JavaScript 类,它根据调用的原型函数显示消息; 这是很基本的,它确实有一些预期的错误,所以让我们找出这些。 让我们回到gulpfile.js; 我已经用 JSLint 的一些例子进行了更新,使用了我们在2 章中提到的相同的常用选项。 查看更新的gulpfile.js文件,如下图所示:

Testing our example file

在第 6 行和第 7 行,我们可以看到诸如gulp.src()pipe()这样的约定。 src函数是一个 gulp 特定的函数,它使用 JavaScript 数组设置源文件; pipe函数,也是与 gulp 相关的,允许我们创建一个任务列表,这些任务将通过我们的构建系统从gulp.src()管道获取源文件。 这里的第 5 到 19 行显示了一个名为 JSLint 的新gulp.task。 如果我们看第 9 行到第 12 行,我们可以看到与JSLint.com相同的选项; 当我们在页面上选择不同的选项时,选项名称可以在页面底部的 JSLint Directives 下找到。

在第 22 行,我们在default任务后添加了数组,将JSLint任务名称添加到数组中。 我们可以在这里添加多个任务,但现在我们只需要 lint 任务。 现在让我们运行脚本并检查终端。

Testing our example file

太好了! 终端中显示的红线报告了脚本中的错误,它在终端中给我们提供了 lint 反馈,正如我们所看到的,我们忘记了一些常见的事情,比如使用使用严格的,缺少分号,等等。 因此,我们可以看到如何在构建过程中使用 Node.js 和 Gulp 自动化测试我们的代码。

创建发行版

把最好的部分留到最后,让 Gulp 处理缩小 JavaScript 源代码,将输出复制到dist文件夹,然后检查输出以进行测试。 我已经修改了ExampleScript.js文件,以修复之前发现的大部分问题。

现在我们需要下载 Gulp 的缩微工具Uglify,可在https://www.npmjs.org/package/gulp-uglify下载。 在基于 gulp 的项目中,它是 JavaScript 中常见的缩小器; 它的安装很简单,遵循与安装 Gulp 本身和用于 Gulp 的 JSlint 相同的过程。 安装工具时使用的命令如下:

sudo npm install --save-dev gulp-uglify

现在我已经更新了我们的gulpfile.js与一个新的缩小任务,并将其添加到数组中,如下面的截图所示:

Creating a distribution

现在,在终端窗口中运行 Gulp 并注意输出(如下图所示); 在 finder 文件夹中,您将在根项目文件夹的dist目录中看到一个全新的缩微文件,同时保留您的开发人员源代码并在获得性能检测!

Creating a distribution

小结

在本章中,我们学习了如何使用 Node.js 和 Gulp 创建一个简单的 JavaScript 构建系统。 我们还探索了其他插件,并检查了 Grunt Task Runner,它的工作原理类似于 Gulp,但包含了更多的插件。

建立系统可以帮助你在没有太多努力的情况下大大提高表现; 请记住,gulp 文件可以在其他项目中重用,因此可以尝试并找出最适合您的项目的文件。

在下一章,我们将学习如何使用 Chrome 的开发工具选项,以更好地优化我们的 web 应用代码的提示和技巧。