React+Ts+Node+MongoDB 构建 Todo App项目总结,offer拿到手软

本文指导新手逐步创建一个Node.jsAPI,使用TypeScript、Express、MongoDB,介绍了项目结构、TypeScript配置、依赖安装、数据库交互、API控制器和路由设置,以及如何与React前端进行数据交换。
摘要由CSDN通过智能技术生成

启动

如果你是新手,可以看看《TypeScript 实用指南》,或者从《如何用 Node JS、Express 和 MongoDB 从头创建 API》。如果你有一定经验了,可以直接开始。

在终端上运行这个命令,创建一个新的 NodeJS 应用程序:

yarn init

它会询问几个问题,然后初始化应用程序。你可以通过向命令中添加  -y  标志来跳过。

然后,按照以下目录构建项目:

├── dist

├── node_modules

├── src

├── app.ts

├── controllers

|  └── todos

|     └── index.ts

├── models

|  └── todo.ts

├── routes

|  └── index.ts

└── types

└── todo.ts

├── nodemon.json

├── package.json

├── tsconfig.json

如你所见,这个文件结构相对简单。代码编译成纯 JavaScript 后,dist 目录将用作输出文件夹。

我们还有一个  app.ts,它是服务器的入口。控制器、类型和路由也在它们各自以它们命名的的文件夹中。

现在,我们需要配置  tsconfig.json,使编译器运行我们的首选项。

  • tsconfig.json

{

“compilerOptions”: {

“target”: “es6”,

“module”: “commonjs”,

“outDir”: “dist/js”,

“rootDir”: “src”,

“strict”: true,

“esModuleInterop”: true,

“forceConsistentCasingInFileNames”: true

},

“include”: [“src/**/*”],

“exclude”: [“src/types/*.ts”, “node_modules”, “.vscode”]

}

这里强调四个主要属性:

outDir: 告诉编译器把编译好的代码放进  dist/js  文件夹

rootDir: 告诉 TypeScript 编译 src 文件夹中的每个 .ts 文件

include: 告诉编译器包含 src 目录和子目录中的文件

exclude: 在编译时会排除数组中的文件或文件夹

现在我们安装依赖项,使项目可以使用 TypeScript。因为默认情况下,这个应用程序会使用 JavaScript。

在 NodeJS 应用程序中有两种使用 TypeScript 的方法,要么在项目中本地安装使用,要么在电脑中全局安装使用。基于个人喜好,我会选择后者。但如果你想,你也可以坚持使用本地安装使用的方式。

现在,让我们在终端上执行以下命令来安装 TypeScript。

yarn add typescript -g

这个  g  标志允许全局安装 TypeScript,这样它就能在计算机任何地方使用。

接下来,为了使用 Express 和 MongoDB,我们安装一些依赖项。

yarn add express cors mongoose

我们还需要安装它们的类型作为开发依赖项,帮助 TypeScript 编译器理解这些包。

yarn add -D @types/node @types/express @types/mongoose @types/cors

现在,TypeScript 不会再对你提示错误——它将使用这些类型来定义我们刚刚安装的库。

我们还需要安装其他依赖项,以便能够编译 TypeScript 代码并同时启动服务器。

yarn add -D concurrently nodemon

有了这些,我们现在就可以更新  package.json  的 scripts 来启动服务器。

  • package.json

“scripts”: {

“build”: “tsc”,

“start”: “concurrently “tsc -w” “nodemon dist/js/app.js””

}

concurrently  帮助编译 TypeScript 代码,持续观察变化,同时启动服务器。也就是说,我们现在可以启动服务器了——但是,我们还没有创建一些有意义的东西。所以,让我们在下一节中解决这个问题。

创建 Todo 类型

  • types/todo.ts

import { Document } from “mongoose”

export interface ITodo extends Document {

name: string

description: string

status: boolean

}

这里,我们有了继承  mongoose  提供的  Document  类型的 Todo 接口。稍后我们将使用它与 MongoDB 交互。也就是说,我们现在可以定义 Todo 模块。

创建 Todo 模块

  • models/todo.ts

import { ITodo } from “./…/types/todo”

import { model, Schema } from “mongoose”

const todoSchema: Schema = new Schema(

{

name: {

type: String,

required: true,

},

description: {

type: String,

required: true,

},

status: {

type: Boolean,

required: true,

},

},

{ timestamps: true }

)

export default model(“Todo”, todoSchema)

首先导入  ITodo  接口和 一些  mongoose  导出的模块,后者是帮助定义 Todo schema 和在导出前把 ITodo 作为类型参数传入  model  。

这样,我们现在就可以在其他文件中使用 Todo 模块来与数据库交互。

创建 API 控制器

获取、新增、更新和删除 Todos
  • controllers/todos/index.ts

import { Response, Request } from “express”

import { ITodo } from “./…/…/types/todo”

import Todo from “…/…/models/todo”

const getTodos = async (req: Request, res: Response): Promise => {

try {

const todos: ITodo[] = await Todo.find()

res.status(200).json({ todos })

} catch (error) {

throw error

}

}

这里,我们首先需要从  express  导入一些类型,因为我想显式指明类型。如果你想,你可以让 TypeScript 帮你推断。

接下来,我们使用 getTodos() 函数来获取数据,它接收  req  和  res  参数并返回 promise。

在前面创建的 Todo 模块的帮助下,我们现在可以从 MongoDB 获取数据并返回 Todo 数组。

  • controllers/todos/index.ts

const addTodo = async (req: Request, res: Response): Promise => {

try {

const body = req.body as Pick<ITodo, “name” | “description” | “status”>

const todo: ITodo = new Todo({

name: body.name,

description: body.description,

status: body.status,

})

const newTodo: ITodo = await todo.save()

const allTodos: ITodo[] = await Todo.find()

res

.status(201)

.json({ message: “Todo added”, todo: newTodo, todos: allTodos })

} catch (error) {

throw error

}

}

addTodo()  函数接收包含用户输入数据的 body 对象。

接下来,我使用类型转换来避免拼写错误,并限制  body  变量与  ITodo  类型匹配,然后基于该模块创建一个新的 Todo。

有了这些,我们现在可以在 DB 中保存 Todo 并返回新增的 Todo 和更新后的 todos 数组。

  • controllers/todos/index.ts

const updateTodo = async (req: Request, res: Response): Promise => {

try {

const {

params: { id },

body,

} = req

const updateTodo: ITodo | null = await Todo.findByIdAndUpdate(

{ _id: id },

body

)

const allTodos: ITodo[] = await Todo.find()

res.status(200).json({

message: “Todo updated”,

todo: updateTodo,

todos: allTodos,

})

} catch (error) {

throw error

}

}

为了实现更新 todo, 我们需要拿到 id 和从  req  对象中获取 body,然后把他们传入  findByIdAndUpdate(),这个函数将会在数据库中找到 Todo 并且更新它。

  • controllers/todos/index.ts

const deleteTodo = async (req: Request, res: Response): Promise => {

try {

const deletedTodo: ITodo | null = await Todo.findByIdAndRemove(

req.params.id

)

const allTodos: ITodo[] = await Todo.find()

res.status(200).json({

message: “Todo deleted”,

todo: deletedTodo,

todos: allTodos,

})

} catch (error) {

throw error

}

}

export { getTodos, addTodo, updateTodo, deleteTodo }

deleteTodo()  函数允许你从数据库中删除 Todo。在这里,我们从 req 中拿到 id,并把它作为参数传递给  findByIdAndRemove(),来获取到对应的 Todo 并从 DB 中删除它。

接下来,导出这些函数以便我们在其他文件中使用它们。也就是说,我们现在可以为 API 创建一些路由,并使用这些方法来处理请求。

创建 API 路由

  • routes/index.ts

import { Router } from “express”

import { getTodos, addTodo, updateTodo, deleteTodo } from “…/controllers/todos”

const router: Router = Router()

router.get(“/todos”, getTodos)

router.post(“/add-todo”, addTodo)

router.put(“/edit-todo/:id”, updateTodo)

router.delete(“/delete-todo/:id”, deleteTodo)

export default router

我们创建四个路由对应从数据库中获取、新增、更新和删除 todo。因为我们已经创建了函数,所以唯一要做的就是导入这些方法并将它们作为参数传递。

到目前为止,我们已经谈了很多,但是仍然没有启动服务器。所以,我们在下一节中解决这个问题。

创建服务器

在创建服务器之前,我们需要在  nodemon.json  加一些环境变量来保存 MongoDB 的凭据。

  • nodemon.json

{

“env”: {

“MONGO_USER”: “your-username”,

“MONGO_PASSWORD”: “your-password”,

“MONGO_DB”: “your-db-name”

}

}

你可以在  MongoDB Atlas,通过创一个新集群来得到凭据。

  • app.ts

import express, { Express } from “express”

import mongoose from “mongoose”

import cors from “cors”

import todoRoutes from “./routes”

const app: Express = express()

const PORT: string | number = process.env.PORT || 4000

app.use(cors())

app.use(todoRoutes)

const uri: string = mongodb+srv://${process.env.MONGO_USER}:${process.env.MONGO_PASSWORD}@clustertodo.raz9g.mongodb.net/${process.env.MONGO_DB}?retryWrites=true&w=majority

const options = { useNewUrlParser: true, useUnifiedTopology: true }

mongoose.set(“useFindAndModify”, false)

mongoose

.connect(uri, options)

.then(() =>

app.listen(PORT, () =>

console.log(Server running on http://localhost:${PORT})

)

)

.catch(error => {

throw error

})

这里,我们首先从导入  express  库开始,这使用我们能调用  use()  方法,这个方法将帮助处理 Todo 路由。

然后,我们用  mongoose  包,通过读取  nodemon.json  带凭证的 url 去连接 MongoDB。

就是说,现在如果我们能成功连接 MongoDB,服务器就会启动,否则,会抛出错误。

我们现在已经通过 Node、Express、TypeScript 和 MongoDB 完成 api 的构建。现在我们开始用 React 和 TypeScript 构建客户端。

用 React 和 TypeScript 创建客户端


构建

为了创建一个新的 React 应用,我将会使用 create-react-app ——你可以用其他你想用的方法。

所以,在终端运行以下代码:

npx create-react-app my-app --template typescript

然后,为了能获取远程数据安装 Axios 库。

yarn add axios

安装完成后,按照以下目录构建项目:

├── node_modules

├── public

├── src

|  ├── API.ts

|  ├── App.test.tsx

|  ├── App.tsx

|  ├── components

|  |  ├── AddTodo.tsx

|  |  └── TodoItem.tsx

|  ├── index.css

|  ├── index.tsx

|  ├── react-app-env.d.ts

|  ├── setupTests.ts

|  └── type.d.ts

├── tsconfig.json

├── package.json

└── yarn.lock

这样,我们有一个相对简单的文件结构。最值得注意的是  src/type.d.ts  被用来存放类型。我几乎在每个文件中都使用了它们,所以我添加了扩展  .d.ts  ,使类型全局可用。现在我们不再需要导入它们。

创建 Todo 类型

  • src/type.d.ts

interface ITodo {

_id: string

name: string

description: string

status: boolean

createdAt?: string

updatedAt?: string

}

interface TodoProps {

todo: ITodo

}

type ApiDataType = {

message: string

status: string

todos: ITodo[]

todo?: ITodo

}

这里,  ITodo  接口需要跟 API 返回的数据类型一样。这里没有  mongoose  , 所以需要加一些额外的属性来匹配 API 定义的数据类型。

然后,我们用相同的的接口定义  TodoProps  ,组件会接受它并渲染数据。

现在我们已经定义了类型——现在让我们开始从 API 获取数据。

从API获取数据

  • src/API.ts

import axios, { AxiosResponse } from “axios”

const baseUrl: string = “http://localhost:4000”

export const getTodos = async (): Promise<AxiosResponse> => {

try {

const todos: AxiosResponse = await axios.get(

baseUrl + “/todos”

)

return todos

} catch (error) {

throw new Error(error)

}

}

我们需要导入  axios,通过 api 来请求数据,然后,用  getTodos()  函数从服务端获取数据。它将返回  AxiosResponse  为类型的 promise, 保存获取到的  ApiDataType  类型的 Todos。

  • src/API.ts

export const addTodo = async (

formData: ITodo

): Promise<AxiosResponse> => {

try {

const todo: Omit<ITodo, “_id”> = {

name: formData.name,

description: formData.description,

status: false,

}

const saveTodo: AxiosResponse = await axios.post(

baseUrl + “/add-todo”,

todo

)

return saveTodo

} catch (error) {

throw new Error(error)

}

}

这个函数接受用户输入的数据作为参数并返回 promise。这里,我们需要去掉  _id  属性因为 MongoDB 会自动生成。

  • src/API.ts

export const updateTodo = async (

todo: ITodo

): Promise<AxiosResponse> => {

try {

const todoUpdate: Pick<ITodo, “status”> = {

status: true,

}

const updatedTodo: AxiosResponse = await axios.put(

${baseUrl}/edit-todo/${todo._id},

todoUpdate

)

return updatedTodo

} catch (error) {

throw new Error(error)

}

}

为了实现更新 Todo,我们必须传入更新后的数据和对象 id。这里,我们需要更改 Todo 的  状态  ,那么在发送到服务器之前我们只需要选择所需的属性即可。

  • src/API.ts

export const deleteTodo = async (

_id: string

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

结尾

学习html5、css、javascript这些基础知识,学习的渠道很多,就不多说了,例如,一些其他的优秀博客。但是本人觉得看书也很必要,可以节省很多时间,常见的javascript的书,例如:javascript的高级程序设计,是每位前端工程师必不可少的一本书,边看边用,了解js的一些基本知识,基本上很全面了,如果有时间可以读一些,js性能相关的书籍,以及设计者模式,在实践中都会用的到。

资料领取方式:戳这里免费获取

throw new Error(error)

}

}

为了实现更新 Todo,我们必须传入更新后的数据和对象 id。这里,我们需要更改 Todo 的  状态  ,那么在发送到服务器之前我们只需要选择所需的属性即可。

  • src/API.ts

export const deleteTodo = async (

_id: string

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-NuPNY8xg-1712200520918)]

[外链图片转存中…(img-0xnayIuQ-1712200520919)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

[外链图片转存中…(img-ig4NKCEj-1712200520919)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

结尾

学习html5、css、javascript这些基础知识,学习的渠道很多,就不多说了,例如,一些其他的优秀博客。但是本人觉得看书也很必要,可以节省很多时间,常见的javascript的书,例如:javascript的高级程序设计,是每位前端工程师必不可少的一本书,边看边用,了解js的一些基本知识,基本上很全面了,如果有时间可以读一些,js性能相关的书籍,以及设计者模式,在实践中都会用的到。

资料领取方式:戳这里免费获取

html5

  • 30
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值