前端:用react+typescript实现一个todo应用。并且将静态资源(html,js,css等)编译到node.js的静态资源目录中。端口:3000 。后端:使用node.js+javascript 将数据存储到服务端json文件中。端口3001。代码实现
时间: 2023-06-18 12:07:09 浏览: 99
前端代码实现:
首先,创建一个新的React项目:
```
npx create-react-app todo-app --template typescript
```
进入项目目录并安装必要的依赖:
```
cd todo-app
npm install axios --save
npm install @types/axios --save-dev
```
我们需要使用axios来处理与后端的数据交互。
接下来,创建一个Todo组件,并在其中实现Todo应用的逻辑:
```tsx
import React, { useState, useEffect } from 'react';
import axios from 'axios';
interface Todo {
id: number;
title: string;
completed: boolean;
}
const Todo: React.FC = () => {
const [todos, setTodos] = useState<Todo[]>([]);
const [newTodo, setNewTodo] = useState<string>('');
useEffect(() => {
getTodos();
}, []);
const getTodos = async () => {
const response = await axios.get('http://localhost:3001/todos');
setTodos(response.data);
};
const addTodo = async () => {
const response = await axios.post('http://localhost:3001/todos', {
title: newTodo,
completed: false
});
setTodos([...todos, response.data]);
setNewTodo('');
};
const deleteTodo = async (id: number) => {
await axios.delete(`http://localhost:3001/todos/${id}`);
const newTodos = todos.filter((todo) => todo.id !== id);
setTodos(newTodos);
};
const toggleTodo = async (id: number) => {
const todo = todos.find((todo) => todo.id === id);
await axios.patch(`http://localhost:3001/todos/${id}`, {
completed: !todo?.completed
});
const newTodos = todos.map((todo) =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
);
setTodos(newTodos);
};
return (
<div>
<h1>Todo App</h1>
<div>
<input
type="text"
placeholder="Add new todo"
value={newTodo}
onChange={(e) => setNewTodo(e.target.value)}
/>
<button onClick={addTodo}>Add</button>
</div>
<ul>
{todos.map((todo) => (
<li key={todo.id}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => toggleTodo(todo.id)}
/>
<span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}>
{todo.title}
</span>
<button onClick={() => deleteTodo(todo.id)}>Delete</button>
</li>
))}
</ul>
</div>
);
};
export default Todo;
```
在这个组件中,我们首先定义了一个Todo接口,用于表示Todo数据的结构。然后,我们使用useState来定义了todos和newTodo两个状态变量。
在组件加载完成的时候,我们会调用getTodos函数来获取所有的Todo。
addTodo函数用于添加新的Todo。我们会向后端发送一个POST请求,并将返回的Todo添加到todos数组中。
deleteTodo函数用于删除一个Todo。我们会向后端发送一个DELETE请求,并从todos数组中过滤掉被删除的Todo。
toggleTodo函数用于切换一个Todo的完成状态。我们会向后端发送一个PATCH请求,并更新todos数组中对应的Todo。
最后,在组件的渲染中,我们会展示所有的Todo,并提供添加、删除和切换完成状态的功能。
接下来,我们需要将静态资源编译到node.js的静态资源目录中。在package.json中添加以下代码:
```json
"homepage": "/",
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build && cp -r build/* ../backend/public",
"test": "react-scripts test",
"eject": "react-scripts eject"
}
```
这里,我们使用了cp命令将build目录中的文件复制到backend/public目录中。这样,我们就可以在后端中访问到这些静态资源了。
后端代码实现:
首先,我们需要创建一个新的node.js项目,并安装必要的依赖:
```
npm init -y
npm install express cors helmet morgan --save
npm install @types/express @types/cors @types/helmet @types/morgan --save-dev
```
这里,我们使用了express来搭建后端服务器,使用了cors来处理跨域请求,使用了helmet来增强安全性,使用了morgan来记录日志。
接下来,创建一个server.ts文件,并编写以下代码:
```ts
import express from 'express';
import cors from 'cors';
import helmet from 'helmet';
import morgan from 'morgan';
import fs from 'fs';
import path from 'path';
const app = express();
const port = 3001;
const todosFilePath = path.join(__dirname, 'todos.json');
app.use(express.json());
app.use(cors());
app.use(helmet());
app.use(morgan('tiny'));
app.get('/todos', (req, res) => {
const todos = JSON.parse(fs.readFileSync(todosFilePath).toString());
res.send(todos);
});
app.post('/todos', (req, res) => {
const todos = JSON.parse(fs.readFileSync(todosFilePath).toString());
const newTodo = {
id: todos.length > 0 ? todos[todos.length - 1].id + 1 : 1,
...req.body
};
todos.push(newTodo);
fs.writeFileSync(todosFilePath, JSON.stringify(todos));
res.send(newTodo);
});
app.delete('/todos/:id', (req, res) => {
const todos = JSON.parse(fs.readFileSync(todosFilePath).toString());
const newTodos = todos.filter((todo) => todo.id !== parseInt(req.params.id));
fs.writeFileSync(todosFilePath, JSON.stringify(newTodos));
res.send('Todo deleted');
});
app.patch('/todos/:id', (req, res) => {
const todos = JSON.parse(fs.readFileSync(todosFilePath).toString());
const todoIndex = todos.findIndex((todo) => todo.id === parseInt(req.params.id));
todos[todoIndex] = { ...todos[todoIndex], ...req.body };
fs.writeFileSync(todosFilePath, JSON.stringify(todos));
res.send('Todo updated');
});
app.listen(port, () => {
console.log(`Server listening at http://localhost:${port}`);
});
```
在这个文件中,我们首先引入了必要的依赖,并创建了一个express应用和一个端口号常量port。
todosFilePath是我们存储Todo数据的文件路径。我们使用fs模块来读写这个文件中的数据。
接下来,我们使用了express的中间件来增强应用的功能。express.json()用于处理请求体中的JSON数据,cors用于处理跨域请求,helmet用于增强安全性,morgan用于记录日志。
我们定义了四个路由:
- GET /todos:用于获取所有的Todo。
- POST /todos:用于添加一个新的Todo。
- DELETE /todos/:id:用于删除一个Todo。
- PATCH /todos/:id:用于更新一个Todo的完成状态。
在每个路由的处理函数中,我们都会读取todos.json文件中的数据,并对其进行相应的操作。然后,我们会将修改后的数据写回到文件中,并返回相应的响应。
最后,我们使用app.listen()方法来启动应用,并监听port端口。
在完成了以上代码之后,我们可以使用以下命令来启动应用:
```
npm run build
cd ../backend
npm start
```
这样,我们就可以在http://localhost:3000访问到Todo应用了。