第二部分:使用 React.js、Express.js、Node.js 和 MongoDB 构建 Web 应用






4.90/5 (18投票s)
这是一个关于使用 React.js、Node.js、Express.js 和 MongoDB 构建 Web 应用的多部分系列。
引言
一段时间以来,我一直在听到一些关于 React.js 的非常有趣的事情,比如它有多快,以及为您的下一个 Web 项目选择 React.js 会有多酷。这一切听起来都很好,但是,除非我们自己尝试,否则我们无法欣赏它。所以,我决定花点时间玩玩 React.js,亲眼看看这些说法有多准确。现在有一个问题,React.js 的文档假定您对现代 JavaScript 开发工作流程有一定的经验,简而言之,如果您是第一次使用它,它帮助不大。这个系列的文章是我尝试将所有碎片整合在一起,展示构建一个功能齐全的 React 应用需要什么。
在这个系列的文章中,我们将从头开始创建一个全栈的单页 JavaScript 应用程序。在本系列的第一部分中,我们将完全专注于 React.js 工作流程,并创建我们应用程序的前端部分,而没有任何后端 API 或数据库。在第二部分中,我们将使用 Express.js 为我们的应用程序创建后端,并将数据持久化到 MongoDB 中。
本文是本系列的第二部分,如果您没有阅读第一部分,您可能会觉得本文有点令人困惑,请先访问第一部分。
设置 Express 应用
我们将从第一部分的结束处开始。到目前为止,我们已经有一个功能齐全的前端 React 应用,接下来我们需要设置我们应用程序的后端,所以,如果您还没有安装 MongoDB,请先在本地安装。我准备的这个演示应用基于 MongoDB 版本 3.0.5 和 Node.js 版本 4.2.3,但旧版本也能正常工作。
接下来,在命令提示符中导航到我们应用的根目录,并运行以下命令:
npm install mongoose --python=python2.7 --save
npm install body-parser --save
npm install underscore --save
Mongoose 是一个文档对象映射器,我们将使用它与应用程序中的 MongoDB 进行交互。我们显式指定了 Python 版本,以避免您在系统安装了多个 Python 版本时可能遇到的安装问题。body-parser 模块用于解析 HTTP 请求的正文,因为 Express.js 没有内置支持。underscore 模块提供了大量非常有用的实用函数,使我们能够专注于应用程序的业务逻辑,而不必花费太多时间编写实用代码。
现在我们已经完成了设置,让我们开始编写一些 Express 代码。
实现 REST 端点
在应用的“server”目录下添加两个新目录:“data”和“controllers”。正如您可能猜到的,“data”目录将包含 Mongoose 数据模型,“controllers”目录将包含 Express 路由器。在我们的应用程序中,只有一个模型School,所以让我们在data目录中添加一个新文件“school.js”,并在其中添加以下代码:
var mongoose = require("mongoose");
var schoolSchema = mongoose.Schema({
name: String,
tagline: String
});
module.exports = mongoose.model("school", schoolSchema);
在上面的代码中,我们创建了一个非常简单的“school”模型,它只有两个类型为String的属性:name和tagline。接下来,让我们创建控制器,在controllers目录中添加一个新文件“schoolController.js”,并在其中添加以下代码:
var mongoose = require("mongoose");
var School = require("../data/school");
var _ = require("underscore");
var router = require("express").Router();
router.route("/schools/:id?").get(getSchools).post(addSchool).delete(deleteSchool);
function getSchools(req, res) {
School.find(function (err, schools) {
if (err)
res.send(err);
else
res.json(schools);
});
}
function addSchool(req, res) {
var school = new School(_.extend({}, req.body));
school.save(function (err) {
if (err)
res.send(err);
else
res.json(school);
});
}
function deleteSchool(req, res) {
var id = req.params.id;
School.remove({ _id: id }, function (err, removed) {
if (err)
res.send(err)
else
res.json(removed);
});
}
module.exports = router;
在上面的代码中,我们创建了一个新的 Express 路由器,它只有一个路由“/schools/:id?”,并且对于每个 HTTP 动词,我们都有一个单独的请求处理程序。为了简单起见,我们将在应用程序中使用 GET、POST 和 DELETE。我们的路由还有一个可选的路由参数,我们在删除特定学校时需要它来标识学校。为了进行 CRUD 操作与数据库交互,我们使用了我们在上一步中创建的School模型。
另外请注意,在addSchool和deleteSchool函数中,我们访问了req.body对象,该对象由body-parser中间件填充。我们将在下一步中配置它。我们使用underscore的extend实用函数,该函数将req.body对象复制到一个空对象中,以填充我们的模型。
现在,让我们按照如下所示修改“server.js”文件:
var express = require("express");
var bodyParser = require("body-parser");
var mongoose = require("mongoose");
var path = require("path");
//controllers
var schoolController = require("./controllers/schoolController");
//Express request pipeline
var app = express();
app.use(express.static(path.join(__dirname, "../app/dist")));
app.use(bodyParser.json())
app.use("/api", schoolController);
app.listen(7777, function () {
console.log("Started listening on port", 7777);
});
// Connect to mongodb database
mongoose.connect("mongodb:///schoolfinder");
我们在server.js文件中做了一些更改,我们需要body-parser和mongoose模块,我们已经配置了 bodyparser 来解析 HTTP 请求上的 JSON 负载,还将我们的schoolController挂载到了/api路由,因此任何形式为 /api/schools 或 /api/schools/xxxx 的请求都将由 schoolController 处理。此外,我们需要使用mongoose.connect函数连接到 MongoDB 数据库。schoolfinder是我们的数据库名称,当您插入第一条记录时,它将自动创建。
请确保在运行应用程序之前 MongoDB 实例正在运行,如果您愿意,也可以将 MongoDB 连接字符串更改为远程 MongoDB 服务器。
到目前为止,我们的后端 API 已经准备就绪,让我们重新审视 React 代码以实现 REST API 调用。
将 REST 连接到 React
我们需要一个库来向后端 API 发出 AJAX 调用,我将使用 JQuery,但您当然可以选择自己喜欢的库。我们还需要一个 Promise 库来避免嵌套且痛苦的回调。让我们使用 npm 安装JQuery和es6-promise模块:
npm install jquery --save
npm install es6-promise --save
接下来,在“app”目录中添加一个新目录“services”,并添加一个新文件“schoolService.js”。此文件将包含 REST API 调用,让我们在其中添加以下代码:
var $ = require("jquery");
var promise = require("es6-promise");
var resourceUrl = "https://:7777/api/schools";
module.exports = {
addSchool: function (school) {
var Promise = promise.Promise;
return new Promise(function (resolve, reject) {
$.ajax({
url: resourceUrl,
data: JSON.stringify(school),
method: "POST",
dataType: "json",
contentType: "application/json",
success: resolve,
error: reject
});
});
},
getSchools: function () {
var Promise = promise.Promise;
return new Promise(function (resolve, reject) {
$.ajax({
url: resourceUrl,
method: "GET",
dataType: "json",
success: resolve,
error: reject
});
});
},
deleteSchool: function (school) {
var Promise = promise.Promise;
return new Promise(function (resolve, reject) {
$.ajax({
url: resourceUrl + "/" + school._id,
method: "DELETE",
dataType: "json",
success: resolve,
error: reject
});
});
}
}
上面的代码包含一些非常熟悉的 JQuery AJAX 调用,我只想强调的是我们使用 Promises 的方式。我们将 JQuery 的success和error处理程序附加到 Promise 对象上的resolve和reject回调,并返回 Promise 对象本身。您可以在此处找到更多关于 JavaScript Promises 的信息,我将不在这里讨论 Promises,它值得一篇独立的文章。
现在,让我们修改“schoolsStore.js”和“main.jsx”文件,如下所示,以便我们的应用程序开始使用schoolService来处理数据,而不是显示模拟数据:
schoolsStore.js
var dispatcher = require("../dispatcher");
var schoolService = require("../services/schoolService");
function SchoolStore() {
var listeners = [];
function onChange(listener) {
getSchools(listener);
listeners.push(listener);
}
function getSchools(cb){
schoolService.getSchools().then(function (res) {
cb(res);
});
}
function addSchool(school) {
schoolService.addSchool(school).then(function (res) {
console.log(res);
triggerListeners();
});
}
function deleteSchool(school) {
schoolService.deleteSchool(school).then(function (res) {
console.log(res);
triggerListeners();
});
}
function triggerListeners() {
getSchools(function (res) {
listeners.forEach(function (listener) {
listener(res);
});
});
}
dispatcher.register(function (payload) {
var split = payload.type.split(":");
if (split[0] === "school") {
switch (split[1]) {
case "addSchool":
addSchool(payload.school);
break;
case "deleteSchool":
deleteSchool(payload.school);
break;
}
}
});
return {
onChange: onChange
}
}
module.exports = SchoolStore();
main.jsx
var React = require("react");
var ReactDOM = require("react-dom");
var SchoolsList = require("./components/SchoolsList.jsx");
var schoolsStore = require("./stores/schoolsStore");
var _schools = [];
var getSchoolsCallback = function(schools){
_schools = schools;
render();
};
schoolsStore.onChange(getSchoolsCallback);
function render(){
ReactDOM.render(<SchoolsList schools={_schools} />, document.getElementById("container"));
}
现在运行 gulp 命令并浏览应用程序,请确保您的 MongoDB 实例已启动并正在运行。恭喜!您拥有了一个全栈 JavaScript 应用。
摘要
正如开头所承诺的,我们已经完成了使用 React.js、Express.js、Node.js 和 MongoDB 构建的全栈 JavaScript 应用的最终版本。您可以扩展此应用,例如实现学校的图片上传功能,这将为您提供更多探索的领域。我已将示例代码的最终版本附在此文章中,如果您在跟进过程中遇到任何问题,我鼓励您下载并参考。请按照以下步骤运行示例:
1. 下载并解压
2. 在命令提示符中导航到解压后应用程序的根目录
3. 运行 npm install
4. 运行 bower install
5. 运行 gulp
6. 运行 nodemon .\server\server.js