全栈笔记之 TodoMVC by React and TypeScript
Table of Contents
这个 TypeScript + React 的 TodoMVC 项目是一个经典的前端示例应用,适合学习如何用 React 和 TypeScript 构建一个小型的功能完整的应用。接下来,我会 详细拆解代码结构和核心逻辑,让你更容易理解。
一、项目结构概览
项目的目录结构如下:
typescript-react/
│── index.html
│── package.json
│── tsconfig.json
│── src/
│ ├── index.tsx
│ ├── components/
│ │ ├── App.tsx
│ │ ├── TodoItem.tsx
│ │ ├── TodoFooter.tsx
│ ├── models/
│ │ ├── Todo.ts
每个文件的作用如下:
index.html
: HTML 入口文件,包含<div id="app"></div>
,React 应用会挂载到这里。package.json
: 记录了项目依赖(React、TypeScript等),以及运行和打包的脚本。tsconfig.json
: TypeScript 配置文件,定义了代码编译规则。src/
:index.tsx
: React 入口文件,把App
组件渲染到 HTML 中。components/
:App.tsx
: 应用的核心组件,负责管理待办事项的状态(添加、删除、修改)。TodoItem.tsx
: 代表一个待办事项的组件,渲染待办事项,并处理勾选/删除等操作。TodoFooter.tsx
: 负责底部筛选、统计和清除已完成任务的功能。models/
:Todo.ts
: 定义Todo
类型结构,方便 TypeScript 进行类型检查。
二、代码详细解析
1. index.tsx
- 入口文件
import React from "react";
import { createRoot } from "react-dom/client";
import { App } from "./components/App";
const root = createRoot(document.getElementById("app")!);
root.render(<App />);
解析:
- 引入 React 和 ReactDOM,用于渲染 React 组件。
createRoot(document.getElementById("app")!)
: 在index.html
里找到<div id="app">
并挂载 React 应用。root.render(<App />);
: 渲染App
组件,它是应用的根组件。
2. models/Todo.ts
- 定义 Todo 类型
export interface Todo {
id: number;
text: string;
completed: boolean;
}
解析:
- 定义
Todo
类型,有id
(唯一标识)、text
(任务内容)、completed
(是否完成)。 - 这个类型被
App.tsx
和TodoItem.tsx
用来确保传递的数据符合预期。
3. App.tsx
- 应用的核心组件
import React, { useState } from "react";
import { Todo } from "../models/Todo";
import { TodoItem } from "./TodoItem";
import { TodoFooter } from "./TodoFooter";
export const App: React.FC = () => {
const [todos, setTodos] = useState<Todo[]>([]);
const addTodo = (text: string) => {
setTodos([...todos, { id: Date.now(), text, completed: false }]);
};
const toggleTodo = (id: number) => {
setTodos(
todos.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
)
);
};
const removeTodo = (id: number) => {
setTodos(todos.filter(todo => todo.id !== id));
};
return (
<div>
<h1>待办事项</h1>
<input
type="text"
placeholder="添加新的待办事项"
onKeyDown={(e) => {
if (e.key === "Enter" && e.currentTarget.value) {
addTodo(e.currentTarget.value);
e.currentTarget.value = "";
}
}}
/>
<ul>
{todos.map((todo) => (
<TodoItem key={todo.id} todo={todo} toggleTodo={toggleTodo} removeTodo={removeTodo} />
))}
</ul>
<TodoFooter todos={todos} setTodos={setTodos} />
</div>
);
};
解析:
useState<Todo[]>([])
: 定义todos
状态(存储所有待办事项)。addTodo(text)
: 按Enter
键添加新任务。toggleTodo(id)
: 切换任务状态(已完成/未完成)。removeTodo(id)
: 删除任务。- 渲染
TodoItem
组件,并传递todo
数据和操作方法。
4. TodoItem.tsx
- 单个待办事项
import React from "react";
import { Todo } from "../models/Todo";
interface TodoItemProps {
todo: Todo;
toggleTodo: (id: number) => void;
removeTodo: (id: number) => void;
}
export const TodoItem: React.FC<TodoItemProps> = ({ todo, toggleTodo, removeTodo }) => {
return (
<li>
<input type="checkbox" checked={todo.completed} onChange={() => toggleTodo(todo.id)} />
<span style={{ textDecoration: todo.completed ? "line-through" : "none" }}>
{todo.text}
</span>
<button onClick={() => removeTodo(todo.id)}>删除</button>
</li>
);
};
解析:
todo
: 一个待办事项对象(text
、completed
等)。toggleTodo(todo.id)
: 点击复选框切换完成状态。removeTodo(todo.id)
: 点击删除按钮移除任务。
5. TodoFooter.tsx
- 底部筛选和清除功能
import React from "react";
import { Todo } from "../models/Todo";
interface TodoFooterProps {
todos: Todo[];
setTodos: React.Dispatch<React.SetStateAction<Todo[]>>;
}
export const TodoFooter: React.FC<TodoFooterProps> = ({ todos, setTodos }) => {
const clearCompleted = () => {
setTodos(todos.filter(todo => !todo.completed));
};
return (
<footer>
<span>剩余任务: {todos.filter(todo => !todo.completed).length} 项</span>
<button onClick={clearCompleted}>清除已完成任务</button>
</footer>
);
};
解析:
- 统计未完成任务数量 并显示出来。
- 提供 "清除已完成任务" 按钮,点击后删除所有
completed: true
的任务。
三、总结
index.tsx
入口文件:加载App
组件。App.tsx
负责管理 任务列表的状态,并渲染TodoItem
和TodoFooter
组件。TodoItem.tsx
负责 渲染单个待办事项,支持切换完成状态和删除任务。TodoFooter.tsx
负责 任务统计和清除已完成任务。
整个应用基于 React 的 组件化架构,使用 TypeScript 进行 类型检查,让代码更安全可维护。你可以尝试自己修改代码,加深理解!
Comments |0|
Category: 似水流年