JavaScript 一路到底

有一个著名的故事,讲的是一位科学家就地球及其在太阳系中的位置发表演讲。演讲结束时,一位女士反驳他说:“那是胡说八道;地球实际上像一个扁平的盘子,由一只乌龟的背驮着。” 科学家笑了笑,反问道:“但是乌龟站在什么上面呢?”,这位女士意识到逻辑陷阱,回答说:“这很简单:它是乌龟一路到底!” 无论这个轶事的真实性如何,科学家的身份(有时提到伯特兰·罗素或威廉·詹姆斯),甚至他们是乌龟还是陆龟,今天我们都可以将类似的解决方案应用于 Web 开发,用“JavaScript 一路到底”。

如果您要开发一个网站,对于客户端开发,您可以选择 Java Applet、ActiveX 控件、Adobe Flash 动画,当然还有纯 JavaScript。另一方面,对于服务器端编码,您可以选择 C# (.Net)、Java、Perl、PHP 等,在服务器(如 Apache、Internet Information Server、Nginx、Tomcat 等)上运行。目前,JavaScript 允许您摆脱大部分这些,并使用单一的编程语言,无论是在客户端还是服务器端,甚至可以使用基于 JavaScript 的服务器。这种工作方式甚至产生了一个完全面向 JavaScript 的首字母缩略词,类似于旧的 LAMP(Linux+Apache+MySQL+PHP):MEAN,它代表 MongoDB(一个可以使用 JavaScript 访问的 NoSQL 数据库)、Express(一个 Node.js 模块,用于构建服务器端代码)、Angular.JS(Google 的 Web 开发框架,用于客户端代码)和 Node.js。

在本文中,我将介绍几种用于编写、测试和部署 Web 应用程序的 JavaScript 工具,以便您可以考虑是否要尝试一下“JavaScript 一路到底”的 Web 堆栈。

名字的由来?

JavaScript 最初于 1995 年在 Netscape 开发,最初名为 Mocha,然后是 LiveScript。不久之后(在 Netscape 和 Sun 合作之后;如今,Mozilla 基金会管理着该语言),它被重命名为 JavaScript,以搭上流行的浪潮,尽管与 Java 没有任何关系。1997 年,它以第四个名称 ECMAScript 成为行业标准。JavaScript 最常见的当前版本是 5.1,日期为 2011 年 6 月,版本 6 正在路上。(但是,如果您想使用更现代的功能,但您的浏览器不支持它们,请查看 Traceur 编译器,它会将版本 6 代码向后编译到版本 5 级别。)

一些公司生产了该语言的超集,例如微软,它开发了 JScript(重命名以避免法律问题),以及 Adobe,它为 Flash 创建了 ActionScript。

还有几种衍生语言(实际上编译为 JavaScript 以执行),例如更简洁的 CoffeeScript、微软的 TypeScript 或 Google 最新的 AtScript(JavaScript 加注解),它是为 Angular.JS 项目开发的。asm.js 项目甚至使用 JavaScript 子集作为其他语言高效编译器的目标语言。同一个概念有这么多不同的名称!

为什么选择 JavaScript?

尽管像 LAMP 或其 Java、Ruby 或 .Net 同类产品如今为许多 Web 应用程序提供动力,但在客户端和服务器端开发都使用单一语言有几个优点,Groupon、LinkedIn、Netflix、PayPal 和 Walmart 等众多公司就是证明。

现代 Web 开发分为客户端和服务器端(或前端和后端)编码,如果您的开发人员可以轻松地处理两方面,则更容易实现最佳平衡。当然,许多开发人员都熟悉两方面编码所需的所有语言,但在任何情况下,他们都可能在一端或另一端更有效率。

JavaScript 有许多可用的工具(构建、测试、部署等),您可以在系统的所有组件中使用它们(图 1)。因此,通过使用相同的单一工具集,您经验丰富的 JavaScript 开发人员将能够兼顾两方面,并且您在为您的公司找到所需的程序员方面遇到的问题也会更少。

图 1. JavaScript 可以用于任何地方,包括客户端和服务器端。

当然,能够使用单一语言并不是唯一的关键点。在“旧时代”(仅仅几年前!),JavaScript 专门存在于浏览器中,用于读取和解释 JavaScript 源代码。(好吧,如果您想精确一点,这并不完全正确;Netscape Enterprise Server 运行服务器端 JavaScript 代码,但它没有被广泛采用。)大约五年前,当 Firefox 和 Chrome 开始与(那时)最流行的 Internet Explorer 认真竞争时,开发了新的 JavaScript 引擎,与实际绘制浏览器上看到的 HTML 页面的布局引擎分离。鉴于基于 AJAX 的应用程序的日益普及,这需要在客户端提供更多的处理能力,一场提供最快 JavaScript 的竞争开始了,并且至今没有停止。随着性能的提高,JavaScript 变得有可能更广泛地使用(表 1)。

表 1. 当前浏览器及其 JavaScript 引擎
浏览器 JavaScript 引擎
Chrome V8
Firefox SpiderMonkey
Opera Carakan
Safari Nitro

其中一些引擎应用了先进的技术来获得最快的速度和性能。例如,V8 在执行 JavaScript 之前将其编译为本机机器代码(这称为 JIT,即时编译,它是在运行时完成的,而不是像传统编译器那样预先翻译整个程序),并且还应用了几种优化和缓存技术以获得更高的吞吐量。SpiderMonkey 包括 IonMonkey,它也能够将 JavaScript 代码编译为目标代码,尽管以更传统的方式工作。因此,接受现代 JavaScript 引擎有足够的性能来完成您可能需要的任何事情,现在让我们开始回顾一下 Web 堆栈,首先是一个服务器,如果不是因为那种高级语言的性能,它就不会存在:Node.js。

Node.js:一种新型服务器

Node.js(或通常称为纯 Node)是一个 Web 服务器,主要本身用 JavaScript 编写,它使用该语言进行所有脚本编写。它最初是为了简化开发具有推送功能的实时网站而开发的——因此,服务器可能会自己与客户端建立连接,而不是所有通信都由客户端发起。Node 可以处理大量实时连接,因为它在需求方面非常轻量级。Node 有两个关键概念:它运行单个进程(而不是多个),并且所有 I/O(数据库查询、文件访问等)都以非阻塞、异步方式实现。

让我们更深入一点,进一步研究 Node 和更传统的服务器(如 Apache)之间的主要区别。每当 Apache 收到请求时,它都会启动一个新的、独立的线程(进程),该线程使用自己的 RAM 和 CPU 处理能力。(如果运行的线程过多,则请求可能需要等待更长时间才能启动。)当线程产生其答案时,线程就完成了。可能的最大线程数取决于进程的平均 RAM 需求;它可能同时有几千个,尽管数字因服务器大小而异(图 2)。

图 2. Apache 和传统的 Web 服务器为每个请求运行一个单独的线程。

另一方面,Node 运行一个单线程。每当收到请求时,它都会尽快处理,并且会持续运行直到需要一些 I/O。然后,当代码等待 I/O 结果可用时,Node 将能够处理其他等待请求(图 3)。由于所有请求都由单个进程服务,因此可能的运行请求数增加,并且已经有超过一百万个并发连接的实验——一点也不寒酸!这表明 Node 的理想用例是具有 CPU 处理量较轻但 I/O 较高的服务器进程。这将允许更多请求同时运行;CPU 密集型服务器进程将阻止所有其他等待请求,并导致输出大幅下降。

图 3. Node 为所有请求运行一个单线程。

Node 的一个巨大优势是,有许多可用的模块(估计有数千个),可以帮助您更快地投入生产。虽然我显然无法列出所有模块,但您可能应该考虑表 2 中列出的一些模块。

表 2. 一些广泛使用的 Node.js 模块,它们将帮助您的开发和运营。
模块 描述
async 简化异步工作,可能是 promise 的替代方案。
cluster 通过派生工作进程来提高多核系统中的并发性。(为了进一步扩展,您还可以设置反向代理并运行多个 Node.js 实例,但这超出了本文的目标。)
connect 与“中间件”一起工作,用于常见任务,例如错误处理、日志记录、服务静态文件等。
ejs, handlebars 或 jade, EJS 模板引擎。
express 一个最小的 Web 框架——MEAN 中的 E。
forever 一个命令行工具,它将保持您的服务器运行,并在崩溃或其他问题后根据需要重新启动。
mongoose, cradle, sequelize 数据库 ORM,用于 MongoDB、CouchDB 以及关系数据库,例如 MySQL 等。
passport 身份验证中间件,可以与 OAuth 提供商(如 Facebook、Twitter、Google 等)一起使用。
request 或 superagent HTTP 客户端,对于与 RESTful API 交互非常有用。
underscore 或 lodash 用于函数式编程和扩展 JavaScript 核心对象的工具。

当然,使用 Node.js 时有一些注意事项。一个明显的注意事项是,任何进程都不应进行繁重的计算,这会“阻塞”Node 的单处理线程。如果需要这样的进程,则应由外部进程完成(您可能需要考虑为此使用消息队列),以免阻止其他请求。此外,必须注意错误处理。未处理的异常最终可能会导致整个服务器崩溃,这对整个服务器来说不是好兆头。另一方面,拥有庞大的用户社区和大量完全可用、生产级、经过测试的代码,可以为您节省大量开发时间,并让您建立现代、快速的服务器环境。

规划和组织您的应用程序

在开始一个新项目时,您可以从零开始设置您的代码并从头开始编写所有内容,但一些框架可以帮助您完成大部分工作,并为您的 Web 应用程序提供清晰的结构和组织。选择正确的框架将对您的开发时间、测试和站点的可维护性产生重要影响。当然,对于“哪个框架最好?”这个问题没有唯一的答案,并且新的框架几乎每天都在出现,所以我只选择今天可用的三个顶级解决方案:AngularJS、Backbone 和 Ember。基本上,所有这些框架都在宽松的许可下可用,并让您在开发现代 SPA(单页应用程序)方面抢先一步。对于服务器端,一些软件包(例如 Sails,仅举一个例子)与所有框架一起工作。

AngularJS(或 Angular.JS 或简称为 Angular——任您选择)由 Google 于 2009 年开发,其当前版本是 1.3.4,日期为 2014 年 11 月。该框架基于声明式编程最适合接口(而命令式编程最适合业务逻辑)的思想,因此它使用自定义标记属性扩展了 HTML,这些属性用于将输入和输出数据绑定到 JavaScript 模型。通过这种方式,程序员不必直接操作网页,因为它会自动更新。Angular 还专注于测试,因为自动测试的难度很大程度上取决于代码结构。请注意,Angular 是 MEAN 中的 A,因此还有一些其他框架对其进行扩展,例如 MEAN.IO 或 MEAN.JS。

Backbone 是一个更轻量级、更精简的框架,始于 2010 年,它使用 RESTful JSON 接口自动更新服务器端。(有趣的事实:Backbone 由 Jeremy Ashkenas 创建,他也是 CoffeeScript 的开发者;请参阅“名字的由来?”边栏。)就社区规模而言,它仅次于 Angular,就代码大小而言,它是迄今为止最小的。Backbone 不包含自己的模板引擎,但它可以与 Underscore 的模板完美配合,并且鉴于此库默认包含,这是一个简单的选择。它被认为比其他框架更少“武断”,并且学习曲线相当浅,这意味着您将能够快速开始工作。一个缺点是 Backbone 缺少双向数据绑定,因此您必须编写代码来在模型更改时更新视图,反之亦然。此外,您可能需要直接操作网页,这将使您的代码更难进行单元测试。

最后,Ember 可能比其他框架更难学习,但它以更高的性能奖励编码人员。它偏爱“约定优于配置”,这可能会让 Ruby on Rails 或 Symfony 用户感到宾至如归。它使用 JSON 进行通信,轻松地与 RESTful 服务器端集成。Ember 包括 Handlebars(参见表 2)用于模板,并提供双向更新。一个缺点是使用 <script> 标签作为标记,以便保持模板与模型同步。如果您尝试调试正在运行的应用程序,您会发现很多意外的元素!

简化和增强您的编码

可以肯定的是,您的应用程序将需要使用 HTML、处理各种事件并进行 AJAX 调用以连接到服务器。这应该相当容易——尽管可能有很多工作——但即使在今天,浏览器也不具有完全相同的功能。因此,您可能不得不使用特定的浏览器检测技术,以便您的代码能够适应并在任何地方工作。现代应用程序用户已经习惯于使用不同的事件(点击、双击、长按、拖放等),您应该能够在您的代码中包含这种类型的处理,可能带有适当的动画。最后,连接到服务器是必须的,因此您将一直使用 AJAX 功能,并且它不应该是一种痛苦的体验。

最有可能帮助您完成所有这些功能的候选库是 jQuery。可以说,它是当今使用最广泛的 JavaScript 库,在访问量最大的网站中超过 60% 的网站都在使用它。jQuery 提供了用于导航应用程序的 Web 文档、轻松处理事件、应用动画和使用 AJAX(列表 1)的工具。其当前版本为 2.1.1(或 1.11.1,如果您想支持较旧的浏览器),并且重量仅约为 32K。一些框架(例如 Angular)甚至会在可用时使用它。

列表 1. 一个简单的 jQuery 示例,展示了如何处理事件、访问页面和使用 AJAX。

var myButtonId = "#processButton");
$(myButtonId).click(function(e) {		 // when clicked...
    $(myButtonId).attr("disabled", "disabled");	 // disable button
    $.get("my/own/services", function(data) {	 // call server service
	window.alert("This came back: " + data); // show what it returns
	$(myButtonId).removeAttr("disabled");	 // re-enable the button
    }
});

其他使用较少的可能性可能是 Prototype(当前版本 1.7.2)、MooTools(版本 1.5.1)或 Dojo Toolkit(版本 11)。所有这些库的关键卖点之一是浏览器之间差异的抽象,因此您可以编写代码而无需担心它是否会在某个浏览器上运行。您可能应该查看所有这些库,以找到最适合您编程风格的库。

此外,您可能还需要一种库。回调对于需要它们进行 AJAX 调用的 JavaScript 程序员来说很熟悉,但是当为 Node 编程时,肯定会有很多回调!您应该关注“promise”,这是一种编程方式,它将使回调编程更具可读性,并使您摆脱“回调地狱”——在这种情况下,您需要一个回调,而该回调也需要一个回调,而该回调也需要一个回调,依此类推,使代码真的很难理解。请参阅列表 2,其中还显示了您的代码将需要的不断增长的缩进。我省略了错误处理代码,这将使示例更加混乱!

列表 2. 当回调包含回调,而回调又包含回调等等时,就会发生回调地狱。

function nurseryRhyme(...) {
  ..., function eeny(...) {
    ..., function meeny(...) {
      ..., function miny(...) {
        ..., function moe(...) {
          ...
        }
      }
    }
  }
}

promise 的行为通过“Promises/A+”开放规范进行了标准化。几个软件包提供 promise(jQuery 和 Dojo 已经包含对它们的一些支持),总的来说,它们甚至可以交互,处理彼此的 promise。promise 是一个对象,表示(通常是异步的)操作的未来值。您可以通过 promise 的 .then(...) 方法处理此值,并使用其 .catch(...) 方法处理异常。promise 可以链接在一起,并且一个 promise 可以产生一个新的 promise,其值将在下一个 .then(...) 中处理。使用这种风格,列表 2 的回调地狱示例将转换为更易于理解的代码;请参阅列表 3。代码不再越来越缩进,而是保持左对齐。回调仍然在(内部)使用,但您的代码没有显式地使用它们。错误处理也更简单;您只需添加适当的 .catch(...) 调用即可。

列表 3. 使用 promise 会产生更易读的代码。

nurseryRhyme(...).
  .then(function eeny(...) {...})
  .then(function meeny(...) {...})
  .then(function miny(...) {...})
  .then(function moe(...) {...});

您还可以从更多 promise 中构建 promise——例如,一个服务可能需要在生成答案之前获得三个不同回调的结果。在这种情况下,您可以从这三个单独的 promise 中构建一个新的单个 promise,并指定只有当其他三个 promise 都已实现时,新的 promise 才会实现。还有其他构造可以让您在给定数量(可能只有一个)的“子 promise”已实现时实现一个 promise。请参阅“资源”部分,了解您可能想要尝试的几个可能的库。

我已经评论了您可能用于编写应用程序的几种工具,所以现在让我们考虑最后几个步骤:构建应用程序、测试应用程序以及最终部署应用程序以进行操作。

测试您的应用程序

无论您是独自编程还是作为大型开发团队的一部分进行编程,测试您的代码都是基本需求,并且以自动化方式进行测试是必须的。几个框架可以帮助您完成此操作,例如 Intern、Jasmine 或 Mocha(请参阅“资源”)。本质上,它们非常相似。您定义“套件”,每个套件运行一个或多个“测试用例”,这些用例测试您的代码是否执行某些特定功能。为了测试结果并查看它们是否满足您的期望,您编写“断言”,断言基本上是必须满足的条件(有关简单示例,请参见列表 4)。您可以将测试套件作为构建过程的一部分运行(我将在下面解释),以查看在尝试部署较新版本的代码之前是否有什么东西被破坏。

列表 4. 套件通常包括多个测试用例。

describe("Prime numbers tests", function() {
  it("Test prime numbers", function() {
    expect(isPrime(2)).to.be.true();
    expect(isPrime(5)).to.be.true();
  });
  
  it("Test non-prime numbers", function() {
    expect(isPrime(1).to.be.false();
    expect(isPrime(4).to.be.not.true();	// just for variety!
    expect(isPrime(NaN)).to.throw(err);  
  });
});

测试可以用“流畅”的风格编写,使用许多匹配器(有关一些示例,请参见列表 5)。几个库提供了不同的编写测试方法,包括 Chai、Unit.js、Should.js 和 Expect.js;查看它们以确定哪个最适合您。

列表 5. 您可以使用许多可用的匹配器来编写断言的一些示例。

expect(someFunction(...)).to.be.false();  // or .true(), .null(), 
                                          // .undefined(), .empty()
expect(someFunction(...)).to.not.equal(33);  // also .above(33), 
                                             // .below(33)
expect(someFunction(...)).to.be.within(30,40);
expect(someObject(...)).to.be.an.instanceOf(someClass);
expect(someObject(...)).to.have.property("key", 22);
expect(someResult(...)).to.have.length.above(2);
expect(someString(...)).to.match(/^aRegularExpression/);
expect(failedCall(...)).to.throw(err);

如果您想运行涉及浏览器的测试,PhantomJS 和 Zombie 提供了一个伪造的 Web 环境,因此您可以比使用 Selenium 等工具更快地运行测试,Selenium 更适合最终验收测试。

列表 6. 使用 Zombie 的示例测试(顺便说一句,使用了 promise)不需要实际的浏览器。

var browser = require("zombie").create();
browser.localhost("your.own.url.com", 3000);
browser.visit("/")
  .then(function() { // when loaded, enter data and click
    return browser
      .fill("User", "fkereki")
      .fill("Password", ¡")
      .pressButton("Log in");
  })
  .done(function() { // page loaded
    browser.assert.success();
    browser.assert.text("#greeting", "Hi, fkereki!");
  });
大量的 DD!

现代敏捷开发过程通常强调非常短的周期,基于为尚未编写的代码编写测试,然后实际编写所需的代码,测试既是对代码是否按预期工作的检查,也是一种规范本身。此过程称为 TDD(测试驱动开发),它通常会导致模块化和灵活的代码,这也更容易理解,因为测试有助于理解。BDD(行为驱动开发)是一个基于 TDD 的过程,它甚至以与本文中提到的匹配器非常相似的形式指定需求。另一个“DD”是 ATDD(验收测试驱动开发),它强调甚至在程序员开始编码之前编写(自动化)验收测试的想法。

构建和部署

每当您的代码准备好部署时,您几乎肯定必须执行几个重复性任务,并且您最好将它们自动化。当然,您可以使用像 make 或 Apache 的 ant 这样的经典工具,但为了坚持“JavaScript 一路到底”的想法,让我们看看 Grunt 和 Gulp 这对工具,它们运行良好。

Grunt 可以使用 npm 安装。执行 sudo npm install -g grunt-cli,但这还不够;您必须准备一个 gruntfile 来让它知道它应该做什么。基本上,您需要一个 package.json 文件来描述您的系统所需的软件包,以及一个 Gruntfile.js 文件来描述您需要的任务。任务可能有自己的子任务,您可以选择运行整个任务或特定的子任务。对于每个任务,您将定义(当然,在 JavaScript 中)需要完成的操作(列表 7)。运行不带参数的 grunt 将运行默认(如果给定)任务或所有任务。

列表 7. 一个示例 Grunt 文件

module.exports = function(grunt) {
  grunt.initConfig({
    concat: {
      dist: {
	  dest: 'dist/build.js',
	  src: ['src/**.js'],
      },
      options: 	{ 
	separator: ';',
	stripBanners : true,
      },
    },
    
    uglify: {
      dist: {
        files: 	{ 'dist/build.min.js': 
           ↪['<%= concat.dist.dest %>'] },
      }
    },
  });

  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.registerTask('default', ['concat', 'uglify']);
};

Gulp 的设置稍微简单一些(事实上,它的创建是为了简化 Grunt 的配置文件),并且它依赖于其作者所谓的“代码优于配置”。Gulp 以“流”或“管道”方式工作,类似于 Linux 的命令行,但带有 JavaScript 插件。每个插件接受一个输入并产生一个输出,该输出会自动馈送到队列中的下一个插件。这更容易理解和设置,甚至对于涉及多个步骤的任务也可能更快。另一方面,作为一个较新的项目意味着用户社区较小且可用插件较少,尽管这两种情况都可能在不久的将来得到解决。

您可以从开发环境(例如 Eclipse 或 NetBeans)、命令行或作为“监视器”使用它们,将它们设置为监视特定文件或目录,并在检测到更改时运行某些任务,以完全自动化的方式进一步简化您的开发过程。您可以设置模板将被处理、代码将被缩小、SASS 或 LESS 样式将被转换为纯 CSS,并且生成的文件将被移动到服务器,无论它们在哪里合适。这两种工具都有自己的粉丝,您应该亲自尝试这两种工具,以决定您更喜欢哪一种。

获取和更新软件包

由于现代系统依赖于大量软件包(框架、库、样式、实用程序等等),因此获取所有软件包,甚至更糟糕的是,保持它们更新,可能会成为一项繁琐的工作。有两种工具可以解决这个问题:Node 自己的 npm(主要用于服务器端软件包,尽管它也可以用于客户端代码)和 Twitter 的 bower(更适合客户端部分)。前者主要处理 Node 软件包,并允许您根据配置文件保持服务器更新。另一方面,后者可以安装和更新您的 Web 应用程序可能需要的所有类型的前端组件(即,不仅是 JavaScript 文件),也基于单独的配置元数据文件。

这两种实用程序的使用方法相同;只需将 bower 替换为 npm,您就完成了。使用 npm search for.some.package 可以帮助您找到给定的软件包。键入 npm install some.package 将安装它,添加 --save 选项将更新相应的配置文件,以便将来 npm update 命令将使所有内容保持最新。在紧急情况下,npm 也可以用作 bower 的替代品,尽管那时您可能需要查看 browserify 以组织您的代码并为部署准备您的 Web 应用程序。以防万一,请查看一下。

结论

现代快速 JavaScript 引擎,加上大量特定工具来帮助您构建、测试或部署您的系统,使得创建“JavaScript 一路到底”的 Web 应用程序成为可能,帮助您的开发人员提高效率,并让他们有可能使用他们已经精通的相同工具在客户端和服务器端工作。对于现代开发,您肯定应该认真考虑这一点。

资源

https://mdn.org.cn/en-US/docs/Web/JavaScript 了解 JavaScript 版本、功能等方面的最新信息。

https://github.com/google/traceur-compiler 获取 Traceur 编译器。CoffeeScript 可以在 https://coffeescript.node.org.cn 找到,TypeScript 在 https://typescript.net.cn。您可以在 https://docs.google.com/document/d/11YUzC-1d0V1-Q3V0fQ7KSit97HnZoKVygDxpWzEYW0U/mobilebasic 阅读 AtScript Primer 的草稿版本。最后,有关 asm.js 的更多详细信息,请访问 http://asmjs.org

常见的客户端框架包括 AngularJS (https://angularjs.org)、Backbone (https://backbone.npmjs.net.cn) 和 Ember (http://emberjs.com) 等。对于 Backbone,您还可以考虑 Chaplin (http://chaplinjs.org) 或 Marionette (http://marionettejs.com) 用于大型 JavaScript Web 应用程序。MEAN.JS 在 http://meanjs.org,MEAN.IO 在 ,Sails 在 http://sailsjs.org

您当然应该使用像 jQuery (https://jqueryjs.cn)、MooTools (http://mootools.net)、Dojo (http://dojotoolkit.org) 或 Prototype (http://prototypejs.org) 这样的库。

使用“promise”来简化 Node 中的回调工作;您可以从 Q (https://github.com/kriskowal/q)、when (https://github.com/cujojs/when)、bluebird (https://github.com/petkaantonov/bluebird) 或 kew (实际上是 Q 的优化子集,在 https://github.com/Medium/kew) 等众多库中选择。您可以在 https://promisesaplus.com 找到标准的“Promises/A+”文档。promise 的替代方案可以是 async (https://github.com/caolan/async)。

对于服务器端软件包管理,请使用来自 https://npmjs.net.cn 的 npm。对于客户端软件包,要么添加来自 http://browserify.org 的 browserify,要么从 http://bower.io 获取 bower。

使用 Sass{less} 可以更轻松地处理 CSS;请注意,后者可以使用 npm 安装。

使用测试框架,例如 InternJasmineMochaChai、Should.js ()、Expect.js () 和 UnitJS 是完整的断言库,具有不同的接口,以满足您的偏好。(UnitJS 实际上包括 Should.js 和 Expect.js,以便让您在选择首选的断言编写风格时有更大的自由度。)PhantomJSZombie.js 允许您在不使用实际浏览器的情况下运行测试,以获得更快的速度,而 Selenium 是实际验收测试的首选。

使用 GruntGulp 部署您的系统。

加载 Disqus 评论