如何使用Node.js和ReactJS构建一个基本的CRUD应用程序
在本文中,我们将从头开始创建一个基本的学生应用程序。
应用程序功能:
- 创建一个新的学生
- 更新现有的学生
- 显示学生列表
- 删除一个学生
该项目中的REST API:
REST API | URL |
---|---|
GET | http://localhost:4000/students |
GET | /students/update-student/id |
POST | /students/create-student |
PUT | /students/update-student/id |
DELETE | /students/delete-student/id |
首先,我们将使用React.js处理应用程序的前端部分。
创建React应用程序并安装模块
步骤1: 让我们开始使用React构建前端部分。要创建一个新的React应用程序,请输入以下代码到终端并按Enter键。
npx create-react-app mern-stack-crud
步骤2: 进入React项目文件夹。
cd mern-stack-crud
步骤3: 要运行React应用程序,请运行以下命令:
npm start
此命令会在以下URL中在浏览器中打开React应用程序: http://localhost:3000/
步骤4: 要构建React应用程序,我们需要安装一些外部模块。
NPM | 详情 |
---|---|
React-Bootstrap | React-Bootstrap与React一同发展和成长,成为您UI的绝佳选择。 |
React-Router-Dom | React-Router-Dom使您能够在React App中实现路由功能。 |
Axios | 这是一个基于promise的HTTP客户端,用于网络请求。 |
Formik | React中构建表单的优秀库。 |
Yup | Yup是用于表单验证的JavaScript架构生成器。 |
要安装,请在终端上运行以下代码。
npm i react-bootstrap@next bootstrap@5.1.0 react-router-dom axios formik yup
步骤5:创建简单的React组件 – 在这一步中,我们将创建一些React组件来管理学生数据。
前往 src 文件夹,创建一个名为 Components 的文件夹,并在该目录下创建以下组件。
- StudentForm.js - 可重用的学生表单
- create-student.component.js - 负责创建新学生
- edit-student.component.js - 负责更新学生数据
- student-list.component.js - 负责显示所有学生
- StudentTableRow.js - 负责显示单个学生
项目结构: 它将如下所示
步骤6: 创建学生表单 – 在这一步中,我们将使用Formik和React-Bootstrap构建一个可复用的学生表单。这个表单包含了输入学生详细信息所需的所有字段。我们还使用Yup进行了客户端表单验证。将来,我们将使用这个组件来创建和更新学生信息。前往 src/Components/StudentForm.js 并写入以下代码。
StudentForm.js
import React from "react";
import * as Yup from "yup";
import { Formik, Form, Field, ErrorMessage } from "formik";
import { FormGroup, FormControl, Button } from "react-bootstrap";
const StudentForm = (props) => {
const validationSchema = Yup.object().shape({
name: Yup.string().required("Required"),
email: Yup.string()
.email("You have enter an invalid email address")
.required("Required"),
rollno: Yup.number()
.positive("Invalid roll number")
.integer("Invalid roll number")
.required("Required"),
});
console.log(props);
return (
<div className="form-wrapper">
<Formik {...props} validationSchema={validationSchema}>
<Form>
<FormGroup>
<Field name="name" type="text"
className="form-control" />
<ErrorMessage
name="name"
className="d-block invalid-feedback"
component="span"
/>
</FormGroup>
<FormGroup>
<Field name="email" type="text"
className="form-control" />
<ErrorMessage
name="email"
className="d-block invalid-feedback"
component="span"
/>
</FormGroup>
<FormGroup>
<Field name="rollno" type="number"
className="form-control" />
<ErrorMessage
name="rollno"
className="d-block invalid-feedback"
component="span"
/>
</FormGroup>
<Button variant="danger" size="lg"
block="block" type="submit">
{props.children}
</Button>
</Form>
</Formik>
</div>
);
};
export default StudentForm;
步骤7:创建新学生: 在这一步中,我们将创建一个组件来添加一个新学生。我们已经创建了一个 StudentForm 组件来输入学生的详细信息。现在是使用这个组件的时候了。进入 src/Components/create-student.component.js 并编写以下代码。
create-student.component.js
// CreateStudent Component for add new student
// Import Modules
import React, { useState, useEffect } from "react";
import axios from 'axios';
import StudentForm from "./StudentForm";
// CreateStudent Component
const CreateStudent = () => {
const [formValues, setFormValues] =
useState({ name: '', email: '', rollno: '' })
// onSubmit handler
const onSubmit = studentObject => {
axios.post(
'http://localhost:4000/students/create-student',
studentObject)
.then(res => {
if (res.status === 200)
alert('Student successfully created')
else
Promise.reject()
})
.catch(err => alert('Something went wrong'))
}
// Return student form
return(
<StudentForm initialValues={formValues}
onSubmit={onSubmit}
enableReinitialize>
Create Student
</StudentForm>
)
}
// Export CreateStudent Component
export default CreateStudent
步骤8:更新学生详情: 在这一部分中,我们将创建一个组件来更新学生的详情。我们有一个可重用的 StudentForm 组件,让我们再次使用它。我们将获取学生详情来重新初始化表单。打开 src/Components/edit-student.component.js 并编写以下代码。
edit-student.component.js
// EditStudent Component for update student data
// Import Modules
import React, { useState, useEffect } from "react";
import axios from "axios";
import StudentForm from "./StudentForm";
// EditStudent Component
const EditStudent = (props) => {
const [formValues, setFormValues] = useState({
name: "",
email: "",
rollno: "",
});
//onSubmit handler
const onSubmit = (studentObject) => {
axios
.put(
"http://localhost:4000/students/update-student/" +
props.match.params.id,
studentObject
)
.then((res) => {
if (res.status === 200) {
alert("Student successfully updated");
props.history.push("/student-list");
} else Promise.reject();
})
.catch((err) => alert("Something went wrong"));
};
// Load data from server and reinitialize student form
useEffect(() => {
axios
.get(
"http://localhost:4000/students/update-student/"
+ props.match.params.id
)
.then((res) => {
const { name, email, rollno } = res.data;
setFormValues({ name, email, rollno });
})
.catch((err) => console.log(err));
}, []);
// Return student form
return (
<StudentForm
initialValues={formValues}
onSubmit={onSubmit}
enableReinitialize
>
Update Student
</StudentForm>
);
};
// Export EditStudent Component
export default EditStudent;
步骤9:显示学生列表: 在这一步中,我们将构建一个组件来在表格中显示学生的详细信息。我们将获取学生的数据,并遍历每个学生创建一个表格行。请转到 src/Components/student-list.component.js 并编写以下代码。
student-list.component.js
import React, { useState, useEffect } from "react";
import axios from "axios";
import { Table } from "react-bootstrap";
import StudentTableRow from "./StudentTableRow";
const StudentList = () => {
const [students, setStudents] = useState([]);
useEffect(() => {
axios
.get("http://localhost:4000/students/")
.then(({ data }) => {
setStudents(data);
})
.catch((error) => {
console.log(error);
});
}, []);
const DataTable = () => {
return students.map((res, i) => {
return <StudentTableRow obj={res} key={i} />;
});
};
return (
<div className="table-wrapper">
<Table striped bordered hover>
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Roll No</th>
<th>Action</th>
</tr>
</thead>
<tbody>{DataTable()}</tbody>
</Table>
</div>
);
};
export default StudentList;
步骤10:显示单个学生: 在这一步中,我们将返回表格行,该行负责显示学生数据。进入 src/Components/StudentTableRow.js 并编写以下代码。
StudentTableRow.js
import React from "react";
import { Button } from "react-bootstrap";
import { Link } from "react-router-dom";
import axios from "axios";
const StudentTableRow = (props) => {
const { _id, name, email, rollno } = props.obj;
const deleteStudent = () => {
axios
.delete(
"http://localhost:4000/students/delete-student/" + _id)
.then((res) => {
if (res.status === 200) {
alert("Student successfully deleted");
window.location.reload();
} else Promise.reject();
})
.catch((err) => alert("Something went wrong"));
};
return (
<tr>
<td>{name}</td>
<td>{email}</td>
<td>{rollno}</td>
<td>
<Link className="edit-link"
to={"/edit-student/" + _id}>
Edit
</Link>
<Button onClick={deleteStudent}
size="sm" variant="danger">
Delete
</Button>
</td>
</tr>
);
};
export default StudentTableRow;
步骤11:编辑App.js: 最后,包含菜单使我们的MERN Stack CRUD应用程序可以进行路由。去 src/App.js 并写入以下代码。
App.js
// Import React
import React from "react";
// Import Bootstrap
import { Nav, Navbar, Container, Row, Col }
from "react-bootstrap";
import "bootstrap/dist/css/bootstrap.css";
// Import Custom CSS
import "./App.css";
// Import from react-router-dom
import { BrowserRouter as Router, Switch,
Route, Link } from "react-router-dom";
// Import other React Component
import CreateStudent from
"./Components/create-student.component";
import EditStudent from
"./Components/edit-student.component";
import StudentList from
"./Components/student-list.component";
// App Component
const App = () => {
return (
<Router>
<div className="App">
<header className="App-header">
<Navbar bg="dark" variant="dark">
<Container>
<Navbar.Brand>
<Link to={"/create-student"}
className="nav-link">
React MERN Stack App
</Link>
</Navbar.Brand>
<Nav className="justify-content-end">
<Nav>
<Link to={"/create-student"}
className="nav-link">
Create Student
</Link>
</Nav>
<Nav>
<Link to={"/student-list"}
className="nav-link">
Student List
</Link>
</Nav>
</Nav>
</Container>
</Navbar>
</header>
<Container>
<Row>
<Col md={12}>
<div className="wrapper">
<Switch>
<Route exact path="/"
component={CreateStudent} />
<Route path="/create-student"
component={CreateStudent} />
<Route path="/edit-student/:id"
component={EditStudent} />
<Route path="/student-list"
component={StudentList} />
</Switch>
</div>
</Col>
</Row>
</Container>
</div>
</Router>
);
};
export default App;
步骤12:添加样式 – 转到 src/App.css 并编写以下代码。
App.css
.wrapper {
padding-top: 30px;
}
body h3 {
margin-bottom: 25px;
}
.navbar-brand a {
color: #ffffff;
}
.form-wrapper,
.table-wrapper {
max-width: 500px;
margin: 0 auto;
}
.table-wrapper {
max-width: 700px;
}
.edit-link {
padding: 7px 10px;
font-size: 0.875rem;
line-height: normal;
border-radius: 0.2rem;
color: #fff;
background-color: #28a745;
border-color: #28a745;
margin-right: 10px;
position: relative;
top: 1px;
}
.edit-link:hover {
text-decoration: none;
color: #ffffff;
}
/* Chrome, Safari, Edge, Opera */
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
/* Firefox */
input[type=number] {
-moz-appearance: textfield;
}
现在,我们已经成功为我们的 mern-stack-app 创建了前端部分。 **现在,让我们来构建后端部分。在进入下一部分之前,先看一下前端部分在没有后端的情况下如何工作。
运行应用程序的步骤: 打开终端并输入以下命令。
npm start
输出:
现在我们将开始处理应用程序的后端部分。我们将在我们的 mern-stack-crud 文件夹中创建一个文件夹,用于管理服务器服务,如数据库、模型、架构、路由和API,将这个文件夹命名为 backend 。
步骤1: 运行命令以创建 backend 文件夹作为服务器,并进入其中。
mkdir backend && cd backend
步骤2:创建package.json – 接下来,我们需要创建一个单独的 package.json 文件来管理我们的 mern-stack-crud 应用程序的服务器。
npm init -y
访问 backend/package.json 文件将如下所示。替换 test 属性如下:
~~"test": "echo \"Error: no test specified\" && exit 1" ~~
"start": "nodemon server.js"
步骤3:安装Node依赖库 – 安装以下的Node依赖库。
NPM | 详情 |
---|---|
Express | Node.js框架,帮助创建强大的REST API。 |
body-parser | 从请求流中提取整个body部分,并在req.body上公开它。 |
cors | 这是一个Node.js包,帮助启用Access-Control-Allow-Origin CORS标头。 |
mongoose | 它是一个用于创建强大Web应用程序的NoSQL数据库。 |
安装上述依赖项,请在终端上运行以下代码。
npm install express body-parser cors mongoose
您可以将nodemon安装为开发依赖项,以自动化服务器重新启动过程。
npm i -D nodemon
后端项目结构
步骤4:设置MongoDB数据库 – 在这一步中,我们将为我们的应用程序设置一个MongoDB数据库。在开始之前,请确保您的系统上安装了最新版本的MongoDB。在 backend 文件夹内创建一个名为 database 的文件夹。在 database 文件夹内创建一个名为 db.js 的文件。转到 backend/database/db.js 并编写以下代码。
db.js
module.exports = {
db: 'mongodb://localhost:27017/reactdb'
};
我们声明了MongoDB数据库并将其命名为 reactdb 。
步骤5:定义Mongoose模式 – 现在,为与MongoDB数据库交互创建MongoDB模式。在 backend 文件夹内创建一个名为 models 的文件夹以保存与模式相关的文件,并在其中创建一个名为 Student.js 的文件来定义MongoDB模式。转到 backend/models/Student.js 并编写以下代码。
Student.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
let studentSchema = new Schema({
name: {
type: String
},
email: {
type: String
},
rollno: {
type: Number
}
}, {
collection: 'students'
})
module.exports = mongoose.model('Student', studentSchema)
我们在student Schema中声明了name,email和rollno字段及其相应的数据类型。
步骤6:使用ExpressJS创建路由 – 在这一步中,我们使用Express和Node.js设置了一些路由(REST APIs)用于创建、读取、更新和删除。这些路由将帮助我们管理在 mern-stack-crud 应用中的数据。在backend文件夹中创建一个名为routes的文件夹,并在其中创建一个名为student.routes.js的文件。在这个文件中定义我们的路由。
mkdir routes && cd routes && touch student.route.js
然后,进入 backend/routes/student.route.js 文件并编写以下代码。
student.route.js
let mongoose = require("mongoose"),
express = require("express"),
router = express.Router();
// Student Model
let studentSchema = require("../models/Student");
// CREATE Student
router.post("/create-student", (req, res, next) => {
studentSchema.create(req.body, (error, data) => {
if (error) {
return next(error);
} else {
console.log(data);
res.json(data);
}
});
});
// READ Students
router.get("/", (req, res) => {
studentSchema.find((error, data) => {
if (error) {
return next(error);
} else {
res.json(data);
}
});
});
// UPDATE student
router
.route("/update-student/:id")
// Get Single Student
.get((req, res) => {
studentSchema.findById(
req.params.id, (error, data) => {
if (error) {
return next(error);
} else {
res.json(data);
}
});
})
// Update Student Data
.put((req, res, next) => {
studentSchema.findByIdAndUpdate(
req.params.id,
{
$set: req.body,
},
(error, data) => {
if (error) {
return next(error);
console.log(error);
} else {
res.json(data);
console.log("Student updated successfully !");
}
}
);
});
// Delete Student
router.delete("/delete-student/:id",
(req, res, next) => {
studentSchema.findByIdAndRemove(
req.params.id, (error, data) => {
if (error) {
return next(error);
} else {
res.status(200).json({
msg: data,
});
}
});
});
module.exports = router;
步骤7:配置server.js – 我们几乎已经创建了所有的内容来实现我们的mern-stack-crud应用。现在,在backend文件夹的根目录下创建server.js文件。进入backend/server.js并写入以下代码。
server.js
let express = require('express');
let mongoose = require('mongoose');
let cors = require('cors');
let bodyParser = require('body-parser');
let dbConfig = require('./database/db');
// Express Route
const studentRoute = require('../backend/routes/student.route')
// Configure mongoDB Database
mongoose.set('useNewUrlParser', true);
mongoose.set('useFindAndModify', false);
mongoose.set('useCreateIndex', true);
mongoose.set('useUnifiedTopology', true);
// Connecting MongoDB Database
mongoose.Promise = global.Promise;
mongoose.connect(dbConfig.db).then(() => {
console.log('Database successfully connected!')
},
error => {
console.log('Could not connect to database : ' + error)
}
)
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(cors());
app.use('/students', studentRoute)
// PORT
const port = process.env.PORT || 4000;
const server = app.listen(port, () => {
console.log('Connected to port ' + port)
})
// 404 Error
app.use((req, res, next) => {
res.status(404).send('Error 404!')
});
app.use(function (err, req, res, next) {
console.error(err.message);
if (!err.statusCode) err.statusCode = 500;
res.status(err.statusCode).send(err.message);
});
现在,我们已经成功创建了我们的后端 mern-stack-app 。
我们的最终项目目录结构:
现在,启动MongoDB数据库服务器运行服务器。
运行应用程序的步骤: 在一个终端中打开并运行以下命令,通过在 backend 文件夹中保持Nodemon服务器的运行。
npm start
如果一切正常运行,你将在终端屏幕上看到以下输出。
最终输出: