ITPub博客

首页 > 应用开发 > Node.js > MERN全栈开发 使用Mongo Express React和Node

MERN全栈开发 使用Mongo Express React和Node

原创 Node.js 作者:qinghuawenkang 时间:2018-11-06 09:01:54 0 删除 编辑


Web 开发经典丛书
MERN 全栈开发
使用 Mongo Express React 和 Node
[ ] Vasan Subramanian
杜伟 柴晓伟 涂曙光 译
北 京
Vasan Subramanian
Pro MERN Stack
EISBN: 978-1-4842-2652-0
Original English language edition published by Apress Media. Copyright © 2017 by Vasan Subramanian.
Simplified Chinese-Language edition copyright © 2017 by Tsinghua University Press. All rights
reserved.
本书中文简体字版由 Apress 出版公司授权清华大学出版社出版。未经出版者书面许可,不得
以任何方式复制或抄袭本书内容。
北京市版权局著作权合同登记号 图字: 01-2017-5754
本书封面贴有清华大学出版社防伪标签,无标签者不得销售。
版权所有,侵权必究。侵权举报电话: 010-62782989 13701121933
图书在版编目(CIP)数据
MERN 全栈开发: 使用Mongo Express React 和Node / (美)瓦萨尼•苏布拉玛尼安(Vasan Subramanian)
著;杜伟,柴晓伟,涂曙光 译. —北京:清华大学出版社, 2018
(Web 开发经典丛书)
书名原文: Pro MERN Stack
ISBN 978-7-302-49152-1
Ⅰ. ①M… Ⅱ. ①瓦… ②杜… ③柴… ④涂… Ⅲ. ①JAVA 语言-程序设计 Ⅳ.
①TP312.8
中国版本图书馆 CIP 数据核字(2017)第 322391 号
责任编辑:王 军 于 平
封面设计:孔祥峰
版式设计:思创景点
责任校对:牛艳敏
责任印制:沈 露
出版发行: 清华大学出版社
网 址: http://www.tup.com.cn, http://www.wqbook.com
地 址: 北京清华大学学研大厦 A 座 邮 编: 100084
社 总 机: 010-62770175 邮 购: 010-62786544
投稿与读者服务: 010-62776969, c-service@tup.tsinghua.edu.cn
质 量 反 馈: 010-62772015, zhiliang@tup.tsinghua.edu.cn
印 装 者:三河市君旺印务有限公司
经 销:全国新华书店
开 本: 148mm×210mm 印 张: 12.25 字 数: 330 千字
版 次: 2018 年 1 月第 1 版 印 次: 2018 年 1 月第 1 次印刷
印 数: 1~3000
定 价: 59.80 元
————————————————————————————————————————————————
产品编号: 076425-01

译 者 序
当我第一次接触 Web 开发领域时,当时最热门的 Web 网站开发技
术是使用 C或 Perl语言在服务器上编写 CGI(Common Gateway Interface)
程序,来处理客户端发起的 HTTP 请求。现在我虽然已经全然忘记了
Perl 语言的语法,但是仍然还依稀记得当年在使用这个强大而“变态”
的语言时的感受。随着 Web 的迅速发展, ASP、 PHP、 JSP、 ASP.NET
出现在我们的眼前,它们各具特色,为不同技术背景的开发人员进入
Web 开发领域提供了所需的武器。
当 Web 开发技术日渐成熟,各种最佳实践和模式逐渐被总结和沉淀
下来,“技术栈”这个术语开始出现。技术栈通常指的是开发一个完整的
Web 应用程序时所需的特定工具、库、框架的组合。印象中,第一个最
热门的 Web 开发技术栈是 LAMP(Linux、 Apache、 MySQL 和 PHP),随
着前端在 Web 开发领域中所占的比例越来越大,以及 Node.js 的流行,
MEAN 技术栈(MongoDB、 Express、 AngularJS 和 Node.js)逐渐异军突起,
影响力越来越大。由于 MEAN 技术栈的前后端全都使用了 JavaScript 语
言,这也使得“全栈工程师”这个称呼开始流行起来。
本书所讲述的 MERN 技术栈,和 MEAN 只有一“字母”之差。 MERN
将 MEAN 中的 AngularJS 更换成这两年明显更受欢迎、使用更加广泛的
React,使得自己相比 MEAN 更接地气,更适合有志成为“全栈工程师”
的 Web 开发人员阅读。

在一个开放、开源的时代,如果一本讲述开发技术的书籍同时也能
让你有机会了解和熟悉 GitHub,那显然是极好的。在阅读本书以及完成
本书中所包含练习的过程中,你很自然地就会学习到如何寻找高质量的
GitHub 开源项目,并将它们应用到你的应用程序中。
祝您享受阅读本书的全过程,并能收获良多!
最后是译者的几则重要声明:
● 本书部分重要段落是由译者柴晓伟的女儿 Momo 完成的。 谢谢
Momo。
● 译者杜伟的爱猫 Yuki 对本书任何章节都没有贡献。
● 译者涂曙光感谢 Phoebe 在翻译工作中所给予的所有支持。
译者
( 杜伟 / 柴晓伟 / 涂曙光 )
作者简介
Vasan Subramanian 使用过各种各样的编程语言, 从
8085 上手工编写 8 位机的汇编代码,一直到 AWS
Lambda。他热衷于通过软件解决问题,更喜欢寻找合
适的技术组合,帮助软件开发团队提高效率。他在
Corel、Wipro、Barracuda Networks 软件公司学习编程,
从事程序员工作的同时,也在这些公司中担任团队负
责人。
Vasan 就读于印度理工学院马德拉斯校区(IIT Madras)和印度管理学
院班加罗尔分校(IIM Bangalore)。他目前在 Accel 公司担任 CTO,为创
业公司提供各种技术指导。除了提供指导、编写程序(当然还有写书! )
之外, Vasan 也是半程马拉松爱好者,还参加五人制足球比赛。你可以
通过 vasan.promern@gmail.com 联系他,欢迎赞扬、批评,或是介于这
两者之间的一切意见。


目 录
第 1 章 引言····························1
1.1 MERN 是什么 ·············1
1.2 本书的目标读者··········3
1.3 本书组织结构··············3
1.4 格式约定 ·····················5
1.5 读者须知 ·····················7
1.6 MERN 的组件 ·············8
1.6.1 React ······················· 8
1.6.2 Node.js ················· 11
1.6.3 Express················· 14
1.6.4 MongoDB ············ 15
1.6.5 工具与库·············· 17
1.7 为何使用 MERN
技术栈 ······················· 19
1.7.1 清一色的 JavaScript
语言 ·····················
19
1.7.2 清一色的 JSON 数据
格式······················
20
1.7.3 Node.js 的性能····· 20
1.7.4 npm 生态系统 ······ 21
1.7.5 同构性·················· 21
1.7.6 它不是一个框架···· 22
1.8 小结 ···························22
第 2 章 Hello World·············· 23
2.1 脱离服务器的
Hello World················23
2.2 服务器搭建················27
2.2.1 nvm ······················ 27
2.2.2 Node.js ················· 28
2.2.3 项目······················ 29
2.2.4 npm ······················ 30
2.2.5 Express ················· 32
2.3 构建阶段的 JSX
编译···························· 34
2.3.1 分离脚本文件······ 35
2.3.2 转换 ····················· 36
2.3.3 自动化 ················· 38
2.3.4 React 库 ··············· 39
2.4 ES2015······················· 39
2.5 小结 ··························· 43
2.6 习题答案 ··················· 43
2.6.1 习题: JSX··········· 43
2.6.2 习题: npm··········· 44
2.6.3 习题: Express ····· 44
2.6.4 习题: babel ········· 45
2.6.5 习题: ES2015 ····· 45
第 3 章 React 组件 ···············47
3.1 Issue Tracker
(问题追踪)················· 47
3.2 React 类 ····················· 49
3.3 组件组装 ··················· 51
3.4 传递数据 ··················· 53
3.4.1 使用属性·············· 53
3.4.2 属性校验·············· 56
3.4.3 使用 Children······· 57
3.5 动态组装 ··················· 59
3.6 小结 ··························· 64
3.7 习题答案 ··················· 64
3.7.1 习题: React 类···· 64
3.7.2 习题:传递数据···· 64
3.7.3 习题:动态组装···· 65
第 4 章 React 状态··············· 67
4.1 设置状态····················67
4.2 异步状态初始化········71
4.3 事件处理····················73
4.4 从子组件到父组件的
通信 ···························74
4.5 无状态组件················77
4.6 设计组件····················79
4.6.1 状态与 props ········ 79
4.6.2 组件层次结构 ······ 80
4.6.3 通信······················ 80
4.6.4 无状态组件 ·········· 80
4.7 小结 ···························81
4.8 习题答案····················81
4.8.1 习题:设置状态···· 81
4.8.2 习题:从子组件到
父组件的通信 ······
82
第 5 章 Express REST APIs··· 83
5.1 REST··························83
5.1.1 基于资源·············· 84
5.1.2 使用 HTTP Methods
标识操作 ··············
84
5.1.3 JSON ···················· 87
5.2 Express·······················87
5.2.1 路由······················ 87
5.2.2 处理程序函数 ······ 89
5.2.3 中间件·················· 91
5.3 List API ······················92
5.3.1 服务器自动重启···· 94
5.3.2 测试 ·····················
95
5.4 Create API·················· 97
5.5 使用 List API ··········· 100
5.6 使用 Create API ······· 102
5.7 错误处理 ················· 104
5.8 小结 ························· 108
5.9 习题答案 ················· 109
5.9.1 习题: List API ···· 109
5.9.2 习题: Create API··· 110
5.9.3 习题:使用
List API ··············
111
5.9.4 习题:使用
Create API ··········
111
5.9.5 习题:错误处理··· 111
第 6 章 使用 MongoDB·······113
6.1 MongoDB 基础········ 113
6.1.1 文档 ··················· 113
6.1.2 集合 ··················· 114
6.1.3 查询语言············ 115
6.1.4 安装 ··················· 116
6.1.5 mongo shell········ 117
6.1.6 shell 脚本 ··········· 121
6.2 架构初始化·············· 122
6.3 MongoDB Node.js
驱动程序 ················· 123
6.3.1 回调 ··················· 126
6.3.2 Promises············· 127
6.3.3 Generator 和 co
模块 ···················
128
6.3.4 async 模块·········· 129
6.4 从 MongoDB 读取
数据 ·························131
6.5 向 MongoDB 写入
数据 ·························134
6.6 小结 ·························136
6.7 习题答案··················136
6.7.1 习题: mongo
shell ····················
136
6.7.2 习题:架构
初始化················
137
6.7.3 习题:从 MongoD B
读取数据 ············ 137
6.7.4 习题:向 MongoDB
写入数据 ·············138
第 7 章 模块化与 webpack··· 139
7.1 服务器端模块··········139
7.2 webpack 简介···········142
7.3 手工使用 webpack·····143
7.4 转换和打包··············146
7.5 库捆绑包··················151
7.6 模块热替换··············155
7.7 使用中间件实现
HMR ························158
7.8 调试 ·························161
7.9 服务器端 ES2015 ····163
7.10 ESLint ····················168
7.11 小结························176
7.12 习题答案················177

7.12.1 习题:转换和
打包 ·················
177
7.12.2 习题:模块热
替换 ·················
178
7.12.3 习题:服务器
端 ES2015········
178
7.12.4 习题: ESLint··· 179
第 8 章 使用 React Router 进行
路由························181
8.1 路由技术 ················· 182
8.2 简单的路由·············· 183
8.3 路由参数 ················· 185
8.4 路由查询字符串······ 188
8.5 使用程序进行导航···· 193
8.6 嵌套的路由·············· 196
8.7 浏览器历史·············· 200
8.8 小结 ························· 202
8.9 习题答案 ················· 202
8.9.1 习题:路由
参数
················· 202
8.9.2 习题:路由查询字
符串 ···················
203
8.9.3 习题: 使用程序进
导航 ··················· 204
第 9 章 表单························205
9.1 List API 中的更多过滤
功能 ························· 205
9.2 过滤表单 ················· 207
9.3 Get API ···················· 214
9.4 Edit 页面··················216
9.5 UI 组件 ····················220
9.5.1 数字输入框 ········ 221
9.5.2 Date 输入框········ 226
9.6 Update API···············232
9.7 使用 Update API ······236
9.8 Delete API ················238
9.9 使用 Delete API ·······239
9.10 小结 ·······················242
9.11 习题答案················242
9.11.1 习题:在 List A PI 中添加更多过滤
条件 ··············
242
9.11.2 习题:过滤
表单
············242
9.11.3 习题: Edit
页面
············243
9.11.4 习题: Date
输入框···········
243
9.11.5 习题:
Update API ····
244
第 10 章 React-Bootstrap ··· 245
10.1 安装 Bootstrap ·······246
10.2 导航 ·······················249
10.3 表格和面板············256
10.4 表单 ·······················258
10.4.1 基于栅格的
表单 ···············
259
10.4.2 内联表单 ······· 263
10.4.3 横向表单·······
265
10.5 提示 ······················· 270
10.5.1 验证消息······· 270
10.5.2 结果消息······· 272
10.6 模态对话框············ 279
10.7 小结 ······················· 284
10.8 习题答案 ··············· 285
10.8.1 习题:导航···· 285
10.8.2 习题:基于栅格
的表单···········
285
10.8.3 习题:内联
表单···············
286
10.8.4 习题:模态对
话框···············
286
第 11 章 服务器端渲染········287
11.1 基本的服务器端
渲染 ························ 288
11.2 处理 state ··············· 293
11.3 初始 state ··············· 296
11.4 服务器端 bundle ···· 298
11.5 后端 HMR·············· 301
11.6 配合路由功能的服务
器端渲染 ················ 306
11.7 封装 Fetch 操作 ····· 314
11.8 小结························319
11.9 习题答案················320
11.9.1 习题:后端
HMR··············
320
11.9.2 习题:配合路由功
能的服务器端
渲染 ···············
320
第 12 章 高级特性·············· 321
12.1 MongoDB 聚合······321
12.2 分页 ·······················331
12.3 高阶组件(Higher Order
Components)··········336
12.4 搜索栏····················345
12.5 Google 账号登录 ···351
12.6 会话处理················359
12.7 小结 ·······················367
第 13 章 展望 ····················· 369
13.1 Mongoose···············370
13.2 Flux ························371
13.3 部署 ·······················373
13.4 mern.io ···················375
13.5 同学们,下课········377


第 1 章
引 言
现今的 Web 应用程序开发和往日相比已然大不相同。今时今日可
以选择的技术如此之多,使得刚入门的初学者经常会面对这么多的选
择而感到无所适从。可供选择的不仅仅是整个技术栈(开发时所使用的
技术与工具集),就连开发过程中所使用的工具都面临同样的选择难
题。本书提出了一种名为 MERN 的技术栈,并讲解如何使用这个优秀
的技术栈开发一个完整的 Web 应用程序,读者也将通过本书了解
MERN 技术栈的方方面面。
在第 1 章,我将首先大致介绍一下组成 MERN 技术栈的各项技
术。我不会在这一章就开始讲述各个技术的细节,或是展示任何示
例,相反,我会从比较高的角度来纵览 MERN 所包含的各种概念。本
章的重点就在于解释这些概念,以及它们为什么会让 MERN 成为你构
建下一个 Web 应用程序的首选技术。
1.1 MERN 是什么
任何一个 Web 应用程序都是由多项技术构建而成。这些技术的组
合就被称为一个“技术栈”。技术栈这个词最开始是因为 LAMP 技术栈

而开始流行, LAMP 技术栈指的是 Linux、 Apache、 MySQL 和 PHP 这
几个开源组件的首字母缩写。随着 Web 开发方式的逐渐成熟,以及用
户交互方式逐渐走向了前端,单页应用程序(Single Page Application,
SPA)逐渐流行。 SPA 是 Web 应用程序的一个类别,它避免了在需要显
示新内容时刷新整个 Web 页面,当需要刷新页面时, SPA 会向服务器
发出轻量级的请求,然后使用返回的数据或代码段更新 Web 页面。相
比过去刷新整个页面的方式, SPA 的这种运作方式要好得多。由于
SPA 应用程序的大部分工作都是在客户端完成的,因此它的流行也导
致了前端框架的崛起。虽说看起来似乎关系不大,但伴随着 SPA 的流
行, NoSQL 数据库也逐渐开始流行。
MEAN(MongoDB、 Express、 AngularJS、 Node.js)技术栈是专门针
对 SPA 和 NoSQL 热潮而发展起来的早期开源技术栈之一。 MEAN 技
术 栈 中 的 AngularJS 是 一 个 基 于 模 型 - 视 图 - 控 制 器 (Model-ViewController, MVC)设计模式的前端框架。 MongoDB 是一个流行的数据
存储 NoSQL 数据库。 Node.js 是一个服务器端 JavaScript 运行环境,而
Express 是一个构建于 Node.js 之上的 Web 服务器。 MEAN 技术栈可以
说是迄今为止最流行的 Web 应用程序技术栈。
React 是一个由 Facebook 创建的前端框架,虽说它的出现并非直
接 针 对 AngularJS , 但 毫 无 疑 问 已 经 获 得 了 巨 大 的 关 注 , 并 成 为
AngularJS 的一名接棒者。它将 MEAN 技术栈中的“A”替换成 “R”,
于是这就给了我们一个名为 MERN 的技术栈。之所以说 React 并非直接
针对 AngularJS,是因为前者并未提供一个完整的 MVC 框架。 React 是
一个用来构建用户界面的 JavaScript 库,从这个角度看,它只涵盖了整
个 MVC 中视图(View)的部分。
虽说我们已经逐个确定了用来构成这个技术栈的各项技术,但是
仅仅靠它们还不足以创建一个完整的 Web 应用程序。在开发过程中,
还需要使用其他的工具, React 也需要一些与之配合的其他 JavaScript
库。本书会讲述所有这些内容,包括如何基于 MERN 技术栈来构建
一个完整的 Web 应用程序,以及如何使用其他辅助性的工具来简化开
发流程。

1.2 本书的目标读者
开发过 Web 应用程序,但不熟悉 MERN 技术栈的开发人员和架构
师,可以通过本书学习 MERN 技术栈。在阅读本书之前,读者需要对
Web 应用程序的运行原理有所了解。我们假设本书的读者已经了解了
HTML 和 CSS 的基础知识。如果你还熟悉版本控制工具 git 的使用,那
就更好了;你可以通过复制包含了本书中所有源代码的 git 代码库,来
尝试使用所有这些代码,并通过签出一个分支,亲手实践本书中所讲
述的每一个开发步骤。
如果你已经确定了在新的应用程序中将要使用 MERN 技术栈,那
么本书可以帮助你快速入门。即使你暂时还没有对技术选择做出决定,
阅读本书也可以让你了解 MERN 技术栈,并帮助你在未来更好地做出
技术决策。你将学到的最重要的事情就是,如何将多项技术组合在一
起,以共同创建一个完整的、有用的 Web 应用程序。当合上本书的封
底时,你将成长为一名合格的 MERN 全栈工程师或架构师。
1.3 本书组织结构
虽然本书的重点在于教会你如何构建一个完整的 Web 应用程序,
但本书的大半部分都将围绕着 React 进行。这么做的原因是因为,对于
大部分的现代 Web 应用程序而言,前端代码是它最主要的组成部分。
在 MERN 技术栈中, React 就是负责前端的组件。
本书的写作风格比较偏向于教程。换言之,只有亲手实践编写代码
并完成练习,才能从阅读本书中收获满满。本书中包含了许多代码清单
( 这 些 代 码 也 同 时 位 于 一 个 GitHub 代 码 库 中 : https://github.com/
vasansr/pro-mern-stack)。也可扫描封底二维码获取代码清单。我希望,
你不要仅仅复制粘贴这些代码然后运行,而是亲手在键盘上逐行编写
这些代码。我发现在学习的过程中,亲自编写代码非常重要。代码中
有一些细枝末节,例如所使用的引号的类型(单引号还是双引号),有时

都会严重影响到代码的运行。当你亲自编写代码时,你对这些细枝末
节的掌握程度会比仅仅阅读它们好得多。只有在你自己编写代码时发
生卡壳的状况,想要将自己的代码与我的代码进行比较时,才需要将
GitHub 上的代码库复制到本地,由于代码库中的代码已经被测试过并
确保它们的正确性,因此你可以将它们与你的代码进行对比,以定位
出自己代码中的错误。不要从本书的电子版中复制代码再粘贴到开发
IDE 中直接使用。因为本书排版后的样式并不一定能保证电子版中代
码的正确性,只有 GitHub 代码库中的代码才是唯一经过测试的版本。
每当对代码进行一次可以独立测试的修改时,我都会添加一 个
checkpoint(本质上就是一个 git 分支),这样你就可以在线查看两个
checkpoint 之间的 diff(差异)。在代码库的首页(README 文件)上列出
了所有 checkpoint 和 checkpoint 之间 diff 的链接。你会发现,首页上的
这些信息,要比直接查看整个源代码或是查看本书中的代码清单更有帮
助。 GitHub 上 diff 的详细程度要远远强过书中的文字描述。
相较于让每个章节都覆盖一个不同的主题或技术,本书将会采用
一种更有实效的“提出问题-解决问题”讲述方式。在本书结尾,你会
得到一个可工作的完整应用程序。但是在一开始,你会首先创建一个
简单得像 Hello World 那样的小例子。然后就像一个真正的项目一样,
你会不断地向应用程序添加更多功能。在你添加功能的过程中,会遇
到一项又一项的开发任务。对于每一个任务,我都会介绍完成它所需
要的概念和技能,然后再对这些概念和技能详加解释。因此,你会发
现每个章节都不会只单纯地讲解一个主题或技术;相反,每个章节都
会包含一组你在构建应用程序时所需要达成的目标。你将使用不同的
技术和工具来完成每个章节的目标。
本书包含了许多练习,它们能够启发你思考,或是需要你去互联
网上阅读一些相关的文档。这些练习能让你知道应该去哪里了解本书
中没有涵盖的知识点(例如某些非常高深的主题或 API)。
通过本书的学习所最终构建完成的应用程序是一个问题跟踪应用
程序。这是一个大多数开发人员都能理解的应用场景,而且类似于任
何一个企业应用,它也将拥有许多属性和需求。对于这样一个应用程

序,通常都将它描述为一个“CRUD”应用程序(CRUD 的意思是指对
数据库记录进行 Create/Read/Update/Delete 操作)。
1.4 格式约定
本书中所使用的许多格式都遵循了常见的范例,所以我不打算将
它们一一列举出来。但是,由于代码的显示格式并没有一个默认的统
一规范,所以我将列举出本书中使用的此类格式约定。
每一章都由多个小节所组成,每一小节都专注于构建和修改一组
特定的代码,这组代码位于整个可工作的应用程序中,可以被单独测
试。每个小节可能有多个代码清单,它们不一定可以被单独测试。每
个小节都在 GitHub 代码库(www.apress.com)中有一个对应的入口,在
那里可以看到小节结束时整个应用的完整源代码,以及当前小节和上
一个小节源代码之间的差异。你会发现这个代码差异视图对于识别每
个小节对应用所造成的影响是非常有用的。
所有对代码的修改都会显示在小节的代码清单中,但是它们的精
确性并没有百分之百的保证。可信赖且可运行的代码位于 GitHub 代码
库中。代码库中的有些代码甚至是在本书定稿之后才完成最后的修
改,所以来不及反映在书中。所有的代码清单都有一个清单标题,里
面包含被修改或创建的代码文件名称。
如果一段代码清单完整地包含了一个文件、类、函数或对象,那么
它就是一段完整的代码清单。完整代码清单还可能会包含两个或多个
类、函数、对象,但是不会包含多个文件。如果代码清单中的实体并非
是连续定义的,我会使用省略号标识出未被修改的代码片段。
代码清单 1-1 演示了一个完整的代码清单,它包含整个文件的完
整内容。
代码清单 1-1 server.js: Express Server
const express = require('express');
const app = express();
app.use(express.static('static'));
app.listen(3000, function () {
console.log('App started on port 3000');
});
与之对应的,一段不完整代码清单则不会列出整个文件、函数或
对象的完整代码。它会以省略号开始和结束,在中间部分也可能会使
用省略号跳过未被修改的代码。只要有可能,就会突出显示对代码所
进行的实际修改。被修改的代码会使用粗体进行标识,而未被修改的
代码则显示成正常的字体。代码清单 1-2 演示了一个包含一些微小修
改的不完整代码清单。
代码清单 1-2 package.json: 添加进行代码转换的脚本
被删除的代码使用删除线进行标识,如代码清单 1-3 所示。
代码清单 1-3 index.html: 对 Script 名称与类型的修改
正文中也会用到一些代码块,以针对代码所做的修改进行讨论。
这些代码块通常是从代码清单中摘出来的重复性内容。在这种情况
下,就不会再专门去定义一个新的代码清单,而且通常这种代码块也
只有一两行的长度。下面就是一个代码块的例子,它们来自于一个代
码清单,其中有一个词被重点标识出来:

所有需要在终端窗口被执行的指令都会显示成一段以$开头的代码
段。下面是一个例子:
$ npm install express
1.5
读者须知
你需要一台计算机,用来运行服务器端代码和执行其他诸如编译
之类的任务。你还需要一个浏览器来测试你的应用。我推荐你使用一
台运行 Ubuntu 的 Linux 计算机,或是使用一台 Mac,来作为开发服务
器。 Windows PC 在进行一些小的配置工作后也能胜任开发服务器的
工作。
如果你有一台 Windows PC,可以使用 Vagrant(www.vagrantup.com/)
运行一个 Ubuntu 服务器虚拟机。由于你最终还是会需要将代码部署到
一台 Linux 服务器上,因此最好一开始就使用 Linux 环境。如果觉得在
Ubuntu 服务器上编辑文件过于麻烦,则可以在虚拟机中安装一个
Ubuntu 桌面环境。当然,虚拟机也会因此需要使用更多内存。
直接在 Windows 上运行 Node.js 也不是不可以,但是本书中的代码
示例都是假设它会运行在一台 Linux PC 或者 Mac 上。如果你选择直接
在一台 Windows PC 上运行本书的示例代码,可能必须对代码进行一些
相应的修改,特别是如下一些场景:例如在 shell 中运行指令时,需要
使用复制来替代软链接(soft links),在极特别的情况下,还需要处理'\'
和'/'这两种路径分隔符的差异。
为了保持本书的简洁,我在书中并未包含如何安装程序包的教
程。在不同的操作系统中,安装程序包的方式是不同的。需要根据程
序包提供者网站上的安装指南来正确地安装它们。在很多时候,即使
我告诉你要去看看某个第三方库,我也不一定会将其网站的链接放在
书中。这是因为,首先,你需要去学习如何从互联网上搜索它们,其
次,由于 MERN 技术栈的快速演进,因此在撰写此书时所使用的链接
很可能在你阅读此书时就已经变了。

1.6 MERN 的组件
我会带你快速了解一下组成 MERN 技术栈的几个主要组件,以及
你在构建 Web 应用程序过程中将会使用到的其他库和工具。我只会简
单介绍一下这些组件的几个突出特性,它们的细节留待后续章节逐一
详述。
1.6.1 React
React 是 MERN 技术栈的基石。从某种意义上说, React 定义和决
定了 MERN 技术栈的基本架构。
React 是由 Facebook 维护的一个开源 JavaScript 库,它的目的是用
来创建被渲染成 HTML 的视图。与 AngularJS 不同, React 并非是一个
框架,而只是一个库。因此, React 并未强制你去实现某种框架模式,
例如,它并不要求整个应用一定要使用 MVC 模式。你只是使用 React
去渲染一个视图(MVC 中的 V),但是如何将应用的其他部分组合起来
则完全取决于你。
先来谈谈 React 为何会脱颖而出。
1. Facebook 发明 React 的原因
Facebook 的工程师一开始创建 React 是为了供自己内部使用,后
来才将它开源。既然市面上已经有了无数类似的库,为什么 Facebook
工程师还是决定要重新发明一个轮子呢?
React 并非诞生于我们平时所访问的那个 Facebook 网站,而是诞
生于 Facebook 公司中的广告部门。一开始,广告部门的工程师使用了
一种经典的客户端 MVC 模型,其中包含了常规的双向数据绑定和模
板。视图会监听模型的变化,然后通过更新它们自己来响应这些变化。
随着应用的复杂度越来越高,这种 MVC 模型很快就变得越来越混
乱。当一个变化导致了一个更新时,这个更新又会导致另一个更新(因
为更新会导致某些东西发生变化),而那个更新又会导致另一个更新,

如此反复下去。这些级联更新将变得难以维护,因为导致更新的根源
不同,在代码中更新视图也会存在细微差别。
然后他们想到,既然在一个视图中已经有了描述模型的代码,为
什么我们还需要处理这些东西呢?我们难道不是已经在复制代码,并
通过添加越来越小的代码段来维护转变吗?为什么我们不能使用模板
(也就是视图)自身来管理状态变化呢?
在那一刻,他们开始想到去构建一个声明式而非命令式的东西。
2.声明式
React 视图是声明式的。它的真正含义是说:作为一个程序员,你
不需要去担心当视图的状态或者数据发生变化时,如何去管理其产生
的影响。换句话说,你不需要担心由于视图的状态发生变化而对 DOM
所需要进行的转换或者修改。这是怎么做到的?
一个 React 组件声明了它的视图的样子,并给出要显示的数据。当
数据发生变化时,如果使用 jQuery 那种方式来写代码,你通常都会去
修改 DOM。但在 React 中并非如此。你什么都不需要做! React 库会
重新计算出新视图应该呈现出的新样子,然后将新视图相对旧视图的
变化应用到旧视图上。通过这样的方式,视图就变得一致、可预测、
易维护、也更容易理解。
那这岂不是会变得很慢吗?每当数据有变化时,这样岂不是会导
致整个屏幕都被刷新? React 通过它的虚拟 DOM(Virtual DOM)技术来
避免这个问题。当你声明视图的外观时,并不是以 HTML 或者 DOM
的方式来进行声明,而是以一种虚拟的展现方式来声明,这种展现方
式以某种数据结构保存在内存中。 React 可以高效地计算出虚拟 DOM
之间的差别,并仅仅将这些差别应用到实际的 DOM 上。由于计算虚
拟 DOM 之间差异的算法已经进行了非常极致的优化,因此相比于只
进行必要的 DOM 修改的手工更新方式, React 所采取的方式仅仅增加
了一点点负担。

3.基于组件
React 的基础组成部分是一个个的组件,每个组件都负责维护自己
的状态以及渲染自己。
在 React 中,你需要做的就是创建组件。然后,通过将组件进行组
合,就可以创建出一整个视图或页面。每个组件都包装了其数据的状
态以及视图(或者其渲染方式)。通过将整个应用分割成更小的组件,开
发人员同一时间都只关注在单个组件的开发上,应用开发的难度得以
大大降低。
组件之间通过共享状态信息的方式来相互交流,父组件通过只读
属性与子组件交流,子组件通过回调与父组件交流。在后续章节中将
更加详细地讲述这个主题,现在你只需要知道, React 组件具有良好的
内聚性,组件之间的耦合度也非常小。
4.没有模板
许多 Web 应用框架都依赖于模板,自动化地创建重复性 HTML 或
DOM 元素。开发人员也必须学习和熟练掌握这些框架所使用的模板语
言。但在 React 中并非如此。
React 使用了一个全功能的编程语言来构建重复性或条件性的
DOM 元素。这个语言就是 JavaScript。例如,当你想要构建一个 table
时,就使用 JavaScript 编写一个 for(...)循环,或者是使用一个 Array 的
map()函数。
用来表现一个虚拟 DOM 的是一种和 HTML 非常类似名为 JSX 的
中间语言。它让你可以以一种熟悉的风格创建嵌套式的 DOM 元素,
而不需要使用 JavaScript 函数手工编码构建。注意, JSX 并非是编程语
言;它是一种类似 HTML 的界面表现标记。它与 HTML 很类似,这
样你不需要花费太多时间就能很好地掌握它。后续章节会更详细地介
绍 JSX。
使用 JSX 并非是必要的;如果你喜欢的话,完全可以编写纯粹的
JavaScript 来创建虚拟 DOM。但是如果你对 HTML 有所了解,那么使
用 JSX 无疑将更加简单。不必担心, JSX 不是一门需要花大功夫才能

学会的陌生语言。
5.同构性
React 也可以在服务器上运行。这就是同构性的含义所在:相同的
代码既可以运行在服务器上,也可以运行在浏览器中。
如果需要的话(例如为了实现更好的 SEO(搜索引擎优化)), React
的这个功能使得你可以在服务器上生成页面。服务器和客户端可以使
用相同的代码。在服务器上,你需要某种能够运行 JavaScript 的引擎,
这就为我们引出了 Node.js。
1.6.2 Node.js
简单来说, Node.js 就是一个浏览器之外的 JavaScript 运行时。
Node.js 的创造者利用 Chrome 的 V8 JavaScript 引擎,让它成为一个可
以独立运行的 JavaScript 运行时。如果你对运行 Java 程序的 Java 运行
时有所了解,那么你就能够很容易地联想到 JavaScript 运行时: Node.js
运行时可以运行 JavaScript 程序。
1. Node.js 模块
在一个浏览器中,你可以加载多个 JavaScript 文件,但是前提是有
一个 HTML 页面来执行加载操作。你不能在一个 JavaScript 文件中引
用另外一个 JavaScript 文件。但是对于 Node.js 而言,并没有 HTML 页
面来扮演一个加载者的角色。不需要额外的 HTML 页面, Node.js 拥有
一个属于它自己的、基于 CommonJS 的模块化系统,来负责加载多个
JavaScript 文件。
模块就像是库。你可以通过使用关键字 require(这是一个在浏览器
环境中不存在的关键字),来引用位于另外的(符合 Node.js 模块化规范
所写成的)JavaScript 文件中的功能。这样你就可以将代码分散在多个文
件或模块中以更好地组织代码。当需要某个文件时,就使用 require 去
加载它。在后续章节中,我将仔细讲解使用 require 的具体语法,现
在,你只需要知道相比于浏览器中管理 JavaScript 文件的加载, Node.js

有更简洁的方法来实现代码的模块化。
Node.js 内置了一些编译成二进制格式的核心组件。这些组件使得
代码可以访问操作系统的功能,例如访问文件系统、网络、输入/输出
等。另外, Node.js 还内置了一些大部分程序都很有可能需要的辅助性
函数。
除了 Node.js 内置的核心模块和你自己编写的模块之外,从互联网
上还可以找到并安装天量的第三方开源库。为了安装这些第三方库,
我们就需要了解一个名为 npm 的工具。
2. Node.js 和 npm
npm 是 Node.js 默认的程序包管理器。你可以使用 npm 来安装第三
方的库(程序包),并通过它维护程序包之间的依赖性。 npm 中心库
(www.npmjs.com)是一个公开的库,任何人只要想将自己写的模块共享
给其他人,就可以将模块发布到 npm 中心库中。
虽然 npm 一开始只是一个 Node.js 模块的中心库,但它很快就变成
一个包含各种其他 JavaScript 模块的程序包管理器,尤其是那些用在浏
览器里面的(非 Node.js)模块。例如, jQuery 这个迄今为止最流行的客户
端 JavaScript 库也已经成为一个 npm 模块。实际上,虽然 React 基本上
只包含有客户端代码,而且可以作为一个脚本文件被直接引用于 HTML
文件中,但它仍然推荐通过 npm 进行安装。不过,当以程序包的方式
安装了一个 JavaScript 前端库之后,我们还需要将程序包中的代码都进
行适当的处理,才能在 HTML 文件中引用它们,浏览器才可以访问这
些代码。诸如 browserify 或 webpack 这样的 build 工具就是用来做这些
事情的。它们可以将你自己的模块和第三方库的代码整合、打包成单个
文件,使之可以被 HTML 文件所引用。
当撰写本书时, npm 已经是排名第一的模块和程序包中心库,它
拥有超过 25 万个程序包(数据来源: www.modulecounts.com)。在两年
前还排名第一的 Maven,到现在为止它的程序包也只有 npm 的一半。
这表明 npm 不仅是最大的,而且还是增长最迅猛的中心库。有一种说
法, Node.js 的成功在很大程度上多亏了 npm 以及围绕着它的模块生态

系统。
npm 模块不但容易编写、容易使用,而且它还具备一套独特的冲
突解决机制,以允许同一个模块的不同版本能并存于同一个项目中,
满足不同模块的依赖性需求。因此在绝大多数时候,你不必关心模块
版本问题,可以直接使用。
3. Node.js 是事件驱动的
Node.js 和其他使用线程来实现多任务的系统不一样,它使用了一
个异步的、事件驱动的、非阻塞式的输入/输出(I/O)模型。
大多数编程语言都依赖线程来实现并行事务。但在实践中,你认
为的所谓并行的代码其实都是由一个 CPU 处理器在执行,而非真正地
并行运行。线程机制允许在某些代码(阻塞式的)等待一些事件完成时,
让其他代码有机会运行,这样就给了开发者一种并行运行的感觉。需
要代码等待完成的事件,通常都是诸如读取文件、通过网络传输数据
之类的 I/O 事件。对于一个程序员来说,这意味着你能够以顺序的方
式编写代码。例如,在第一行代码中,你调用了一个打开文件的函
数,然后在下一行,你就拿到了一个可用的文件句柄。在代码后面所
真正发生的,是打开文件的第一行代码其实会被阻塞住,然后等到文
件被打开,才会继续运行后面第二行的代码。如果在这两行代码运行
的同时还有另一个运行着的线程,操作系统或者编程语言就会进行调
度,在打开文件的代码被阻塞时自动切换到另外的线程,运行那个线
程中的代码。
Node.js 没有采用线程这种机制。它依赖回调来让你知道一个等待
的任务在何时完成。所以,如果你写了一行打开文件的代码,可以给
它提供一个回调来处理打开文件的结果。在打开文件代码的下一行,
你可以继续写不需要文件句柄的后续代码。如果你对异步 Ajax 请求比
较了解,那么立即就会明白我刚才所说的。由于有了诸如闭包这样的
底层语言特性的支持, Node.js 编程天然就是事件驱动编程。
Node.js 通过一个事件循环机制来实现多任务。事件循环其实就是
一个队列,队列中包含了需要处理的事件,以及当事件发生时需要运

14 MERN 全栈开发 使用 Mongo Express React 和 Node
行的回调。在上例中,文件已被打开并可以对它进行读取操作就是一
个事件,这个事件会触发你所提供的用来读取文件内容的回调。如果
你对此并未完全理解,别担心,本书后面的例子应该会让你进一步明
白事件循环机制。
一方面,这种基于事件的机制既让 Node.js 应用可以拥有很好的性
能,同时也使程序员不需要去关注诸如信号量(semaphore)、同步锁等
用来同步化多线程事件的工具。另一方面,在使用这种机制之前,也
需要程序员进行一些学习与练习。
1.6.3 Express
Node.js 只是一个可以运行 JavaScript 的运行时环境。直接基于
Node.js 徒手编写一个功能完整的 Web 服务器既不容易,也没有必要。
Express 是一个用来简化编写服务器代码的框架。
Express 框架让你可以定义路由。路由是一种规范,它决定一个
HTTP 请求在匹配了某个特定模式后要执行何种操作。与其他 Web 应
用类似,由于匹配规范基于正则表达式,因此它具有很高的灵活性。
每个路由被匹配之后,要执行的操作是一个具体的函数,函数的参数
是被解析后的 HTTP 请求。
Express 会为你解析请求的 URL、主机头和参数。对于处理返回的
响应数据, Express 也包含一个 Web 应用所需要的全部功能。这些功能
包括设置响应代码(HTTP 状态码)、设置 cookie、发送自定义主机头
等。另外,你还可以编写 Express 中间件,这是一种可以插入到请求/
响应处理管道中的自定义代码块,它们可以用来实现一些通用的功
能,例如日志和身份验证等。
Express 并未内置模板引擎,但是它支持你挑选自己想要的模板引
擎,例如 pug、 mustache 等。但是,对于一个 SPA 应用来说,你通常
都不会想要使用服务器端的模板引擎。这是因为所有的动态内容都是
在客户端渲染的, Web 服务器只提供静态文件以及响应数据 API 的调
用。对于 MERN 技术栈来说,页面生成是由 React 自己在服务器端负

责处理的。
总之, Express 是一个 Node.js 上的 Web 服务器框架,它和许多其
他服务器端框架相比,并没有太大区别。
1.6.4 MongoDB
MongoDB 是 MERN 技术栈中所使用的数据库。它是一个 NoSQL
面向文档的数据库,拥有灵活的架构和一种基于 JSON 的查询语言。
我将在这个小节稍微介绍一下 MongoDB。
1. NoSQL
NoSQL 这个简称的意思,是“非关系型”。其核心概念是指,它
不是那种拥有表、列的(被称为“关系型数据库”的)传统数据库。我觉
得, NoSQL 相比于传统数据库,有两个最主要的特点。
第一个特点是通过将负载分布到多台服务器之上的水平扩展能
力。为了获得这种水平扩展能力, NoSQL 数据库牺牲了传统数据库中
很重要的一个保证:强一致性。换句话说,位于多个数据存储复制点
的数据在极短时间内可以是不一致的。有关这个特点的更多信息,请
阅读“CAP 定理”一文(https://en.wikipedia.org/wiki/CAP_theorem)。在
实践中,只有很少的应用需要做 Web 扩展,所以 NoSQL 数据库的这
个特点很少被用到。
NoSQL 的第二个特点就是它不需要是关系型数据库。对我而言,
这个特点比它的第一个特点还更重要。你不必一定要将你的数据想成
是数据表的行和列的格式。传统上,应用里面的数据和存储在磁盘上
的数据之间存在所谓的“阻抗失谐”。这个术语来自于电气工程行业,
其含义是指:不同地方的数据说的是不同的语言。在 MongoDB 中,你
可将存储在其中的数据认为是你在应用代码中所看到的数据,换言之,
存储在 MongoDB 中的就是对象或文档。使用 MongoDB,程序员可以
避免添加一个额外的转换层,在应用中的对象和关系型数据库中的数
据之间进行双向转换。我们所熟知的对象关系映射(Object Relational
Mapping, ORM)层就是一种典型的转换层。

2.面向文档
与关系型数据库将数据存储成关系式或数据表式不同, MongoDB
是一个面向文档的数据库。 MongoDB 存储的单元是一个文档或是一个
对象(关系型数据的存储单元是一个数据行),多个文档组合成一个集合
(关系型数据库中多个数据行组成一个数据表)。集合中的每个文档都有
一个唯一标识符。标识符会被自动索引。
让我们想象一下一张包含有客户名、客户地址、多行数据项的发
票存储结构。如果要在一个关系型数据库中存储这个信息,需要使用
两张数据表: invoice 和 invoice_lines,前者存储发票本身,后者存储发
票中的每一行数据项,两者通过一个外键关系进行关联。但是在
MongoDB 中则不需要如此。你可以将整个发票存储成一个单独的文
档,每次不管是读取还是更新,都是一个单独的原子操作。每个文档
都可以包含任何深度层级的子对象,而不仅限于两层父子对象。
现代的关系型数据库通过允许使用数据字段和 JSON 字段,可以
支 持 单 层 的 嵌 套 , 但 是 仍 然 和 真 正 的 文 档 型 数 据 库 相 差 甚 远 。
MongoDB 可以对多层嵌套的子级字段进行索引,而关系型数据库则做
不到这点。
面向文档存储格式的缺点是数据都是以非格式化方式存储的。这
意味着数据有可能会有重复,需要更多的存储空间。同样,类似重命
名一个主(类别)条目这样的操作,就意味着需要扫描整个数据库。但
是,一方面现在存储空间已经越来越便宜了,另一方面重命名主条目
这样的操作也很少发生,所以这些都不算大问题。
3.无预定义结构
在一个 MongoDB 数据库中存储一个对象不需要遵循一个预定义的
结构。一个集合中的所有文档也不需要都包含完全相同的字段。
由于具备这个特点,在开发的早期阶段,你不需要在存储结构中
添加或重命名字段。你可以在你的应用代码中快速地添加字段,而不
需要担心是不是要去写一个数据库迁移脚本。一开始,这看起来像是
天赐神器,但实际上,这只是将保证数据一致性的责任从数据库转移到

你的应用代码中。我发现,在大型团队和更稳定的产品开发中有一个严
格或半严格的数据结构是一个更佳的选择。使用诸如 mongoose(本书中
未作介绍)这样的对象文档映射库,有助于解决此类问题。
4.基于 JavaScript
MongoDB 的语言是 JavaScript。
关系型数据库有一门专门的查询语言: SQL。对于 MongoDB,查
询语言是基于 JSON 的:你可以通过在一个 JSON 对象中指定操作,来
创建、搜索、变更和删除文档。 MongoDB 的这种查询语言和英文并不
类似(你不会使用到 SELECT 或者 WHERE 这些单词),因此也更容易
使用代码来构造查询语言。
MongoDB 的数据也是以 JSON 格式进行交互。实际上,数据使用
了一种名为 BSON 的 JSON 变体来进行存储(BSON 中的 B 指的是二进
制),以更有效地利用存储空间。当你从一个集合中获取一个文档时,
它返回的就是一个 JSON 对象。
MongoDB 内置了一个 shell,它构建于一个类似 Node.js 那样的
JavaScript 运行时之上。通过 shell,你可以在命令行中使用强大而熟悉的
脚本语言(JavaScript)来与数据库进行交互。你还可以编写 JavaScript 代
码段,将之存储和运行于服务器之上(类似于传统的存储过程)。
1.6.5 工具与库
如果不使用任何开发辅助工具,创建一个 Web 应用的难度会大大
提高。我将简要介绍一些 MERN 技术栈并不包含但在本书的示范应用
开发过程中你将用到的其他一些工具。
1. React-Router
React 所提供的功能仅仅包括视图渲染和管理单个组件中的交互。
当涉及在组件的不同视图之间进行切换,并保持浏览器 URL 和当前视
图状态的同步时,就需要使用额外的库。
管理 URL 和浏览器 history 的功能被称为路由。它和服务器端

18 MERN 全栈开发 使用 Mongo Express React 和 Node
Express 路由所做的事情类似:解析一个 URL,找到这个 URL 所对应
的组件,执行组件中的相关代码。 React-Router 所做的不仅如此,它还
管理浏览器后退按钮的功能,有了这个功能,不需要从服务器端加载
整个页面,用户就可以在不同视图之间进行转换。我们可能曾经自己
动手做过类似 React-Router 的工具,但是 React-Router 是一个非常容易
使用的库,它提供了所有与路由有关的功能。
2. React-Bootstrap
最流行的 Bootstrap CSS 框架也被适配到 React 中。实现这个适配
的项目就是 React-Bootstrap。 React-Bootstrap 不仅提供了 Bootstrap 的
大多数功能,它所提供的组件和 widget,也让我们能够学习如何设计
自己的组件和 widget。
还有其他一些为 React 构建的组件/CSS 库(例如 Material-UI、
MUI、 Elemental UI 等)和一些独立的组件(例如 react-select、 reacttreeview 和 react-date-picker)。如果你需要用到某个组件,它们都是很
不错的选择。不过我觉得对于已经熟悉了 Bootstrap 的人而言, ReactBootstrap 是功能最全面的一个库。
3. webpack
当需要对代码进行模块化管理时, webpack 是不可或缺的。也有其
他一些类似的工具,例如 Bower 和 Browserify,同样可以实现客户端
代码的模块化和合并,但是我觉得 webpack 更易使用,而且它不需要
其他工具(例如 gulp 或 grunt)来管理构建过程。
我们将不仅仅使用 webpack 实现代码模块化,以及将客户端代码
构建成一个打包文件,同时也将使用它“编译”一些代码。在将 JSX
编写的 React 代码生成为纯粹的 JavaScript 代码时,就需 要使用
webpack 执行额外的编译步骤。
4.其他库
在解决某些常见问题时,会使用一些其他库。在本书中,我们会
在服务器代码中使用 body-parser(来将 POST 数据或 form 数据解析成

JSON 格式)、 ESLint(来确保代码遵循风格约定)和 express-session,在
客户端代码中会使用一些类似 react-select 这样的库。
1.7 为何使用 MERN 技术栈
现在,我们已经对 MERN 技术栈以及它的各个组件有了一些了
解。但是它相对于其他诸如 LAMP、 MEAN、 J2EE 等技术栈的优势何
在?毕竟,所有那些技术栈对于大多数现代 Web 应用而言,都能够满
足需求。总的来说,对技术的熟悉程度是软件开发效率的关键所在,
所以我不会建议 MERN 初学者盲目地使用 MERN 技术栈来开始他们的
下一个新项目,特别是在他们面临进度压力的情况下。我会建议他们
选择使用他们已经熟悉的技术栈。
但是 MERN 确实有它的独特之处。它非常适合用于构建在前端有大
量用户交互场景的 Web 应用。翻到前面的“Facebook 发明 React 的原
因”小节,重新把它读一遍,你就会豁然开朗。使用其他技术栈,你也
许能够实现同样的效果,但是你会发现使用 MERN 技术栈来实现是最舒
心惬意的。所以,如果你并非一定要绑定在某个其他的技术栈,而且也
有一点点时间来学习,你会发现 MERN 确实是一个好的选择。我会介绍
我喜欢 MERN 的几个原因,它们也能帮助你做出你自己的决定。
1.7.1 清一色的 JavaScript 语言
MERN 技术栈最棒的地方就是它全部使用的是同一个语言。无论
在后端还是前端, MERN 都使用 JavaScript。即使你要(在 MongoDB 中)
编写数据库脚本,你使用的仍然是 JavaScript。所以,你唯一需要了解
并熟悉的语言就是 JavaScript。
对于其他基于 MongoDB 和 Node.js 的技术栈,特别是 MEAN 技术
栈而言,也同样拥有这个优点。但是 MERN 技术栈要比它们更强的一
点是,你甚至不需要使用另外一门模板语言来生成页面。在 React
中,你使用 JavaScript 来程序化地生成 HTML(实际上生成的是 DOM

元素)。所以,你不但不需要学习另外一门新的语言,你还可以得到
JavaScript 的全部火力。与专门的模板语言不同,使用 JavaScript 生成
HTML 是没有任何限制的。当然,你需要学习 HTML 和 CSS,但是它
们都不是编程语言,再说不管怎么样,你都得学习 HTML 和 CSS(不仅
仅是学习那些标记,还需要学习其范式和结构)。
除了在写客户端和服务器端代码时不需要切换编程语言的这个显
而易见的优点,在应用的各个层都使用单一语言还可以让你在不同的
层之间共享相同的代码。可以共享的代码包括执行业务逻辑和执行数
据校验的函数。这些函数需要运行在客户端,这样用户输入数据之后
马上就可以得到反馈,用户体验更佳。它们还需要运行在服务器端以
保护数据模型。
1.7.2 清一色的 JSON 数据格式
当使用 MERN 技术栈时,每个组件都使用 JSON(JavaScript Object
Notation)格式来表示对象:不论是在数据库中,还是在应用服务器
上,还是在客户端,甚至在数据通信中。
我发现这种设计通常能免去我大量转换数据格式的麻烦。不需要
使用对象关系映射(Object Relational Mapping, ORM),不需要将数据
模型的结构分解成行与列,不需要对数据进行序列化与反序列化。使
用诸如 mongoose 这样的对象文档映射器(Object Document Mapper,
ODM)可以帮助你确保数据结构的一致性,使得开发过程更简单。
因此,我可以直接以原生对象的方式进行思考,并视之为理所应
当。就算是在 shell 中直接查看数据库中的数据,也不需要转换我的思
考方式。
1.7.3 Node.js 的性能
由于 Node.js 使用了事件驱动架构和非阻塞式 I/O,因此它是一个
性能良好、抗压性强的 Web 服务器。
虽然需要一点时间去习惯 Node.js 的事件驱动和非阻塞式模型,但

是当你的应用开始扩展并承受大量请求时,我毫不怀疑它的这些特性将
极大帮助你节省成本,并缩短在优化 CPU 资源和 I/O 问题上所花费的
时间。
1.7.4 npm 生态系统
上文已经提到过, npm 拥有数量庞大的可供所有人免费使用的程
序包。你遇到的任何问题都会存在一个解决那个问题的 npm 程序包。
即使那个程序包不一定能百分百地满足你的需要,但是你可以分叉
它,定制一个自己的 npm 程序包。
npm 站在了其他优秀程序包管理器巨人的肩膀上,它在最开始就
吸收了其他程序包管理器的大量最佳实践。我发现 npm 是迄今为止我
所使用过的程序包管理器中最容易使用且速度最快的。部分原因也是
由于 JavaScript 代码可以被压缩的天然特性导致大部分 npm 程序包的
体积都很小。
1.7.5 同构性
SPA 应用通常很难进行搜索引擎优化(SEO)。我们必须使用一些变
通的解决方案,例如在服务器上运行 PhantomJS 生成伪 HTML 页面,
或者使用 Prerender.io 服务来为我们做类似的事情。无论哪种变通方案
都会增加额外的复杂性。
对 MERN 技术栈而言,在服务器之外生成页面是它的一种天然特
性,不需要借助其他工具,就可以满足 SEO 的需求。这个特性是通过
React 所使用的虚拟 DOM 技术来实现的。一旦有了一个虚拟 DOM,
由虚拟 DOM 生成一个可渲染页面的逻辑就很容易被抽象出来。在浏
览器环境中,可以将虚拟 DOM 直接应用到真正的 DOM 之上。在服
务器端环境中,可以使用虚拟 DOM 生成 HTML 页面。实际上, React
Native 就是实现这种抽象的一个极致:它将虚拟 DOM 生成为一个移
动 App!
本书不会讲述 React Native,但是它能让你了解虚拟 DOM 这种抽

象所具备的广泛应用场景。
1.7.6 它不是一个框架
虽然许多开发人员并不在意,但是 React 让我非常欣赏的一个原因
在于, React 仅仅是一个库,而不是一个框架。
框架是固化的东西,每个框架都有它自己做事的方式方法。框架
会将它觉得你需要完成的事情的种种变数整合到它统一的体系之中。
而一个库,则只会为你提供构建应用所需要的工具。简单来说,一个
框架通过解决绝大多数可能遇到的问题来帮助你快速地开发应用。但
是随着项目的进行,框架的种种令人费解之处、它对于你要完成的事
情的种种假设以及它的学习曲线,都会使得你希望能够控制框架底层
的某些东西,尤其是当你有一些特定需求时就更是如此。
而如果只是使用一个库的话,一个经验丰富的架构师可以自由选
择一个库所提供的某些功能来设计他的应用,并构建出满足他的应用
之独特需求与特点的自有框架。所以,对于一个经验丰富的架构师,
或者非常独特的应用需求来说,虽然使用框架在一开始能更加快速地
起步,但是使用一个库仍然比使用一整个框架要更好。
1.8 小结
本书将教会你如何使用 MERN 技术栈来开发一个应用。我希望你
不仅阅读书中的文字,而是在阅读的同时,多思考、多练习。这也是
我准备如此多的示例的原因。另外,我还准备了一些帮助你进一步思
考的练习。本书将使用 MERN 这个最具有代表性的技术栈来完成一个
CRUD 应用程序。
如果你已经准备好了,就请继续阅读本书。让我们开始吧!

购买地址:

https://item.jd.com/12309244.html

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/26421423/viewspace-2218776/,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论
分享计算机前沿技术和国外计算机先进技术书籍。

注册时间:2011-11-08

  • 博文量
    54
  • 访问量
    104928