Web 开发,如何集各家所长?

 

回顾Web 开发历史,随着框架和现代化工具简化日常开发,前端和后端开发人员的发展方向正在合并,两端的知识已经能够被同时熟练掌握。笔者日常工作中接触到的大部分高级开发人员,都具有全栈开发的能力。当前,全栈开发已经成为一种趋势。

MEAN 技术栈汇集了一些“同类别中最佳”的现代技术,包括MongoDB、Express、Angular、Node.js,形成了一种灵活强大的技术栈。

Node.js 是整个MEAN 技术栈的基础,提供了创建Web 服务器和应用程序的软件平台。Express 是Node.js 的Web 应用程序框架,抽象出了复杂和重复的服务器端任务。MongoDB 以速度快和扩展性好著称,对JSON 格式的支持,使它非常适合以JavaScript为中心的MEAN 技术栈。Angular 是用于为网站或应用程序创建界面的JavaScript 框架,虽然版本发布较为频繁,但变更点通常只是增量内容,从1.x 升级2.x 的不兼容现象不会再出现。MEAN 技术栈中的每个技术部分都有自身的挑战和问题解决方案。

MEAN 技术栈由单一语言构成,这意味着可以在前端和后端均使用JavaScript 编码。MongoDB 将数据存储在二进制JSON 中。Express 框架位于Node.js 之上,这部分代码是用JavaScript 完成的。前端是Angular,可以使用TypeScript。通常,学习新技术时最困难的部分是学习语言,单一语言的特性意味着MEAN 可能比想象中更容易掌握。

《MEAN全栈开发(第2版) 使用Mongo、Express、 Angular和Node》系统地讲述MEAN 技术栈。全书深入浅出地介绍MEAN 技术栈的各个技术部分的功能特性,而且以一个罗列附近免费Wi-Fi 信息的应用程序贯穿始终。在每一章中讲解相关的技术后,通过示例加以应用并逐步完善。对照实际项目开发,本书最后两章介绍身份认证的相关内容,包括会话管理、API 安全以及在Angular 应用程序中使用身份认证API 等技术。作为背景知识,本书附录讲述MEAN 技术栈中各部分的安装、相关配套技术的安装、应用中的数据处理以及JavaScript 基础知识。读者可以根据自身需求,优先阅读附录部分或将其作为参考资料。

                                                                                                                        颜宇 王威 谷守闯

节选自《MEAN全栈开发(第2版) 使用Mongo、Express、 Angular和Node》一书

------------------------------------------------------------------图书基本信息------------------------------------------------------------------------

书名:《MEAN全栈开发(第2版) 使用Mongo、Express、 Angular和Node》

ISBN:9787302551850

定价:128元

出版时间:2020年6月

-----------------------------------------------------------试读样章-----------------------------------------------------------------------------

 

第1 章

全栈开发介绍
本章概览:
● 评价全栈开发
● 了解MEAN 技术栈的组件
● 分析是什么让MEAN 技术栈如此引人注目
● 预览本书中将构建的应用程序
像我们一样,你可能迫不及待地想去钻研代码和构建应用程序。但为了保证你能
理解所有的内容,我们需要先花费一些时间讲解MEAN 是什么,看一看MEAN 技术
栈中的组件部分。
在谈论全栈开发时,真正讨论的是关于站点或应用程序的所有部分。全栈是指从
后端数据库和Web 服务器开始,中间包含应用程序的逻辑和控制,再通过一路传输,
抵达前端用户界面。
MEAN 技术栈由单一语言构成,包括以下四项主要技术:
● MongoDB(数据库)
● Express(Web 框架)
● Angular(前端框架)
● Node.js(Web 服务器)
MongoDB 以前的名字叫10gen,发布于2007 年,由MongoDB 公司积极维护。
Express 由T.J.Holowaychuk 在2009 年发布了第一个版本,它已经成为Node.js
中最受欢迎的框架。作为一个开源框架,Express 有100 多名贡献者在积极开发和维
护它。

Angular 是由谷歌支持的开源框架。2010 年发布的第一个版本被称作AngularJS
或Angular 1。如今,Angular 2 被简称为Angular,在2016 年发布后一直处于持续开
发和扩展中,当前的版本是Angular 7.1;Angular 2 不向上兼容AngularJS。
Angular 版本和发布周期
对于开发者社区来说,从Angular 1.x 升级到Angular 2 是件重大的事情,是个长
时间的变化,并且不向后兼容。当前,Angular 版本的发布更加频繁,大约每六个月
发布一次。当前版本是Angular 7.1,而且下个版本正处于开发中。
不用担心,因为后面的版本升级都只是一些增量变化,都是前后兼容的,不需要
重写所有代码。
Node.js 创建于2009 年,由Node Foundation 的核心成员Joyent 负责开发和维护,
Node.js 使用谷歌开源的V8 引擎作为核心部分。
1.1 为何学习全栈
为何学习全栈?这听起来会涉及很多令人厌烦的工作。这确实会带来很多工作,
但是,如果能够自己搭建完整的数据驱动型应用程序,这些工作将是值得的。关于
MEAN 技术栈,可能比你想象得要简单。
1.1.1 Web 开发简史
回溯到使用Web 的早期阶段,人们对于网站的期望值比较低。Web 的展现形式并
不被重视,构建网站时更关心展现在屏幕上的内容。比较典型的是,如果知道Perl 并
且能将它和HTML 串联起来使用,那么你已经是一名Web 开发人员。
随着互联网的普及,线上的展示形式给企业带来的收益开始变多。结合浏览器对
CSS 和JavaScript 的支持,直接导致前端实现变得复杂,不再是简单地对HTML 字符
串进行串联。你需要在CSS 和JavaScript 上花费时间,以确保它们看起来是正常的,
以预期的方式工作。前端工作需要兼容不同的浏览器,当初,浏览器的一致性比今天
要差很多。
这导致前端和后端开发人员之间出现了差异,图1.1 说明了随着时间的推移,前
后端开始分离。
当后端开发人员关注页面的背后逻辑时,前端开发人员在专注于构建更好的用户
体验。随着时间的推移,人们对这两个阵营的期望值变得更高,这鼓励着前后端分离

的趋势继续下去。开发人员不得不选择某个专业领域,集中精力专注于上面。

图1.1 随着时间的推进,前端和后端开发人员之间出现了差异
使用库和框架协助开发人员
在2000 年左右,在前端和后端,大多数常见语言的库和框架开始流行开来。想想
前端语言JavaScript 的Dojo 和jQuery,想想PHP 和Ruby on Rails 的Symfony。设计
这些框架的初衷是为了使开发人员的工作内容变得更简单,降低上手开发的门槛。好
的库和框架能抽象封装出复杂的开发场景,在提高开发效率的同时,降低对深度专业
知识的依赖。如图1.2 所示,知识深度上的简化使得能同时构建前端和后端的全栈开
发人员再次变得吃香。
图1.2 只是说明了趋势,我们并没有下结论说“所有Web 开发人员都必须成为全
栈开发人员”。纵观Web 开发历史,全栈开发人员这个角色之前就已经存在,再往前
看,开发人员更可能会选择成为前端或后端开发人员中的某一关。前面那些仅仅告诉
大家,使用框架和现代化工具可以简化日常开发,即便不在前端或后端开发人员之间
做角色选择,也能成为一名优秀的Web 开发人员。
使用框架的一个巨大好处在于能明显提升效率,前提是了解应用程序的全面技术
架构以及它们之间是如何联系在一起的。

图1.2 框架对分离后前后端Web 开发人员的影响
使用技术栈完成应用程序代码
随着过去几年框架的持续发展,越来越多的工作内容从后端迁移至前端完成,可
以想象成在前端完成后端的部分工作。能完成此类工作的JavaScript 流行框架有
Angular、React 和Vue.js。
以这种方式将应用程序代码在前端紧密结合,会使前端和后端之间传统意义上的
界限变得模糊。人们喜欢使用这种方式的一个原因在于能降低服务器端的负载,从而
也会减少开销。实际上需要做的只是将应用程序需要的计算能力外包,并推送到用户
的浏览器中进行加载。
我们将在1.5 节讨论这种方式的优缺点,并且说明在什么时候适合(或不适合)使用
这些技术中的某一个。
1.1.2 全栈开发的趋势
如前所述,前端和后端开发人员的发展方向正在趋于一致;前后两端的知识是能
够同时被熟练掌握的。如果是自由职业者、顾问,或者身处小团队中,多掌握一些技
能是极为有用的,这使你能向客户提供更多的服务价值。不再由不同的团队独立构建

组件,你能覆盖应用程序开发的全方面,这有益于你对项目的整体把控,也能够使团
队间的合作更加紧密。
如果你是大团队中的一员,那么你很可能需要专注(或者至少需要聚焦)在某个领
域。但是一般来说,了解你的组件如何适配其他组件是比较明智的,这有助于更好地
了解其他团队和项目的总体需求和目标。
最后,自己构建全栈应用程序是值得的。每一个技术部分都有各自的挑战和问题
解决方案,你会觉得十分有趣。当今的技术和工具能够提升这种体验,并能够更简单
快捷地构建大型Web 应用程序。
1.1.3 学习全栈开发的益处
学习全栈开发有很多益处。首先,当然是能够学习和使用新的事物和技术。其次,
你会对自己能够构建和启动完全由数据驱动的应用程序、掌握不同的技术而感到满足。
在团队中工作的益处包括:
● 通过理解不同的领域以及掌握它们之间如何协同工作,可以更好地理解全局。
● 你将知道团队里其他人在做什么,以及他们想取得成功又需要做些什么。
● 像团队的其他成员一样,可以更从容地考虑跳槽、换工作。
自己做全栈开发的额外益处包括:
● 可以自己实现应用程序端到端的连接,而不再依赖他人。
● 可以掌握更多的技能,为客户提供服务。
总之,关于全栈开发有很多益处。我们见到大部分最终成为全栈开发人员的人士,
他们在对项目整体情况的全面理解和掌控能力方面,有着巨大的收获。
1.1.4 为何专门介绍MEAN
MEAN 汇集了一些“同类别中最佳”的现代技术,形成了灵活强大的技术栈。
MEAN 的一个优点在于不仅在浏览器中使用JavaScript,而是自始至终都在使用
JavaScript。使用MEAN 技术栈,可以在前端和后端使用同一语言进行编码。也就
是说,使用TypeScript 完成Angular 部分将变得更常见。我们将在第8 章讨论这个
观点。
图1.3 展示了MEAN 技术栈的主要技术,并显示了每种技术的常见位置。

图1.3 MEAN 技术栈的主要技术
实现全栈使用JavaScript 的主要技术是Node.js,它将JavaScript 带到了后端领域。
1.2 Node.js 介绍
MEAN 里的N 指的是Node.js。位置处于最后并不代表不重要,Node.js 是整个
MEAN 技术栈的基础!
简而言之,Node.js 是一个允许你在它上面创建自己的Web 服务器和Web 应用程
序的软件平台。Node.js 本身不是Web 服务器,也不是独立的编程语言。Node.js 内置
了一个HTTP 服务器框架,这意味着不需要再单独运行服务器,比如Nginx、Apache
或IIS(Internet Information Services)。这使得Web 服务器工作变得更可控,当然也会增
加服务在启动和运行时的复杂性,特别是在线上环境中。
例如,通过使用PHP,你能够很容易找到一个共享服务器来运行Apache,可以使
用FTP 协议传输文件,在你的站点上一切都能运转良好。之所以能正常工作,是因为
Web 服务器已经帮助你配置好Apache。对于Node.js 来说,则会是另一种情况,因为
在创建应用程序时,需要设置Node.js 服务器。很多传统的网络服务对Node.js 的支持
都比较落后,但还是有些新的PaaS(Platform as a Service)服务在积极推进和解决这个需
求点,包括Heroku、Nodejitsu 和DigitalOcean。这些PaaS 主机部署线上网站的方式和
以前的FTP 方式有些不同,但如果你掌握了窍门,这些做起来也会比较简单。接下来,
本书将会在Heroku 上部署一个线上网站。
创建Node.js 应用程序的另一种方式是自己搭建一个专门的服务器,使用AWS 或
Azure 云服务商提供的虚拟服务器,在那里你能够安装需要的一切。但是,管理生产
服务器不是本书要讨论的话题!尽管可以将其他任意的独立技术组件替换掉,可一旦
替换Node.js,那么它上面的所有部分都需要改变。

1.2.1 JavaScript:MEAN 技术栈中唯一的语言
Node.js 能够广泛流行的一个主要原因是它只需要一种编码语言,很多Web 开发
人员都非常熟悉:JavaScript。在Node.js 发布之前,如果想成为一名全栈开发人员,
将不得不精通至少两门语言:前端语言JavaScript 以及PHP 或Ruby 这样的后端语言。
微软进军服务器端JavaScript
20 世纪90 年代,微软发布了Active Server Pages (现在被称为Classic ASP)。ASP 可
以用VBScript 或JavaScript 编写,但JavaScript 版的ASP 没有流行开来,因为在当时,
很多人都非常熟悉Visual Basic,而VBScript 看起来很像Visual Basic。很多图书和线
上资源都是关于VBScript 的,所以VBScript 慢慢成了Classic ASP 的标准语言。
随着Node.js 的发布,在服务器端可以使用已经掌握的知识。学习一种新技术时
最困难的部分是学习语言,如果现在掌握了部分JavaScript 知识,那么你已经领先他
人一步!
在使用Node.js 时,即使是一名经验丰富的前端JavaScript 开发人员,也会有一定
的学习难度。后端的挑战和阻碍与前端不同,但不管选择什么技术,都需要面对这些
挑战。在前端,你可能会担心在不同浏览器中是否所有内容都正常工作。在后端,更
多可能是了解代码流程,以确保流程不被阻塞以及系统资源不出现浪费。
1.2.2 快速、高效和可扩展
当编码方式正确时,Node.js 的速度会很快,因为它能高效利用系统资源,这是
Node.js 能流行起来的另一个原因。Node.js 的这些特性,使它相比其他大部分主流的
服务器技术,虽然花费更少的资源,却能负载更多的用户。业务负责人也很喜欢
Node.js,因为它能减少运行成本,即使在规模比较大的时候。
Node.js 如何做到这一点?Node.js 对系统资源很敏感,因为它是单线程的,而传
统的Web 服务器是多线程的。在接下来的部分,你将了解这些专业术语的含义,让我
们从多线程开始。
传统的多线程Web 服务器
当前大多数主流Web 服务器都是多线程的,包括Apache 和IIS。这意味着每个新
的访问者(或会话)会拥有单独的线程和对应的RAM,通常在8MB 左右。
以现实世界做类比,设想两个人一起进入一家银行并做互相独立的事。在多线程
模式下,他们将分别去找对应的银行柜员处理自己的需求,就像图1.4显示的一样。
从图1.4 中可以看到,Simon 找了银行柜员1,Sally 找了银行柜员2。双方都不知

道对方,也不会受到对方的影响。银行柜员1 处理Simon 的需求,在整个事件中没有
其他人;银行柜员2 和Sally 也是如此。

图1.4 多线程示例:访问者使用单独的资源
一名访问者和他的专用资源不会与其他访问者以及他们的专用资源发生接触
如果有足够多的银行柜员服务客户,这种方式会运行得非常完美。当银行变得忙
碌,客户人数超过银行柜员人数时,服务会开始变慢,客户必须等待。银行永远不会
担心这种情况,并乐于让客户排队,但网站和银行不一样。如果网站响应非常慢,用
户很可能会选择离开,并且永远不再回来。
这就是Web服务器通常准备如此多RAM的原因之一,尽管它们在90%的时间里不会
被用到。硬件配置就是这样,需要为很高的突点峰值做好准备。这种配置类似于银行额
外雇用50名全职柜员,因为通常银行在午饭时间会非常忙。

当然,这里有一种扩展性更好的解决方法,这就是单线程方式的由来。
单线程Web 服务器
Node.js 服务器是单线程的,并且工作方式不同于多线程服务器。Node.js 服务器
会将所有访问者加入同一个线程中,而不是为每位访问者分配独立线程和单独的资源
空间。访问者和线程之间只在必要时才发生交互,比如访问者请求某些内容,或者线
程响应某个请求。
回到刚才的例子,假设只有一名银行柜员服务所有顾客。但是与端到端的处理和
管理所有请求不同的是,银行柜员会将所有耗时的任务委托给后台员工,然后直接处
理下一个请求。图1.5对这个工作流程做了说明。

图1.5 单线程示例:访问者使用相同的中心资源
中心资源必须得到良好管理,以防止某个访问者影响其他访问者

在图1.5 所示的单线程方式中,Sally 和Simon 向同一个银行柜员发出请求。银行
柜员不是在下一个请求之前只处理它们中的某一个,而是在接受下个请求前,先接受
第一个请求并将它委托给最适合的人去处理,之后对下个请求执行同样的处理。当银
行柜员被告知请求任务已经完成时,就将处理结果传递给最初发送请求的访问者。
阻塞和非阻塞代码
在单线程方式下,记住所有用户会使用同一个中心进程是非常重要的事情。为了
保持流程顺畅,需要确保代码中不会有任何部分导致延迟,从而阻塞其他人的操作。
例如,如果银行柜员必须去保险箱为Simon 存钱,那么在这种情况下,Sally 的请求将
不得不处于等待状态。
相似地,如果中心进程负责读取每个静态文件(如CSS、JavaScript 或图片),它将
无法处理其他请求,从而阻塞流程。另一个可能导致阻塞的常见任务是与数据库的交
互。如果进程每次都去连接数据库,搜索或存储数据,进程将无法执行其他操作。
对于采用单线程方式完成的工作,必须确认代码里没有触发阻塞的内容。实现方
法是将任何会导致阻塞的操作,都处理成异步,以防止它们阻塞主要流程。
尽管这里只有一名银行柜员,但所有访问者都不会意识到其他人的存在,不会影
响到其他人的请求。这意味着银行并不需要多个柜员随时待命。当然,这种模式不能
无限扩展,但明显效率更高。银行使用更少的资源做了更多的事情。当然,也不是说
永远不需要再添加资源。
正如你将在本书后面看到的,由于JavaScript 的异步特性,使得这种特定方式在
Node.js 中可行。但如果你对这个特性不太熟悉,请查看附录D。
1.2.3 通过npm 使用预构建的包
npm 是一个包管理器,当安装Node.js 时,需要先安装npm。npm 提供了下载Node.js
模块和包的能力,使用它们可以扩展应用程序的功能。当前有35 万多个可用包,这能
为应用程序带来深层次的知识和经验。
npm 提供的包非常广泛。本书将使用其中的一部分,比如应用程序框架和数据库
驱动程序。其他示例包括辅助库(如Underscore)、测试框架(如Mocha)以及能给Node.js
控制台日志添加颜色的工具库(如Colors)。在第3 章开始构建应用程序时,将更详细
地探讨npm 是如何工作的。
如你所见,Node.js 非常强大和灵活,但是当你尝试创建网站或应用程序时,Node.js
并不能提供太多帮助。Express 能够为你提供一些帮助。需要使用npm 安装Express。

1.3 Express 介绍
MEAN 中的E 就是指Express。Node.js 是一个平台,它没有规定要如何设置和使
用,这是它的一大优势。但是每次创建网站和Web 应用程序时,都需要执行很多相似
的任务。Express 是Node.js 的一个Web 应用程序框架,旨在以经过良好测试、可重复
的方式执行这些任务。
1.3.1 简化服务器配置
如前所述,Node.js 是平台而不是服务器,Node.js 允许在服务器设置上执行创造
性的变更,执行无法在其他Web 服务器上执行的操作。这使得运行基本的网站变得更
困难。
Express 通过配置Web 服务器来监听发送过来的请求,并返回相关的响应来解决
这一困难。此外,Express 定义了一个目录结构。可通过配置一个文件夹,以非阻塞的
方式提供静态文件;你最不希望看到的是应用程序在有人请求CSS 文件时必须等待!
可以选择在Node.js 中直接进行配置,但Express 已经为你配置好了。
1.3.2 路由响应URL
Express 的一大特点是提供了一个简单的接口来引导传入的URL 到达一段对应的
代码。无论这个接口是提供静态HTML 页面、读取数据库还是写入数据库,这都不重
要。这个接口是简洁且明确的。
Express 在原生Node.js 中抽象出了一些关于创建Web 服务器时比较复杂的事情,
这使得代码能编写得更快、更易于维护。
1.3.3 视图:HTML 响应
你可能希望发送一些HTML到浏览器,用于响应从应用程序发送过来的很多请求。
到目前为止,不需要有多惊讶,相对于原生的Node.js,使用Express处理这些任务会更
简单。
Express 提供对很多模板化引擎的支持,通过使用可复用的组件和应用程序数据,
构建HTML 会非常简单,这是个聪明的方法。Express 会将这些代码编译在一起,并
以HTML 形式发送给浏览器。

1.3.4 通过会话记录访问者的信息
由于Node.js 是单线程的,因此在一次请求和下一次请求之间,无法记录访问者
的信息。Node.js 没有预留内存空间来做这件事,它看到的只是一系列的HTTP 请求。
HTTP 本身是一个无状态协议,因此没有存储会话状态的概念。现在,很难在Node.js
中创建个性化的体验,或者要求用户必须登录的安全区域;如果网站无法在每个页面
中记住用户的身份信息,网站将变得没有多大意义。当然,这些都可以实现,但必须
自己编码。
你永远猜不到:Express 也拥有解决方案!Express 可以使用会话,能在多个请求
和页面中识别不同的访问者。
Express 不仅为你提供了很好的帮助,而且为你构建Web 应用程序提供了良好的
起点。不需要担心,Express 帮我们抽象出了复杂和重复的任务。我们只需要关注如何
构建Web 应用程序。
1.4 MongoDB 介绍
对于大多数应用程序,存储和使用数据的能力至关重要。MEAN 技术栈选择了
MongoDB 作为数据库,MEAN 中的M 就是MongoDB。MongoDB 非常适合MEAN
技术栈。和Node.js 一样,MongoDB 以速度快和扩展性好著称。
1.4.1 关系数据库与文档数据库
如果以前使用过关系数据库,即使只是电子表格,也肯定习惯了列和行的概念。
通常,列用来定义名字和数据类型,每行是不同的数据条目,如表1.1 所示。




MongoDB 不是这样的!它是文档数据库。行的概念仍然存在,但列已被移除。
每一行都是文档,而不是在列中定义每行应该包含什么,文档会自己定义并保存数

据。表1.2 展示了文档集合的样子(进行缩进式布局是为了提高可读性,不表示列的可
视化)。

这种非结构化的方式意味着文档集合内部可能有各种各样的数据。
1.4.2 MongoDB 文档:JavaScript 数据存储
MongoDB 将文档存储为BSON,BSON 是二进制JSON(JavaScript Serialized Object
Notation)。不熟悉JSON 也无须担心,请查看附录D 中的相关部分。简而言之,JSON
是JavaScript 存储数据的一种方式,这也是为何MongoDB 非常适合以JavaScript 为中
心的MEAN 技术栈的原因。
下列代码片段展示了一个简单的MongoDB 文档示例:
{
"firstName" : "Simon",
"lastName" : "Holmes",
_id : ObjectId("52279effc62ca8b0c1000007")
}
即使对JSON 不太了解,也应该明白这个文档存储了Simon Holmes 的名字和姓氏。
不像普通文档那样设置每列对应的信息,而是包含名称/数值的对应结构,这使得这个
文档本身就很有用,因为它同时描述和定义了数据。
关于_id 的简短解释:你很可能注意到前面MongoDB 示例文档中名字旁边的_id,
_id 是MongoDB 在创建新文档时为文档分配的唯一标识符。
当在第5 章中开始向应用程序添加数据时,将会看到MongoDB 文档的更详细
信息。

1.4.3 不止是文档数据库
MongoDB 通过支持辅助索引和丰富的查询,使自身不同于许多其他的文档数据
库。可以创建多个索引,不必局限于唯一标识字段,查询索引字段的速度要快得多。
也可以给MongoDB 创建一些复杂的查询,这种级别的SQL 查询命令并不支持所有场
景,但对于大部分场景来说已经足够。
在本书接下来的部分,在构建应用程序时,你将获得使用MongoDB 带来的乐趣,
并开始确切了解MongoDB 到底能做些什么。
1.4.4 MongoDB 的不足之处
从版本4 开始,除了已经讨论过的明显差异外,传统的RDBMS 能做而MongoDB
做不到的事情非常少。MongoDB 早期版本的最大问题之一是缺少对事务的支持。本
书使用的版本是MongoDB 4,该版本已经具有执行多文档ACID(原子性、一致性、隔
离性、持久性)事务的能力。
1.4.5 Mongoose:关于数据建模更多的事
MongoDB 在文档中存储的灵活性,对于数据库来说是件好事。但大多数应用程
序需要一些数据结构。注意:是应用程序(而不是数据库)需要数据结构。那么,在哪
里定义应用程序的数据结构最合适?于应用程序自身!
为此,创建MongoDB 的公司又创建了Mongoose。用这家公司的话说,Mongoose
提供了“对于Node.js 最优雅的MongoDB 对象结构”(https:// mongoosejs.com)。
数据建模是什么
在Mongoose 和MongoDB 中,数据建模定义了文档中可以包含哪些数据,以及
必须包含哪些数据。在存储用户信息时,可能希望能够保存名字、姓氏、邮件地址和
电话号码。但其实只需要名字和邮件地址,而且邮件地址必须是唯一的。这些信息是
在模式中定义的,模式是数据建模的基础。
Mongoose 还能提供什么
除了建模数据之外,Mongoose 还在MongoDB 上添加了一层功能,这些功能对
于构建Web 应用程序很有用。Mongoose 使MongoDB 数据库的连接和数据读写操作
管理起来更容易。稍后你将用到所有这些功能。在本书的后续部分,我们将讨论
Mongoose 如何能够在模式层级添加数据验证功能,确保只允许验证后的数据保存到

数据库中。
对于大多数Web 应用程序来说,MongoDB 是很好的数据库选择,因为它在纯文
档数据库的速度和关系数据库的控制力之间提供了平衡。数据被有效地存储在JSON
中,这使得MongoDB 成为MEAN 技术栈的完美数据库选择。
图1.6 显示了Mongoose 的一些亮点及其如何适配数据库和应用程序。

图1.6 Mongoose 在数据库和应用程序之间是合适的,
它提供了易用的接口(对象模型)和对其他功能的访问,比如验证功能
1.5 Angular 介绍
Angular 是MEAN 中的A。简而言之,Angular 是一个用于创建网站或应用程序
页面的JavaScript 框架。在本书中,你将使用Angular 7,这是最新的Angular 可用版
本。所有以前的版本都已经被弃用,线上的文档不再适用。
可以使用Node.js、Express 和MongoDB 构建功能完整、数据驱动的Web 应用程
序,稍后你会在本书中这样做。将Angular 也加入MEAN 技术栈中,就好比在蛋糕上
加糖衣。
传统的做法是首先在服务器上完成所有的数据处理和应用程序逻辑,然后将
HTML 输出给浏览器。Angular 允许将这些数据处理和逻辑的一部分(甚至全部)转移到
浏览器中完成,通常只是让服务器传输数据库中的数据。等到讨论数据绑定时,我们
将了解这个过程,但首先需要解决的问题是,Angular 是否像jQuery 一样,是前端
JavaScript 库中的领先者。

1.5.1 jQuery 和Angular
如果熟悉jQuery,你可能想知道Angular 的工作方式是否与jQuery 相同。简而言
之,是不同的。通常,在HTML 被发送到浏览器并加载完文档对象模型(Document
Object Model,DOM)后,jQuery 被添加到页面,从而提升页面的交互性。Angular 通
常用在更早的步骤中,可以基于提供的数据和模板构建HTML。
另外,jQuery 是一个库,因此拥有一组特性,可以根据需求使用它们。Angular
被认为是一个固定的框架,这意味着必须按照规定的方式使用它。Angular 抽象了一
些底层比较复杂的内容,简化了开发体验。
如前所述,Angular 可以将HTML 和提供的数据结合在一起,但Angular 能做更
多的事:如果数据发生更改,可以立即更新HTML;如果HTML 发生更改,可以立
即更新数据。这个特性被称为双向数据绑定。
1.5.2 双向数据绑定:处理页面中的数据
要想了解双向数据绑定,请先考虑一个简单的示例,并与传统的单向数据绑定进
行比较。假设有一个Web 页面和一些数据,并且希望执行以下步骤:
(1) 将数据作为清单显示给用户。
(2) 允许用户在表单字段中通过输入文本对清单进行筛选。
单向和双向数据绑定在步骤(1)中是相似的。使用这些数据生成一些HTML 在终
端供用户查看。在步骤(2)中,事情变得有些不同。
在步骤(2)中,希望用户在表单字段中输入一些文本,展示筛选后的数据清单。通
过单向绑定,不得不给表单字段添加事件监听器,捕获具体数据以便更新数据模型(更
改最终显示给用户的内容)。
通过双向数据绑定,表单里的任何变更都会被自动捕获,直接更新数据模型以及
展示给用户的内容。这项功能听起来可能不算什么,但了解它有助于更好地了解
Angular,可以在步骤(1)和(2)中实现所有功能,而不需要编写任何JavaScript 代码!没
错,这都是使用Angular 的双向数据绑定完成的。当然,还要依赖Angular 一些其他
特性的协助。
1.5.3 使用Angular 加载新页面
Angular 是专门为单页面应用(SPA)设计的。实际上,单页面应用会运行浏览器中
的所有内容,整个页面不需要重新加载。应用程序的所有逻辑、数据处理、用户流和

模板传递都可以在浏览器中进行管理。
想想Gmail,它是一个单页面应用。页面会显示不同的视图和各种数据集合,但
页面本身永远不会完整地重新加载。
这种方式可以减少服务器上的资源,因为基本上已将计算相关的内容外包出去了。
每个人的浏览器都在辛苦地工作,而服务器只是根据需要提供静态文件和数据。
在这种方式下,用户体验也会更好。应用程序加载完毕后,对服务器的调用相对
来说会更少,从而降低了出现延迟的可能性。
所有这些听起来都不错,但肯定要付出一些代价。不然,为什么不是所有应用程
序都使用Angular 构建?
1.5.4 Angular 的缺陷
尽管有很多好处,但Angular 并不适合每个网站。像jQuery 这样的前端库适用于
渐进增强模式。背后的理念是,如果没有JavaScript,站点会运行得很好,但如果使用
了JavaScript,体验会变得更好。对于Angular 或者其他任何单页面应用框架,情况并
非如此。Angular 使用JavaScript 完成从数据和模板到HTML 的构建渲染,因此,如
果浏览器不支持JavaScript 或代码中存在错误,站点将无法运行。
这种对使用JavaScript 构建页面的依赖也会导致搜索引擎出现问题。当一个搜索
引擎抓取站点数据时,它不会执行所有JavaScript。使用Angular,在JavaScript 执行
之前唯一能做的事情是从服务器获取基本模板。如果百分百确信,想让搜索引擎抓取
数据和内容,而不仅是模板,那么需要好好考虑一下Angular 是否适合你的项目。
当然有能够解决这个问题的办法:简而言之,需要服务器输出的内容与Angular
编译后的一致。但是,如果不需要打这场仗,我们建议不要这样做。
可以做的一件事情是有选择地使用Angular。在项目中有选择地使用Angular 没有
什么错。例如,含有丰富数据的交互式应用程序或者站点的某个部分,非常适合使用
Angular 构建。应用程序也可能涉及博客或一些营销页面。这部分不需要用Angular 构
建,或者说通过服务器实现传统的服务会更适合。因此,站点的一部分由Node.js、
Express 和MongoDB 提供服务,另一部分由Angular 完成。
在MEAN 技术栈中,这种灵活的方式正是最强大的地方之一。使用MEAN 技术
栈可以做很多事情,但一定要记得在思想上保持灵活,不要将MEAN 当成单一的架
构栈。
不过,情况正在好转。Web 爬虫技术,特别是谷歌采用的技术,正变得越来越强
大,这个问题正在迅速成为过去。

1.5.5 使用TypeScript 进行开发
Angular 应用程序可以使用多种风格的JavaScript 进行编写,包括ES5、ES2015+
和Dart。到目前为止,最流行的是TypeScript。
TypeScript 是JavaScript 的超集,这意味着它是JavaScript,但具有附加功能。在
本书中,将使用TypeScript 构建应用程序中的Angular 部分。不必担心:我们将在第
Ⅲ部分从头开始介绍需要了解的TypeScript 内容。
1.6 相关配套支持
MEAN 技术栈提供了创建包含丰富数据的交互式Web 应用程序所需要的一切,
但是你可能还希望使用一些额外的技术来辅助实现这个目标。例如,可以使用Twitter
的Bootstrap 创建良好的用户界面,使用Git 协助管理代码版本,使用Heroku 托管应
用程序到线上URL。在后续章节中,我们将研究如何将这些技术和MEAN 结合在一
起使用。在本节中,将会简要介绍每部分技术可以帮你做些什么。
1.6.1 使用Twitter Bootstrap 创建用户界面
在本书中,将使用Twitter Bootstrap 创建一种响应式设计,需要的工作量极小。对
于MEAN 技术栈来说它不是必需的,如果计划使用已存在的HTML 或特定的设计构
建应用程序,那你可能不会使用它。但是,本书将以一种快速成型的样式构建应用程
序,从构思到应用程序,不受外部影响。
Twitter Bootstrap 是个前端框架,它为创建优秀的用户界面提供了大量帮助。其中,
Twitter Bootstrap 提供了响应式网格系统、许多接口组件的默认样式以及通过主题更改
视觉外观的能力。
响应式网格布局
在响应式网格布局中,会通过检测屏幕分辨率而不是试图识别出真实设备,在不
同设备上为独立的HTML 页面提供不同的样式布局。Twitter Bootstrap 针对4 个不同
像素宽度的断点进行布局,主要针对手机、平板电脑、笔记本电脑和显示器。如果对
设置HTML 和CSS 类有想法,可以使用HTML 文件,以适合屏幕大小的不同样式进
行布局,但仍展示相同的内容。

CSS 类和HTML 组件
Twitter Bootstrap 附带了一组预定义的CSS 类,这些类对于创建可视组件很有用,
如页面标题、警报消息的容器、标签和标记以及样式化的清单。Twitter Bootstrap 的创
建者在框架中做了大量思考。Twitter Bootstrap 可以协助快速构建应用程序,而不必在
HTML 布局和CSS 样式上花费太多的时间。
Twitter Bootstrap 教学不是本书的目标,但我们会在你使用它们时指出相关特性。
添加主题以获得不同的感受
Twitter Bootstrap 有默认的外观和风格,提供了整洁的基线,由于Bootstrap 被广
泛使用,你的网站最终可能会看起来和他人的网站有些相似。幸运的是,可以下载
Twitter Bootstrap 的其他主题,为应用程序提供不同的变化。下载主题通常和替换
Twitter Bootstrap CSS 文件一样简单。本书将使用免费主题构建应用程序,但可以从多
个网站购买高级主题,给应用程序一种独特的感觉。
1.6.2 使用Git 管理源代码的版本
将代码保存在计算机中或网络驱动程序上是可行的,但计算机或网络驱动程序只
会保存当前版本,并且只有你(或使用你网络的其他用户)能够访问它。
Git 是分布式源代码管理控制系统,允许许多人即使使用不同的计算机和网络,
也仍可以在相同的代码库中同时工作。他们可以一起推送代码,所有的变化都被记录
在案。如有必要,也可以回滚到代码的早期版本。
如何使用Git
Git 是典型的命令行工具,在Windows、Linux 和Mac 下都可使用。在本书中,将
使用命令行语句解决所有需要的命令。Git 很强大,在本书中你将透过表面去学习它,
我们所做的一切都将被提供为示例的一部分。
在典型的Git 配置中,你的计算机有一个本地存储库,在类似GitHub 或BitBucket
的地方还有一个远程存储库。可以从远程存储库拉取代码到本地存储库,也可以从本
地存储库推送代码到远程存储库。在命令行中执行这些任务都很容易,GitHub 和
BitBucket 都有Web 页面,这样可以直观地跟踪提交的所有内容。
在本书中使用Git 做什么
在本书中,使用Git 有如下两个原因:
● 在本书中,示例应用程序的源代码存储在GitHub 上,并针对不同阶段使用不
同的分支。可以克隆和使用主干或单独的分支代码。

● 使用Git 将应用程序部署到线上的Web 服务器,这样全世界的人都可以看到
它。你将使用Heroku 作为主机。
1.6.3 使用Heroku 作为主机
托管Node.js 应用程序可能会很复杂,但不是必须这样做。许多传统的共享主机
供应商没有跟上Node.js 的节奏。一些供应商为你安装服务器以便可以运行应用程序,
但是服务器的配置通常并不能满足Node.js 的特有需求。为了成功运行Node.js 应用程
序,需要配置应用程序的服务器,也可以使用专门设计用于托管Node.js 的PaaS。
在本书中将使用后一种方式。你将使用Heroku(https://www.heroku.com)作为服务
器托管供应商。Heroku 是Node.js 应用程序主要的主机供应商之一,拥有可使用的优
秀且免费的套餐。
Heroku 上的应用程序本质上是Git 存储库,这使得发布过程非常简单。配置完所
有内容后,可以使用单条命令将应用程序发布到线上环境:
$ git push heroku master
1.7 结合实际示例将它们结合到一起
正如我们已多次提到的,贯穿本书将使用MEAN 技术栈构建一个应用程序。在这
个过程中将为你提供每项技术的基础知识,并展示它们是如何被结合使用的。
1.7.1 介绍应用程序示例
那么,本书后续部分会构建什么?你将构建一个名为Loc8r 的应用程序。Loc8r
会列出附近有Wi-Fi 的地点,人们可以去那里完成一些工作。它还会显示这些地点的
设施、开放时间、评级和地图。用户能够登录并提交评级和评论信息。
现实世界中有一些功能相似的应用程序。基于地点的应用程序本身并不算特别创
新,并且可以通过多种方式呈现。Swarm 和Facebook 在登记清单中罗列了附近它们能
找到的所有信息。Urbanspoon 能帮助人们在附近寻找吃饭的地点,允许用户按照价格
和烹饪类型进行搜索。甚至连星巴克和麦当劳这样的公司也有自己的应用程序,用于
帮助人们找到它们的店铺。

真实数据或伪造数据
好吧,我们要在本书中为Loc8r 伪造数据,但你可以处理、封装它们。如果需要,
也可以使用外部数据源。对于快速成型的方式,通常会发现应用程序的第一个私有版
本的伪造数据能够加速应用程序的开发过程。
最终产品
你将使用MEAN 技术栈的所有技术部分创建Loc8r,包括使用Twitter Bootstrap
协助创建响应式布局页面。图1.7 展示了在本书中将要构建的应用程序的一些屏幕
截图。

图1.7 Loc8r 是你将在本书中构建的应用程序。它会在不同的设备上显示不同的样式,
显示出所有地点的清单和详细信息,允许访问者登录并留下评论

1.7.2 MEAN 技术栈组件如何协同工作
当读完本书时,你将拥有一个通过MEAN 技术栈完成的应用程序,该应用程序从
头到尾一直在用JavaScript 进行编码。MongoDB 将数据存储在二进制JSON 中,通过
Mongoose 以JSON 格式输出。Express 框架位于Node.js 之上,这部分代码也是由
JavaScript 完成的。前端是Angular,会使用TypeScript 完成。图1.8 对流程之间的连接
进行了说明。

图1.8 JavaScript(部分是TypeScript)是MEAN 技术栈中的通用语言,JSON 是通用的数据格式
在第2 章中,我们将探讨使用MEAN 的各种方法以及如何构建Loc8r。
因为JavaScript 在MEAN 技术栈中扮演着十分重要的角色,所以建议查看附录D,
里面对JavaScript 的隐患和最佳实践做了相关说明。
1.8 本章小结
在本章中,你掌握了:
● MEAN 技术栈的技术构成,它们是如何协同工作的。
● MongoDB 适合作为数据层。

● Node.js 和Express 如何协同提供应用程序的服务层。 ● Angular 如何提供惊人的前端数据绑定层。 ● 使用附加技术扩展MEAN 技术栈的一些方法。