全栈笔记之 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: 似水流年