Node应用程序构建——使用MongoDB和Backbone(txt+pdf+epub+mobi电子书下载)


发布时间:2020-06-05 06:32:58

点击下载

作者:[美]Mike Wilson 著

出版社:人民邮电出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

Node应用程序构建——使用MongoDB和Backbone

Node应用程序构建——使用MongoDB和Backbone试读:

前言

Google在2008年发布了第一个版本的V8 JavaScript引擎时,兴奋就像安静波澜,在开发者社区荡漾开来。第一次(承诺),我们能够在客户端和服务器都使用JavaScript进行编程:使用一种语言来控制一切。Web应用程序已经开始变得更像桌面并且复杂程度也在膨胀,所以减少语言数量的依赖这个想法有利于使技术开放和透明,推出更加令人兴奋和不断突破的应用程序。

Ryan Dahl就是一个看到了新的机会的开发者,他没有浪费时间,马上将他原来写的非阻塞套接字库转换到新的V8引擎上,导致了Node.js的诞生。他发布的技术已经让原来荡漾着的兴奋变成一次重大范式变革,对于实时响应的应用程序的兴趣到达了高峰。Node.js不仅仅是一组套接字函数的集合,它提供了一个框架处理异步I/O,以全新的事件驱动编程模式作为基础。

网上的情况在过去的几年中已经快速发生改变,没有任何迹象表明会放缓。“社交”网络的爆发对我们来说意义重大:越来越多的人在线,已经不只是技术用户。互联网是所有人的,在这个新的空间里,赢家将是那些明白如何让在线体验温暖而人性化,真正让人与人之间联系起来的公司。

使用JavaScript来连接系统提供了一个优势,因为你可以快速从处理与个人用户交互的前端Web堆栈转换到处理后端的数据存储,以及之间的所有网络管道。你将能够把系统真正模块化;每个部分都是可插拨的,可以部署到资源最合适它的地方。不同于以前,你创建的应用程序将可以与用户群共同成长、共同呼吸。读者和假设

本书的读者应该了解网站和网络应用是如何组织在一起的。为了保持对核心技术的专注,本书略过“为什么”构建Web应用程序,而关注“如何”构建。

要完全理解书中的示例,需要一些JavaScript的知识。这些示例会进行透彻的解析,但是以前的知识能够帮助读者理解编写过程中做出的编程决策和背后的故事。

许多开发者使用NoSQL数据库是从关系型数据库系统转过来的。这本书并没有假设读者熟练掌握数据库设计,我详细讨论了在整个数据库架构阶段做出的设计决策的缘由。MongoDB对于SQL的概念是友好的,这是选择它作为这个项目数据存储的主要动机。

在本书的最后一部分,我将走出第一和第二部分中纯JavaScript环境的构建,讨论如何选择支持的工具和技术。不要求读者对其他的语言(比如Scala、Java、PHP和Bash脚本编程)有深刻的理解,因为深入探索这些概念超出了本书的范畴,所以我鼓励使用这些示例作为跳板,进行进一步研究。组织结构

这本书基本分为两部分,第一部分概述Node.js、Backbone.js和MongoDB(本书讲述的核心技术),第二部分是如何使用这些工具去构建一个具有社交网络风格的网站。如果是刚开始学习,建议先从第一部分获取一些背景知识,然后在第二部分深入学习。如果你已经熟悉JavaScript,可以直接跳过第一部分,会发现第二部分中的例子也是可以理解的。

下面是本书的组织结构。

第一部分 Node.js、Backbone.js和MongoDB简介

第1章 介绍和总览

本章介绍了JavaScript和贯穿全书探讨的一些核心概念。

第2章 Node.js

本章介绍了Node.js,并引导你开始第一个独立的应用程序。当你熟悉关键模块后,你会用它来建立一个完整的实时应用。

第3章 Backbone.js

接下来,您将探索Backbone.js如何使得JavaScript在浏览器上的编程更像是构建传统应用程序而不是网站。我们也会遇到维护基于JavaScript项目中的一些问题,引入模板将可视的HTML布局与功能性的JavaScript应用代码分离开。

第4章 MongoDB

喜欢MongoDB是因为它可以快速和简单的搭建,易于接口,并与Node.js应用语言相通。在这一章我们将着眼于如何做基本的查询和数据处理,以及当MongoDB使用增长时复杂随之增加,这些情况也是需要考虑的。

第二部分 建立社交网络

第5章 创建项目

如何构建项目并将项目中的文件组织在一起是面临的一个大问题。在这一章中配置Node.js和Backbone.js形成这个网站,为其余的代码奠定基础。

第6章 认证

在使用应用程序之前,你需要创建账号并登录。在这一章中解释了如何从数据库中获取用户,并且一旦拥有他们的数据,如何确保数据的安全。

第7章 用户界面

在基本结构和登录功能完成之后,这一章引导你建立网页,包含所有展现给用户的内容。这里我们将详细介绍模板和Backbone.js的使用,集成访问控制和Node.js。

第8章 交朋友

联系人列表就是这个网站背后的社交部分。在这一章中,你将学习到如何在列表中添加和删除联系人,将数据去规范化,存放到MongoDB中。对于一个来自关系型数据库环境的来说,这一章是很值得阅读的。

第9章 聊天

这一章在第8章创建的联系人列表的基础上,利用Socket.io添加了实时聊天的功能。你和朋友之间收发消息不需要重新加载页面。

第10章 实时互动

最后,我们将回顾书中构建的用户界面,利用Socket.io进行扩展,就像聊天列表一样。这将为网站增加活力,让用户及时知道联系人的上线和下线,将所有共享的消息空间变为可以交互的聊天室。第一部分Node.js、Backbone.js和 MongoDB简介第1章介绍与总览

互联网已经成为发展最快的技术领域之一,它还在加速。对于希望通过编写软件来获得收入的人来说,这既是好消息,也是坏消息。今天,优秀的开发人员拥有难得的机会,可以做他们喜欢的事,拓展视野,不断进步,并从工作中得到更大的满足,只要他们愿意付出必要的努力,去理解海量的、快速增长的知识。

精彩的职业生涯是有代价的。作为一名软件开发者,你必须不断寻找下一个优秀的工具,来帮助你更多、更好、更快地实现梦想。你在10年后面对的东西,与今天面对的东西会完全不同。本质上说,你需要多次再培训自己,才能保持好的状态。

马尔科姆·格拉德威尔(Malcolm Gladwell)在他2008年所著的《Outliers》一书中指出,专业水准的融汇贯通需要10 000小时的努力。即使是天才也需要投入时间来收获成功,平庸者和精湛者之间的差异归结于个人实践经验的多少。阅读本书这样的书籍,能让你成为精湛者。现在你正在花费额外的时间,接触这门前沿的手艺。未来即将到来,你会首先获得它的好处。

Node.js已经将一大批软件开发者引入了面向事件编程。无论你的技术背景如何,只要有开放的心态,放下对JavaScript的偏见,就会领悟到在这个疯狂应用多线程的世界里,单线程编程是多么强大。更重要的是,你会更欣赏事件处理,在其他编程语言中处理多线程问题时,也会有所帮助。

JavaScript是一种独特的编程语言,有时会被误解,现在它在开发人员的工具箱中终于得到了应有的地位。由于开发JavaScript应用程序的工具集不断完善和成熟,你会看到全球范围内这种编程语言的重要性在持续增长。1.1 打造一个社交网络

本书将手把手指导你搭建一个社交网络,类似于LinkedIn、MySpace或Facebook。使用Node.js,Backbone.js和MongoDB作为工具,你将学会如何创建一个快速响应的应用,并且可以扩展到几百万用户。

由于是例子,本书中介绍的许多组件会走捷径,使用Node或MongoDB提供的内建方法,来展示某项功能,但在“真正”大型的部署中是不合适的。如果出现这样的捷径,我会给出特别提示,并探讨如何转向更具扩展性或更容易修改的结构。本书的挑战是在清晰性和构建真正实用的应用之间取得平衡。

什么是社交网络?“社交网络”这个简单的词组似乎传达了很多意义(在行为科学领域,确实如此),让我们逐词解读,并应用于因特网。“网络”是互联的一组系统,它可以是任何东西,从遍布全国的高速公路网,到学校实验室里的一排计算机,或是名片盒里的专业联系人。“社交”这个词指的是生物体的交互(如动物或人),以及它们作为群体的存在。所以社交网络在这里指的是一群互联、互动的人。

在社交网络里,人的因素高于一切。在构建任何软件时,如果不首先(并且持续)考虑最终用户(不论是客户、教授还是你自己),就针对某一特定功能或目标进行开发,这就是不负责任。要抵制为了技术的缘故而继续编程之路的冲动,直至你能看清楚工作的最终目的。

我们说打造一个社交网络时,当然不可能是打造这里定义的社交网络。你要创建的是一个论坛,一条道路,让社交网络生根成长。系统的每一项功能都为这一目标服务,消除用户的障碍,提供足够的功能来推动、鼓励和促进沟通,而没有过多的修饰。这是一条充满困难的道路,但也是区分伟大和平庸的产品的唯一途径。1.2 模型—视图—控制器(MVC)

本书多处提到并使用“模型—视图—控制器(MVC)”设计模式,进行服务器端和前端编程。有人认为,MVC在Web上是随着Ruby on Rails的增长而流行的,但它最早是在20世纪70年代为Smalltalk平台开发的。

MVC的主要作用是将系统拆成3个弱耦合的部件。

模型:包含了要读取或操作的数据的一种结构。

视图:用户与模型产生互动的界面。

控制器:在视图和底层模型之间代理用户操作。

模型和控制器通常成对出现。在本书中,控制器的工作可作为用户和模型之间来回传递消息的合约。虽然一个控制器可以操控多个模型,但这样做不好,我们建议一个控制器对应一个模型。

视图不一样。就像现实生活中一样,软件往往提供多种方式来展现同一信息。例如,音频的文字版本包含了和原始内容一致的信息,但它的内容传播更容易、更方便。因特网到处都是这样的例子:很多网络服务的数据展现同时采用JSON和XML,用两种不同的格式提供相同的信息。1.3 纯JavaScript

Node、Backbone和MongoDB让你使用单一编程语言,专注于应用逻辑,最终降低系统各部分之间的连接次数。你会看到这是一种引人注目的编程方式,因为客户端UI、后端服务器逻辑和数据库持久之间的界限模糊了,几乎形成一个生动的系统。随着实时网络逐渐进入生活,情况就更清楚了。数据在各个应用中舞动,甚至经过多个用户之手,所有事情就像一齐发生在单个过程中。

有些缺陷是需要预先避免的。虽然连接方式很强大,使用同一种方言,但是在底层,你的工作仍然是跨浏览器、服务器和数据库的编程。有些JavaScript开发范式着眼点略有不同,这取决于它们的主要目标是服务于UI(如在浏览器中使用的Backbone)、验证(如在服务器端常用的Node)或持久(如本书中提到的MongoDB)。你需要时刻保持警惕,注意数据要去哪里,是否阻塞你自己的进程,如何监听进出的事件并做出反应。这可能是一个挑战,但就像其他有许多灵活部分的系统一样,在实验中会有许多有趣的收获。第2章Node.js

今天的互联网不同于20世纪90年代的互联网。在以前,用户和一个网站之间的交互基本上属于信息消费。Web服务器生成大量静态网页,用户在页面之间穿梭。当然也有动态元素,但互动的信息流主要是限于请求和应答。多年的研究都集中于优化客户端—服务器的信息流,可以肯定地说目前对这方面的理解很透彻。

大约在IE6开始出现时,一个微妙而根本的转变开始占据上风。因特网用户变得越来越适应和内行,电脑变得更为强大,宽带连接开始变得常见。人们使用因特网不再是主要为了信息和交易,而是在社交和娱乐上花更多时间。因特网现在是媒体频道,但与之前的电视、广播和报纸不同。

现在互联网络用户不是消费数据,而是产生难以想象的大量数据。Web服务器和浏览器作为信息消费者的传统观念仍然存在,但了解这一点仅仅看到了信息发布者很少一部分能力。现在的重点是使人控制他们的体验,并借助他们创造的数据去实时改变、改进和提高这些体验。这是一个崭新的世界,网络服务器和程序员不再是体验的来源,而是促进者。

Node.js是转向“因特网即体验”范式的新型技术之一。2.1 安装Node.js

如果你没有安装过Node.js,那么首先你得安装它。你可以从Node项目主页(http://nodejs.org)下载到适合自己的操作系统的安装包。主页提供了用于UNIX、Mac和Windows的二进制安装包。如果你真心喜欢冒险,可以从Node的GitHub版本库中找到链接,安装一个开发快照版。在撰写本书时,Node的稳定发行版本是。

Node提供了一个JavaScript运行时环境,你可以在任何时候使用命令提示符或终端窗口,输入node。考虑到本书的目的,你需要运行JavaScript源文件,而不是直接输入代码。要运行一个JavaScript文件,使用Node命令带一个参数,比如:node filename.js。

Node采用包管理工具npm进行管理,它让你在你的代码中引用第三方的库。包管理是使用Node的很重要的一个方面,没有它,你必须从头开始编写所有的应用,重新发明一些常见问题的解决方案,而这些问题已经被其他数以百计的开发者解决并共享了。

要用npm安装一个库,只要用库名作为参数来运行npm,比如:npm install async。

npm十分有用,不仅仅是进行简单的安装:它帮助你打包应用,控制库版本,甚至与朋友分享项目。在本书第二部分,我们将深入探讨npm。现在只要说你和npm将成为亲密的朋友。2.2 Express

本书中应用程序大量用到Express框架。依赖于Connect HTTP服务器框架,Express提供了视图渲染和一种描述路由的语言。

要安装Express,只要在工作目录中使用npm:npm install express。

例2-1利用Express构建了一个简单且非常实用的服务器。首先,通过调用express库中的express()函数对app变量初始化。require命令指示Node导入express库,并把它分配给一个局部变量,从而将它的功能提供给当前命名空间。例2-1 一个Express小应用

应用初始化时,定义了3个路由。

/stooges/[name]

期待一个stooge(配角)的名字作为输入。

/stooges/

如果上一个路由提供的名字未找到,给出反馈信息。

/

一个默认的路由,用于访问应用程序的主页。

在第1个路由,Express会比较提供的配角名称是否为“larry”、“curly”或“moe”,并打印信息。在下一个路由中,Express显示一条消息说明没有找到配角名称。第3个路由中,显示默认的“hello world”。

等一下,第1个路由中的next参数是从哪里来的?它又是什么?实际上,next是指一个函数。next命令指示Express尝试处理匹配当前请求的下一个路由。在这个例子中,输入URL/stooges/ 会被第一个路由(/stooges/:name?)处理,而不是被第2个路由(/stooges)处理。由于没有提供名字,switch语句逻辑会跳转到默认情况下,此时next()函数会被调用。下一个路由(/stooges)包含了预期的响应。

:name参数之后的问号表明name是一个可选的输入,即使不提供name,这个路由也将被加载。警告你会看到很多在线资源,用express.createServer()来实例化app,而不是用express()。函数createServer()已被弃用,尽管它可能在相当长一段时间内仍可使用,但最好避免使用它,让你的应用程序尽可能未来也能用。

在switch语句中,使用了一个三元操作符作为缩写的if/else语句:name ? name. toLowerCase() : ''。这相当于一个很长的代码块,检查变量name是否存在,如果存在,返回小写的字符串,否则返回一个空字符串。模板

模板让你将展现信息从程序代码中分离出来,使你的项目文件更易整理,并渲染出复杂结构的网页。虽然本书主要关注用纯JavaScript来创建和显示视图,但有时候你也会想用Express的模板功能来生成网页,或者只是为了更容易在一个网页中引导所有的JavaScript文件。我选择Jade作为引擎,因为它在概念上类似于CSS,能产生清晰、简洁的代码。

要安装Jade模板引擎,在工作目录中使用NPM命令。

例2-2在例2-1的基础上增加了Jade模板支持。3个路由都使用了res.send代替直接在屏幕上打印文字。Express被告知以.jade作为后缀的文件来渲染内容:要么用stooges.jade模板来渲染配角名字列表(或没有名字),要么用默认的index.jade来渲染网站的根。例2-2 使用Jade模板的Express应用

另外新增的是包含app.set('views'...)的3行代码。这个命令告诉Express所有视图都存放在命名为“views”的目录下,是相对于代码文件运行(-dirname)的下级目录。

虽然Jade是用来渲染视图的,但你可能已经注意到,代码中并没有提到或实例化Jade。这是因为Express渲染命令会负责加载任何需要的模板模块,在这个例子中是Jade。如果你没有安装Jade,你的应用程序将会在进行页面渲染时停止运行,并抛出调用栈。

例2-3展示了使用Jade的整个页面布局。这是围绕“真正”页面的部分,它们是网站每一个页面上的标题信息,开放的标签和元数据。body标签将包含单个视图渲染的布局,Express渲染模板时将会包含这个布局。例2-3 Jade布局(layout.jade)!!! 5警告缩进在Jade模板中具有重要含义。如果你从一个PDF文件中复制粘贴这段代码,空格缩进可能无法正确地保留,这取决于你使用的软件。

第一行“!!! 5”表明文档类型是HTML5。也可以写成doctype html,但谁高兴打那么多字?

例2-4中包含的模板应该保存在views/index.jade中。它包含网站根内容:没什么消息比一个“hello world”更简单。这个模板的第一行扩展了例2-3中创建的layout.jade模板,这将导致用户的网络浏览器渲染布局的内容。因为h1标签嵌套在模板的block content元素中,Jade知道它应该在布局中的block content被定义后再被渲染。例2-4 Jade风格的索引主页(index.jade)

你可能会注意到,例2-3的布局还包含一个block元素称为scripts,它没有被index模板使用。这个脚本块在网页浏览器中显示为空白。可以在你的模板中添加或删除任何block内容,为页面布局留下极大的灵活性。

例2-5在Jade模板中引入了条件逻辑。如果一个stooge变量被传递到模板,它将显示出这个配角的名字,否则它将显示一般的“没有找到”消息。例2-5 Jade风格的新stooges页面(stooges.jade)2.3 事件

事件是Node.js的活力之源,实际上也是JavaScript本身的活力之源。其他语言在多个并发线程中处理工作流,每个线程都将大部分时间花在等待阻塞的I/O操作上,如磁盘读取、数据库操作或通过网络获取信息。但JavaScript总被认为是一个基于事件的编程模型。早期的事件很简单,如一次鼠标点击、一个页面加载或一次表单提交。更晚期的用法包括完成一次数据库写入,或从磁盘读入文件的内容等事件。

JavaScript利用回调,通过另一种方式来解决问题。程序员编写称为“回调”的特殊函数,让它与特定的事件挂钩,在符合条件时执行,而不是管理长时间运行的进程。例2-1和例2-2的路由例子说明了这一点:Node.js为每个URL设置了特定的响应,然后只在Web浏览器访问这些路由时执行相应的代码。

Node.js包括一个专门的事件库可以供你准备自定义事件。假设你有一个网络应用程序,已经开放给其他开发人员。如果他们想扩展你的工作,有两种选择:基于你的代码创建一些原型函数,并重写所有行为不同的函数,或者直接修改你的代码,构建他们需要的特性。事件给出了第三种选择:在你的代码的特定位置触发一个事件,让所有监听者知道动作已发生,并注入他们自己的行为。

例如,如果你的应用程序包含一个登录功能,你可能有一个OnLoggedIn事件。后继的开发人员可以添加一个监听器来提供额外的功能,如连接到社交网站收集所有与登录者相关的新闻。例2-6 创建与处理应用程序事件

例2-6展示了怎样将3个无关的事件onApplicationStart、onApplicationRun和onApplicationStop串在一起,产生下面的输出。

在mainLoop函数执行之前,调用eventEmitter的on方法注册了ApplicationStart、ApplicationRun和ApplicationStop事件。这为每一个事件增加了一个事件监听器:从现在起,只要有事件发生,都会检查这些监听器是否匹配,匹配的回调函数将被执行。

屏幕输出突出了Node.js的一个重要特征:所有工作都在单个线程中进行。当事件发生并由回调响应时,调用方法会暂停直到回调执行完毕。这很重要,因为如果在回调期间发生了消耗大量运算资源的事情,原始函数将不会继续执行,直到全部工作完成。所以这个例子的执行遵循以下路线。

1.运行mainLoop,触发ApplicationStartEvent。

2.运行回调onApplicationStart。

3.继续执行mainLoop,触发ApplicationRun。

4.运行回调onApplicationRun。

5.继续执行mainLoop,触发ApplicationStop。

6.运行回调onApplicationStop。

7.回到mainLoop的执行,接下去没什么可做的,停止。2.4 Socket.io

Socket.io是你的朋友。它将消除制作实时网络应用程序时的乏味工作,处理所有跨浏览器兼容的问题,在你的后端Node服务器和前端JavaScript客户端之间,留出干净、简单的JavaScript接口。这是一个令人激动的库,因为作为一个程序员,它让你专注于单一脚本语言的程序代码,消除数据和最终用户之间的网络障碍。

要安装Socket.id,就用npm。

例2-7为网站添加了实时聊天功能,它创建了一个Socket.io对象,并将其附加到http.Server上,在Express之前。在接到浏览器的socket连接时,Socket.io触发应用内的一个回调函数,向连接用户发出任意的一段欢迎消息。sendChat函数是为了方便而创建的;给定标题和内容,它使用Socket.io的emit命令发送一段JSON数据给连接的socket。因为它在回调函数之内,所以对所有socket级别的事件是可见的,但对应用的其他部分是不可见的。例2-7 为Express服务器添加实时聊天功能

socket.on('chat'…)这行代码创建了一个事件回调,每当连接的用户往socket发送消息时,就会执行。这里没有太多功能,服务器只是将用户发来的消息直接显示给他们。

为了给这个例子增加点活力,我已经将一个JavaScript的setInterval函数与其回调函数包含了进来,每隔5秒钟,它会向连接的客户端随机发送3个配角的一句口头禅。

在例2-7中,Express服务器的实例化和之前的例子不同。它不是让Express对象直接监听传入的连接,而是先通过http.createServer(app)函数附加在http.Server中,得到的服务器对象再监听进入的连接。在幕后,Express的listen命令做同样的事而不暴露http.Server。采用这种方式,你要暴露http.Server,以便让Socket.io连接到它。

例2-8的聊天布局在block scripts中加入了内容,在浏览器中渲染时,它在HTML内容中加上了Socket.io的功能。Socket.io使某些文件可供下载,这就是为什么你可以包含对/socket.io/socket.io.js的脚本引用。JavaScript文件socket.io.js包含了浏览器连接到socket服务器所需的全部功能,它还包含回退机制,为过时的、不支持Web socket的浏览器提供类似socket的功能。例2-8 聊天页面的Jade模板(chat.jade)

一旦提交按钮被激活,Socket.io会发出聊天框的内容。在从网络服务器接收聊天事件后,Socket.io会把传入的消息内容替换到聊天显示窗口(div#chat)。重要的是,要注意到socket事件和函数不论用在客户端上还是网络服务器上都是完全相同的。这让你为整个应用程序提供了一个干净的通信联系,无需担心传输或接收的数据需要转换。2.5 模块与CommonJS

Node引发了服务器端JavaScript编程的巨大兴趣,更别提对一般JavaScript的影响了。但这不是JavaScript第一次脱离浏览器作为工具,实际上,JavaScript在网景公司的浏览器中首次亮相之后不久,该公司发布的一个Web服务器就包含了该语言。不到一年后,微软的因特网信息服务(IIS)服务器软件也开始支持服务器端JavaScript(他们的方言称为JScript)。大约在同一时间,网景打算使用Java编程语言重写他们的旗舰Web浏览器,该项目最终产生了Mozilla的Rhino JavaScript引擎。

如果这听起来像是一个快速碎片化的市场,请想象一下试图为一个现有服务器产品编写JavaScript代码,然后在其他地方重用同样的代码。如果你的应用足够小,你或许可以通过一些修改对付过去。任何正常大小的应用程序很快会发现,这时需要调用外部库和模块化组件,否则最后将得到一个乱成一团、无法维护的几千行脚本代码的大文件。每个服务器都有不同的方法来划分应用代码,但没有统一标准。一旦选用了一种,你就被套牢了。

情况并没有好多少,虽然今天在代码开发标准化、命名空间保护、对象创建和模块化方面已经取得了重大进步。CommonJS旨在为脱离浏览器的JavaScript提供一套标准规范,其中许多已经被Node采用。如果你刚开始使用Node,而你的应用程序已经大到不合适放在一个文件中,那么你必须知道,你的应用无法使用外部文件中声明的变量,除非显式地使用exports关键字使它们可见。

例2-9展示了一个简单的Node.js模块,它暴露出一个函数:getFlagWidth,用于计算美国国旗的法定宽度,宽度必须1.9倍于国旗的高度;这个比例存储在名为FLAG_WIDTH的变量中。函数getFlagWidth获取高度值并乘以宽高比例,为给定的国旗高度产生对应的宽度。例2-9 计算国旗宽度的模块

如果你在应用中包含这个文件,你将能够访问getFlagWidth函数,它使用exports关键字暴露接口,而不是FLAG_WIDTH变量。FLAG_WIDTH可以认为是一个“私有”变量,只能在该模块的上下文中访问,而不能在整个应用范围内访问。第3章Backbone.js

Backbone.js是一个面对客户端的JavaScript模型—视图—控制器(MVC)框架。任何人,只要曾在一定规模的JavaScript项目上花过时间,都会看到这个语言很快陷入回调之网和金字塔式代码之中。在为网络浏览器编写代码时,几乎不能避免在应用逻辑中显示相关的代码,随着时间的推移,代码混合的情况变得越来越严重,越来越难以维护。改变业务领域的逻辑会影响视图,反之亦然。

Backbone旨在解决代码耦合问题,它提供一个带模板的模型—视图框架,分离了编程关注点。桌面应用开发者和服务器端开发者都应该对这种方式感到熟悉。

讨论Backbone就一定要讨论Underscore.js,它是Backbone的必不可少的辅助库。Underscore以工具函数的形式提供了函数式编程支持,如map/reduce、数组迭代和过滤以及高级对象绑定和链接。jQuery或Zepto,虽然没有严格的要求,也是Backbone支持的。特别是jQuery,将用于本书的应用开发过程中。3.1 模型

模型是Backbone应用的核心。虽然模型也可能是应用临时创造的,但大多数情况下,模型将代表存储在数据库中的对象。

Backbone的理念是让模型负责数据的存储、检索和转换。一些框架扭曲了MVC的这一目的,让控制器负责数据转换。在现实中,控制器的唯一职责是解释用户通过视图发出的请求,并访问模型的正确部分。模型本身需要理解如何处理接收到的数据,以及如何从数据存储中获取自身。

例3-1展示了一个模型类型如何利用Backbone来声明和初始化。这个扩展函数建立了一个Stooge类的原型链。因此你可以在使用Stooge时随时访问模型所有属性。所有特定子类的功能定义在extend的属性对象中。对Backbone,这是一个重要的概念:尽管其他库包含它们自己的extend方法,通常从一个类复制内容到另一个类,但Backbone中使用的方法创建了一个构造函数,因此你可以实例化你的类,将它复制到新类中,这样就能扩展许多层。例3-1 初始化一个Backbone模型3.2 视图

视图代表模型中数据的展现,通常根据上下文的需要会提供不同的信息。

例如,加拿大公民可以选择订购短版或长版的出生证明。两个文档(视图)呈现相同的重要信息(模型),但详细程度不同。Backbone中的视图提供了一个窗口,通向模型中的数据。作为开发者,你可以监听用户交互,或监听底层模型的变更,触发更新浏览器中显示的内容。

例3-2包含了一个Web页面,可以直接运行在浏览器中。jQuery、Underscore和Backbone库包含在页面的head部分,在所有代码之前。当你运行这个页面时,浏览器会等所有JavaScript都下载并加载到内存后,再继续执行。例3-2 初始化一个Backbone视图提示如果页面打算让公众使用,你应该尽量避免将script标记放在页面的head部分。网页浏览器会暂停下来,在继续渲染页面之前去下载这些脚本。所以,只有脚本包含渲染页面必不可少的功能时,你才应该在head部分中包含它。

CertificateView类是一个出生证明的占位符,它使用Backbone的extend方法,为这个自定义的视图创建一个原型链,这与例3-1中Backbone模型原型扩展的方式相同。这个视图有两个自定义函数。

initialize

在创建视图的新实例时执行。在上例中,要求是让视图立即渲染内容。

render

画出视图的内容,填充到下文指定的目标元素中。

最后,Backbone中的每个视图都关联到HTML DOM树中的一个元素。在例3-2中,你明确指示视图将自己渲染在ID为“certificate”的div中,但如果没有明确声明的元素,Backbone将使用通用的div。所以当我们到达new CertificateView({ ... })指令时,视图会先初始化,然后将内容渲染到ID为“certificate”的div中。视图模板

采用模板是使用Backbone的关键部分,所以越早介绍它越好,因为模板将在本书中扮演重要角色。

例3-2包含一个严重的反模式:它的HTML脚本包含了能够生成具体化HTML的JavaScript代码。

在例3-3中,生成页面所需的HTML被移出视图的render函数,放到一个脚本标签中。模板脚本被赋予“text/template”类型,以防止网页浏览器试图将代码当作JavaScript渲染到屏幕上。否则会导致一个错误,因为模板代码不是可执行的JavaScript代码。例3-3 在Backbone视图中渲染一个模板

我决定把证书拥有者的名字和出生日期作为变量输入,因此当它们在HTML中显示时,它们分别由变量名name和dob指定。将变量放置在脚本标签<%和%>之中是因为JavaScript引擎会生成它们的内容,而不是打印出“name”和“dob”,它们包含的值将被生成并替换进去。标签后放置的等于运算符(=),是使变量内容显示在屏幕上的速记符。所以如果name包含值“Steve Smith”,就会出现在

标签中。

使用视图中的模板需要两个步骤:首先,视图必须知道它将使用哪个模板;第二,模板必须有一组参数来渲染。利用jQuery的一个优点,模板的内容(记住,它们存储在脚本标签中,ID是tpl-certificate)用.html()函数提取出来,该函数返回一个tag中的原始HTML内容。下划线的.template()函数接受这段HTML内容,创建一个编译好的函数,以备将来使用。提示在JavaScript中,函数被认为是“一等公民”,这意味着它们可以被赋给变量,就像数字或字符串一样,然后被重新赋值,或作为参数在函数间传递。这是一个重要的特征,因为它支持函数式编程,下划线(_)为此提供了帮助。

现在要渲染模板,只要用期望的变量列表来调用新创建的template函数,得到的HTML将打印在屏幕上,就像例3-2中硬编码的HTML一样。数据目前仍然是静态的,templateArgs对象包含一个字典,其中的名称和值对应于模板中的name和dob变量。通常情况下,你会用一个独立的数据模型,而不是像这里明确定义的对象。3.3 集合

在Backbone中,集合为你的模型提供了状态,它将数据的读写代理给后端服务器,并在模型被读写时通知所有监听者。集合负责存储、检索和更新一组模型。大多数应用都使用某种类型的列表或菜单,集合非常适合存储这些数据。

为什么你愿意承担集合的开销,而不是使用更基本、更简单的JavaScript数组?原因之一在于,从集合扩展对象有助于提高代码可读性。在例3-4中,集合Team定义了Stooge作为它的模型类型,从而设定了这样的环境:如果Backbone在迭代或改变Team实例,就会寻找Stooge作为它的模型类型。当然,使用集合的更令人信服的理由是因为它们提供了一组强大的命令,让你能够控制它们的内容,甚至检索方法也比你自己写代码做相同的任务来得更容易。例3-4 在Backbone.js中建立集合

如果集合包含的模型被更新,集合就会发起一个事件,这对你在应用的其他地方更新显示内容是非常有用的。Sync

Backbone每次需要从服务器读写模型时都会使用Sync类。默认情况下,它使用jQuery的.ajax方法收发JSON数据。但根据需要,你可能想用不同的存储机制覆盖它。

在本书稍后你将使用Backbone.ioBind项目,作为Backbone默认同步命令的插入式替换。所有模型的CRUD(创建、读取、更新和删除)操作将转到Socket.io,通过JavaScript支持全面实时套接字操作,不需要新建网络连接。3.4 路由和历史

路由允许Backbone响应散列标签(#)的改变,显示新的资源。这为应用程序提供了深度链接能力,创建的链接可以作为书签,让游客分享。结合历史组件,路由可以与网络浏览器历史记录按钮挂钩,支持前进和后退导航。

这可能看上去很奇怪,Backbone缺乏基本控制类型。实际上,我们现在知道路由起源于一个控制器,每声明一个路由,控制器就被扩展。这种安排是有问题的,因为它紧密地耦合了控制器和路由的概念。控制器的意图是基于视图的输入来控制一个模型,而路由的意图是在视图之间为终端用户导航。通过将Router移到它自己的原型,Backbone的创造者优雅地区分了界面机身和操纵显示模块。

那么为什么没有专用控制器原型?控制器是应用程序逻辑的单纯实现。因为所有应用维护细节都从控制器中移除了,你可以在JavaScript中自由编写一个控制器,专门处理模型的输入输出。在这个层面上,理论上你的代码只有很少的重复逻辑,所以不需要一个基本控制器类型。

这个例子展示了如何连接路由,监听出生证请求,诸如#/certificates/123和 #/certificates/ mycertificatename,然后用例3-5中的CertificateView对象创建一个视图,显示指定的出生证。在这个简单的例子中,CertificateView实际上不接收任何参数,但如果它接收参数,在使用示例的URL时,id将包含123或mycertificatename。例3-5 Backbone路由示范第4章MongoDB

提到NoSQL数据库,很难不提到MongoDB的易用性。不仅因为它有良好的文档,强大的社区支持,也因为它对有SQL背景的开发人员非常友好(许多查询和大量的关系型思维可以直接从SQL移植到MongoDB)。对于NoSQL世界的入门者来说,MongoDB是特别有吸引力的系统。

在关系型数据库中,实体存储在由很多列组成的行中。因为实体通过严格的模式来定义,所以每一行有相同的列。处理实体就是比较列,开销很小,因为所有的数据按相同的格式进行设计。在MongoDB中,没有严格的模式定义,没有包含列的行,相反,每个实体存储在由任意数量的字段组成的文档中。

文档提供了强大的功能,你可以在文档中存储更多与实体有关的信息,甚至可以将一系列文档存入其他的文档。你也可以在单一操作中加载整个数据集,而不用为了获取完整信息而对数据库执行多次查询(在SQL数据库中你不得不这样做)。4.1 数据访问

MongoDB不仅对具有SQL背景的开发者友好,而且在它的网站上极力展示了多种SQL语句可以转化成MongoDB的查询。在所有数据库系统中,最终的目的都是写数据(通常持久化到一个磁盘),然后再将它读出来。

例4-1展示了MongoDB的一个简单会话。所有步骤都不需要特别的设置,需要做的仅仅是安装MongoDB并运行,然后用mongo客户端连接。一旦连接上MongoDB,我立即开始使用名为newdb的数据库,但不需要做任何初始设置,要做的只是开始写数据。例4-1 基本MongoDB用法

我创建了一个book变量,用来存放一本我读过的编程书籍的信息,包括作者、标题和出版日期。然后创建了books集,用db.books.insert命令插入它。在这个过程中,我从未定义模式。我从来没有告知MongoDB什么是book,它应该包含什么数据,甚至books集是什么。MongoDB负责创建文档,维护列表,甚至负责索引和约束(稍后我们将在本章讨论)。4.1.1 写入

正如你所看到的,向MongoDB写入数据时,格式非常自由。你可以很灵活,因为存储在数据库中的每一条记录实际上都是一个JSON文档,因此,可以用自由或结构化的方式来解析和利用。没有必要像传统的RDBMS一样,为每个表严格地定义一组列。在例4-1的基础上,你可以向这个数据库中添加另一本书的信息,而不用遵循前面输入数据的格式。

在例4-2中添加一本书时,我包含了一个名为keywords的字段,这个字段在例4-1添加的书中没有出现。这无关紧要,因为当我在后面查询books列表时,两本书的数据都会返回,即使它们没有相同的字段名。除了《50 Tips and Tricks for MongoDB Developers》,MongoDB也很容易地获取了《20 Recipes for Programming PhoneGap》的数据,即使它们的结构不完全一样。例4-2 插入文档

你可以想象,能够以如此随意的方式插入记录,会带来很强大的功能。你在用MongoDB构建应用时,程序代码负责控制要用的数据结构。任何时候你想添加一个新的字段,记录类型,甚至数据库,你只要声明并使用它,不必做任何其他事情。但是这种灵活性和强大的功能也伴随着一个管理上的问题:你的应用运行一段时间之后,它需要既能处理旧的数据格式,也能处理新的数据格式。这意味着你要么必须非常警惕所有的改变,要么必须精心设计应用,以适应数据的变化。

例4-2中简单的场景展示了这方面一个经典的例子。你向你的book文档中添加了keywords字段,从此之后每一个插入系统的新书都将包含一个keywords字段,它将暴露给下游某处的图书馆终端。如果那个终端尝试读取一本书,而keyword字段缺失,会发生什么情况呢?希望创建界面的开发者想到这一点,如果没有发现keywords,就显示一个空列表或一条特殊的信息。警告在创建应用逻辑时,你总是应该先检查数据库字段是否存在,然后再使用。否则即使数据库的行为完全符合预期,你也会得到一个破碎的应用。

如果想确保所有的文档都有keywords字段,你可以对整个集合发出一个更新操作,如例4-3所示。例4-3 向集合中所有的文档添加一个字段

例4-3展示了MongoDB中update命令的应用,带有完整的4个参数。

搜索条件

这个参数包含了所有的搜索条件,MongoDB用这些搜索条件来决定需要改变哪个记录。在这个例子中,没有给出条件,这意味着所

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载