如何开始使用MERN堆栈
介绍
MERN 堆栈由 MongoDB、Express、React/Redux 和 Node.js 组成。 MERN 堆栈是用于构建现代单页 Web 应用程序的最流行的 JavaScript 堆栈之一。
在本教程中,您将构建一个使用 RESTful API 的 todo 应用程序,您还将在本教程的后面构建它。
先决条件
要完成本教程,您需要:
- Node.js 安装在本地,您可以按照【X57X】如何安装Node.js 并创建本地开发环境【X126X】进行。
注意: 本教程最初是为了使用 mLab 服务来托管 MongoDB 数据库而编写的。 自 2019 年 2 月以来,mLab 已禁止创建新帐户,他们建议使用 MongoDB Atlas。
也可以按照 安装说明 并在本地运行 MongoDB,但本教程将不涵盖该过程,并保持原样用于教育目的。
您还需要一个您熟悉的代码编辑器,最好是支持 JavaScript 代码突出显示的代码编辑器。
建议下载和安装类似 Postman 的工具来测试 API 端点。
本教程已使用 Node v14.2.0、npm
v6.14.5、mongodb-community
v4.2.6、express
v4.17.1 和 mongoose
v5.9.17 进行了验证.
第 1 步 — 设置应用程序
让我们从设置开始。 打开您的终端并在本地计算机上任何方便的位置创建一个新文件目录。 您可以将其命名为任何名称,但在此示例中,它称为 mern-todo
。
mkdir mern-todo
现在,进入该文件目录:
cd mern-todo
下一步是使用 package.json
文件初始化项目。 此文件将包含有关您的应用程序及其运行所需的依赖项的一些信息。
您可以使用:
npm init
并在出现提示时按照说明进行操作。 或者您可以使用:
npm init -y
使用默认值。
第 2 步 — 设置节点服务器
要在后端运行 JavaScript 代码,您需要启动一个服务器来编译您的代码。
创建服务器有两种方式:一是使用Node内置的http
模块; 二是利用Express.js框架。
本教程将使用 Express.js。 它是一个 Node.js HTTP 框架,可以开箱即用地处理很多事情,并且只需要很少的代码即可创建功能齐全的 RESTful API。 要使用 Express,请使用 npm 安装它:
npm install express
现在,创建一个文件 index.js
并在其中输入以下代码并保存:
index.js
const express = require('express'); const app = express(); const port = process.env.PORT || 5000; app.use((req, res, next) => { res.header('Access-Control-Allow-Origin', '*'); res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept'); next(); }); app.use((req, res, next) => { res.send('Welcome to Express'); }); app.listen(port, () => { console.log(`Server running on port ${port}`); });
前面代码中的这段代码有助于处理您在开发和测试期间尝试从不同域访问 API 时可能遇到的与 CORS 相关的问题:
app.use((req, res, next) => { res.header('Access-Control-Allow-Origin', '*'); res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept'); next(); });
是时候启动你的服务器来看看它是否工作了。 在与 index.js
文件相同的目录中打开终端并键入:
node index.js
如果一切顺利,您将在终端中看到 Server 在端口 5000 上运行。
第 3 步 - 创建路由
应用程序需要做三件事:
- 创建任务
- 查看所有任务
- 删除已完成的任务
对于每个任务,您将需要创建将定义待办事项应用程序所依赖的多个端点的路由。 因此,让我们创建一个文件夹 routes
并创建一个文件 api.js
,其中包含以下代码。
mkdir routes
编辑 api.js
并在其中粘贴以下代码:
路线/api.js
const express = require('express'); const router = express.Router(); router.get('/todos', (req, res, next) => { // get placeholder }); router.post('/todos', (req, res, next) => { // post placeholder }); router.delete('/todos/:id', (req, res, next) => { // delete placeholder }); module.exports = router;
这为 GET、POST 和 DELETE 提供了占位符路由。
第四步——定义模型
现在,有趣的部分来了。 由于应用程序将使用 MongoDB 这是一个 NoSQL 数据库,我们需要创建一个 model 和一个 schema。 模型是使用模式接口定义的。 该架构允许您定义存储在每个文档中的字段及其验证要求和默认值。 本质上,模式是如何构建数据库的蓝图。 此外,您可以定义静态和实例辅助方法以更轻松地使用您的数据类型,还可以定义可以像任何其他字段一样使用但不存储在数据库中的虚拟属性。
要创建模式和模型,请安装 Mongoose,它是一个 Node 包,可以更轻松地使用 MongoDB。
# ensure that you are in the `mern-todo` project directory npm install mongoose
在根目录中创建一个新文件夹并将其命名为 models
。 在其中创建一个文件并将其命名为 todo.js
,其中包含以下代码:
mkdir models
使用文本编辑器将以下内容粘贴到 todo.js
中:
模型/todo.js
const mongoose = require('mongoose'); const Schema = mongoose.Schema; // Create schema for todo const TodoSchema = new Schema({ action: { type: String, required: [true, 'The todo text field is required'], }, }); // Create model for todo const Todo = mongoose.model('todo', TodoSchema); module.exports = Todo;
现在,我们需要更新我们的路线以使用新模型。
路线/api.js
const express = require('express'); const router = express.Router(); const Todo = require('../models/todo'); router.get('/todos', (req, res, next) => { // This will return all the data, exposing only the id and action field to the client Todo.find({}, 'action') .then((data) => res.json(data)) .catch(next); }); router.post('/todos', (req, res, next) => { if (req.body.action) { Todo.create(req.body) .then((data) => res.json(data)) .catch(next); } else { res.json({ error: 'The input field is empty', }); } }); router.delete('/todos/:id', (req, res, next) => { Todo.findOneAndDelete({ _id: req.params.id }) .then((data) => res.json(data)) .catch(next); }); module.exports = router;
第 5 步 — 连接到数据库
您将需要一个用于存储数据的数据库。 为此,您将使用 mLab。 按照文档开始使用mLab。
设置数据库后,您需要使用以下代码更新 index.js
文件:
index.js
const express = require('express'); const bodyParser = require('body-parser'); const mongoose = require('mongoose'); const routes = require('./routes/api'); require('dotenv').config(); const app = express(); const port = process.env.PORT || 5000; // Connect to the database mongoose .connect(process.env.DB, { useNewUrlParser: true }) .then(() => console.log(`Database connected successfully`)) .catch((err) => console.log(err)); // Since mongoose's Promise is deprecated, we override it with Node's Promise mongoose.Promise = global.Promise; app.use((req, res, next) => { res.header('Access-Control-Allow-Origin', '*'); res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept'); next(); }); app.use(bodyParser.json()); app.use('/api', routes); app.use((err, req, res, next) => { console.log(err); next(); }); app.listen(port, () => { console.log(`Server running on port ${port}`); });
注意: 在 Express 4.16+ 之前的版本中,必须依赖像 body-parser
这样的中间件。 但是,现在可以使用内置解析器。
如果您使用的是旧版本的 Express,请使用 npm 安装 body-parser
:
npm install body-parser
在前面的代码中使用 process.env
来访问需要创建的环境变量。 在根目录中创建一个名为 .env
的文件并编辑:
.env
DB = 'mongodb://<USER>:<PASSWORD>@example.mlab.com:port/todo'
创建数据库和用户后,请确保使用您自己的来自 mLab 的 MongoDB URL。 将 <USER>
替换为用户名,将 <PASSWORD>
替换为您创建的用户的密码。
要使用环境变量,您必须安装一个名为 dotenv
的 Node 包,以确保您可以访问存储在 .env
文件中的环境变量。
# ensure that you are in the `mern-todo` project directory npm install dotenv
然后在 index.js
中 require 和配置它:
require('dotenv').config()
使用环境变量而不是直接将凭据写入应用程序代码可以隐藏版本控制系统的敏感信息。 以这种方式将配置和秘密数据与应用程序代码分开被认为是最佳实践。
第 6 步 — 测试 API
这是我们开始尝试确保您的 RESTful API 正常工作的部分。 由于您的前端还没有准备好,您可以使用一些 API 开发客户端来测试您的代码。
您可以使用 Postman 或 Insomnia 或您的首选客户端来测试 API。
使用以下命令启动服务器:
node index.js
现在,打开您的客户端,创建一个 GET 方法并导航到 http://localhost:5000/api/todos
。
测试所有 API 端点并确保它们正常工作。 对于需要 body
的端点,发送回带有必要字段的 JSON,因为它是您在代码中设置的。
示例 POST 请求:
POST localhost:5000/api/todos Body raw
示例 POST 值:
{ "action": "build a mern stack application" }
示例 GET 请求:
GET localhost:5000/api/todos
示例 GET 响应:
Output[ { "id": "5bd4edfc89d4c3228e1bbe0a", "action": "build a mern stack application" } ]
示例 DELETE 请求:
DELETE localhost:5000/api/todos/5bd4edfc89d4c3228e1bbe0ad
测试并观察 GET、POST 和 DELETE 的结果。
第 7 步 - 创建前端
由于您已经完成了 API 所需的功能,是时候为客户端创建一个接口以与 API 交互了。 要从 todo 应用程序的前端开始,您将使用 create-react-app 命令来搭建您的应用程序。
在与后端代码相同的根目录中,即 mern-todo
目录中,运行:
npx create-react-app client
这将在您的 mern-todo
目录中创建一个名为 client
的新文件夹,您将在其中添加所有 React 代码。
第 8 步 — 运行 React 应用程序
在测试 React 应用程序之前,需要在项目根目录中安装许多依赖项。
首先,同时安装 [1] 作为开发依赖项:
npm install concurrently --save-dev
Concurrently 用于从同一个终端窗口同时运行多个命令。
然后,安装 nodemon 作为开发依赖项:
npm install nodemon --save-dev
Nodemon 用于运行服务器并监控它。 如果服务器代码有任何更改,Nodemon 将使用新更改自动重新启动它。
接下来,打开应用项目根文件夹中的 package.json
文件,并粘贴以下代码:
包.json
{ // ... "scripts": { "start": "node index.js", "start-watch": "nodemon index.js", "dev": "concurrently \"npm run start-watch\" \"cd client && npm start\"" }, // ... }
进入客户端文件夹,然后找到 package.json
文件并在其中添加以下键值对。
客户端/package.json
{ // ... "proxy": "http://localhost:5000" }
我们的 package.json
文件中的此代理设置将使您无需输入完整的 URL 即可进行 API 调用,只需 /api/todos
即可获取所有待办事项
打开终端并运行 npm run dev
并确保您在 todo
目录中,而不是在 client
目录中。
您的应用程序将在 localhost:3000
上打开并运行。
第 9 步——创建 React 组件
React 的优点之一是它使用了可重用的组件,并且还使代码模块化。 对于您的待办事项应用程序,将有两个状态组件和一个无状态组件。
在 src
文件夹中创建另一个名为 components
的文件夹,并在其中创建三个文件 Input.js
、ListTodo.js
和 Todo.js
。
打开 Input.js
文件并粘贴以下内容:
客户端/src/components/Input.js
import React, { Component } from 'react'; import axios from 'axios'; class Input extends Component { state = { action: '', }; addTodo = () => { const task = { action: this.state.action }; if (task.action && task.action.length > 0) { axios .post('/api/todos', task) .then((res) => { if (res.data) { this.props.getTodos(); this.setState({ action: '' }); } }) .catch((err) => console.log(err)); } else { console.log('input field required'); } }; handleChange = (e) => { this.setState({ action: e.target.value, }); }; render() { let { action } = this.state; return ( <div> <input type="text" onChange={this.handleChange} value={action} /> <button onClick={this.addTodo}>add todo</button> </div> ); } } export default Input;
要使用 axios,它是浏览器和 Node.js 的基于 Promise 的 HTTP 客户端,您需要从终端导航到 client
目录:
cd client
并运行 npm install axios
:
npm install axios
之后,打开您的 ListTodo.js
文件并粘贴以下代码:
客户端/src/components/ListTodo.js
import React from 'react'; const ListTodo = ({ todos, deleteTodo }) => { return ( <ul> {todos && todos.length > 0 ? ( todos.map((todo) => { return ( <li key={todo._id} onClick={() => deleteTodo(todo._id)}> {todo.action} </li> ); }) ) : ( <li>No todo(s) left</li> )} </ul> ); }; export default ListTodo;
然后,在您的 Todo.js
文件中编写以下代码:
客户端/src/components/Todo.js
import React, { Component } from 'react'; import axios from 'axios'; import Input from './Input'; import ListTodo from './ListTodo'; class Todo extends Component { state = { todos: [], }; componentDidMount() { this.getTodos(); } getTodos = () => { axios .get('/api/todos') .then((res) => { if (res.data) { this.setState({ todos: res.data, }); } }) .catch((err) => console.log(err)); }; deleteTodo = (id) => { axios .delete(`/api/todos/${id}`) .then((res) => { if (res.data) { this.getTodos(); } }) .catch((err) => console.log(err)); }; render() { let { todos } = this.state; return ( <div> <h1>My Todo(s)</h1> <Input getTodos={this.getTodos} /> <ListTodo todos={todos} deleteTodo={this.deleteTodo} /> </div> ); } } export default Todo;
你需要对你的 React 代码做一些调整。 删除徽标并将 App.js
调整为如下所示:
客户端/src/App.js
import React from 'react'; import Todo from './components/Todo'; import './App.css'; const App = () => { return ( <div className="App"> <Todo /> </div> ); }; export default App;
然后将以下代码粘贴到 App.css
中:
客户端/src/App.css
.App { text-align: center; font-size: calc(10px + 2vmin); width: 60%; margin-left: auto; margin-right: auto; } input { height: 40px; width: 50%; border: none; border-bottom: 2px #101113 solid; background: none; font-size: 1.5rem; color: #787a80; } input:focus { outline: none; } button { width: 25%; height: 45px; border: none; margin-left: 10px; font-size: 25px; background: #101113; border-radius: 5px; color: #787a80; cursor: pointer; } button:focus { outline: none; } ul { list-style: none; text-align: left; padding: 15px; background: #171a1f; border-radius: 5px; } li { padding: 15px; font-size: 1.5rem; margin-bottom: 15px; background: #282c34; border-radius: 5px; overflow-wrap: break-word; cursor: pointer; } @media only screen and (min-width: 300px) { .App { width: 80%; } input { width: 100% } button { width: 100%; margin-top: 15px; margin-left: 0; } } @media only screen and (min-width: 640px) { .App { width: 60%; } input { width: 50%; } button { width: 30%; margin-left: 10px; margin-top: 0; } }
同样在 index.css
添加以下规则:
客户端/src/index.css
body { margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; box-sizing: border-box; background-color: #282c34; color: #787a80; } code { font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; }
假设保存所有这些文件时没有错误,待办事项应用程序将准备就绪并具有前面讨论的功能:创建任务、删除任务和查看所有任务。
结论
在本教程中,您使用 MERN 堆栈创建了一个待办事项应用程序。 您使用 React 编写了一个前端应用程序,该应用程序与使用 Express.js 编写的后端应用程序进行通信。 您还创建了一个 MongoDB 后端,用于在数据库中存储任务。