当前位置: 首页 > news >正文

Vue 3 30天精进之旅:Day 21 - 项目实践:打造功能完备的Todo应用

前言

经过前20天的学习,我们已经掌握了Vue 3的核心概念、组合式API、路由、状态管理等关键技术。今天将通过一个完整的项目实践——Todo应用,将所学知识融会贯通。我们将为Todo应用添加编辑、删除、过滤等进阶功能,并优化代码结构。


一、项目回顾与初始化

假设已通过Vue CLI创建了一个基础Todo应用,当前功能包括:

  • 添加Todo项
  • 展示Todo列表
  • 切换Todo完成状态

项目结构如下:

src/
├── components/
│   └── TodoItem.vue
├── store/
│   └── index.js       # Vuex状态管理
├── router/
│   └── index.js       # 路由配置
├── views/
│   └── TodoList.vue
└── App.vue

二、功能实现:编辑Todo项

目标:双击Todo文本进入编辑模式,输入后保存修改。

1. 组件通信优化

TodoItem.vue中,添加编辑逻辑:

<template><div class="todo-item"><!-- 双击触发编辑模式 --><span v-if="!isEditing" @dblclick="enterEditMode">{{ todo.text }}</span><!-- 编辑输入框 --><input v-elsetype="text"v-model="editedText"@blur="saveEdit"@keyup.enter="saveEdit"/></div>
</template><script setup>
import { ref } from 'vue';
const props = defineProps(['todo']);
const emit = defineEmits(['edit-todo']);const isEditing = ref(false);
const editedText = ref(props.todo.text);const enterEditMode = () => {isEditing.value = true;
};const saveEdit = () => {if (editedText.value.trim()) {emit('edit-todo', {id: props.todo.id,text: editedText.value.trim()});isEditing.value = false;}
};
</script>
2. Vuex中实现编辑Mutation

store/index.js中添加:

mutations: {EDIT_TODO(state, payload) {const todo = state.todos.find(t => t.id === payload.id);if (todo) todo.text = payload.text;}
}

三、功能实现:删除Todo项(续)

1. 添加删除按钮

修改TodoItem.vue模板:

<template><div class="todo-item"><!-- ...原有内容... --><button
2. Vuex中实现删除Mutation

store/index.js中添加删除逻辑:

mutations: {// ...其他mutation...DELETE_TODO(state, todoId) {state.todos = state.todos.filter(t => t.id !== todoId);}
}
3. 组件中触发删除事件

TodoItem.vue中添加删除按钮逻辑:

<template><div class="todo-item"><!-- ...原有内容... --><button @click="deleteTodo">🗑️</button></div>
</template><script setup>
const deleteTodo = () => {emit('delete-todo', props.todo.id);
};
</script>

在父组件TodoList.vue中处理事件:

<template><TodoItem v-for="todo in filteredTodos" :key="todo.id":todo="todo"@edit-todo="editTodo"@delete-todo="deleteTodo"/>
</template><script setup>
const deleteTodo = (id) => {store.commit('DELETE_TODO', id);
};
</script>

四、功能实现:过滤Todo项

目标:添加"全部/已完成/未完成"过滤功能。

1. Vuex中添加过滤状态
// store/index.js
state: {todos: [],filter: 'all' // all | completed | active
},
getters: {filteredTodos: (state) => {switch (state.filter) {case 'completed': return state.todos.filter(t => t.done);case 'active':return state.todos.filter(t => !t.done);default:return state.todos;}}
},
mutations: {SET_FILTER(state, filter) {state.filter = filter;}
}

2. 添加过滤组件

创建components/FilterTodos.vue

<template><div class="filters"><button v-for="filter in filters":key="filter":class="{ active: currentFilter === filter }"@click="setFilter(filter)">{{ filter }}</button></div>
</template><script setup>
import { computed } from 'vue';
import { useStore } from 'vuex';const store = useStore();
const filters = ['all', 'active', 'completed'];const currentFilter = computed(() => store.state.filter);const setFilter = (filter) => {store.commit('SET_FILTER', filter);
};
</script><style scoped>
.active {background: #42b983;color: white;
}
</style>
3. 在父组件中集成
<!-- TodoList.vue -->
<template><FilterTodos /><!-- ...其他内容... -->
</template>


五、代码优化与重构

目标:提升代码可维护性

1. 模块化Vuex Store

创建store/modules/todos.js

export default {state: () => ({todos: [],filter: 'all'}),mutations: { /* ... */ },getters: { /* ... */ }
}

更新store/index.js

import todosModule from './modules/todos';export default createStore({modules: {todos: todosModule}
});
2. 组件拆分

将Todo列表项拆分为components/TodoList.vue,逻辑与视图分离。


六、添加过渡动画

优化用户体验,为Todo项添加进场/退场动画:

<!-- TodoList.vue -->
<template><TransitionGroup name="todo-list" tag="ul"><TodoItem v-for="todo in filteredTodos" :key="todo.id":todo="todo"/></TransitionGroup>
</template><style>
.todo-list-enter-active,
.todo-list-leave-active {transition: all 0.5s ease;
}
.todo-list-enter-from,
.todo-list-leave-to {opacity: 0;transform: translateX(30px);
}
</style>

七、完整功能演示

最终实现功能:

  • ✅ 添加Todo(回车确认)
  • ✅ 编辑Todo(双击修改)
  • ✅ 删除Todo(点击垃圾桶图标)
  • ✅ 切换完成状态(复选框)
  • ✅ 过滤显示(全部/进行中/已完成)
  • 🎨 平滑的过渡动画

八、总结与扩展

今日收获

  1. 实践了组件通信的多种方式(props/emit/Vuex)
  2. 掌握了Vuex状态管理模式的核心流程
  3. 体验了组合式API的模块化优势
  4. 学会使用Transition组件实现动画

扩展挑战

  • 添加本地存储持久化(localStorage)
  • 实现拖拽排序功能
  • 增加分类标签系统
  • 部署到Vercel/Netlify

相关文章:

Vue 3 30天精进之旅:Day 21 - 项目实践:打造功能完备的Todo应用

前言 经过前20天的学习&#xff0c;我们已经掌握了Vue 3的核心概念、组合式API、路由、状态管理等关键技术。今天将通过一个完整的项目实践——Todo应用&#xff0c;将所学知识融会贯通。我们将为Todo应用添加编辑、删除、过滤等进阶功能&#xff0c;并优化代码结构。 一、项目…...

32单片机学习记录1之GPIO

32单片机学习记录1之GPIO 前置 GPIO口在单片机中扮演着什么角色&#xff1f; 在单片机中&#xff0c;GPIO口&#xff08;General Purpose Input/Output&#xff09; 是一种通用输入/输出接口&#xff0c;扮演着连接单片机与外部设备的桥梁角色。具体来说&#xff0c;它在单片…...

AI 编程助手 Cline

Cline 是一款集成于 Visual Studio Code&#xff08;VS Code&#xff09;的 AI 编程助手&#xff0c;旨在提升开发者的编程效率和代码质量。 主要功能&#xff1a; 代码生成与补全&#xff1a;Cline 能根据开发者的输入&#xff0c;自动生成代码片段或完整的函数&#xff0c;减…...

YOLOv11-ultralytics-8.3.67部分代码阅读笔记-patches.py

patches.py ultralytics\utils\patches.py 目录 patches.py 1.所需的库和模块 2.def imread(filename: str, flags: int cv2.IMREAD_COLOR): 3.def imwrite(filename: str, img: np.ndarray, paramsNone): 4.def imshow(winname: str, mat: np.ndarray): 5.PyTorch…...

R语言LCMM多维度潜在类别模型流行病学研究:LCA、MM方法分析纵向数据

全文代码数据&#xff1a;https://tecdat.cn/?p39710 在数据分析领域&#xff0c;当我们面对一组数据时&#xff0c;通常会有已知的分组情况&#xff0c;比如不同的治疗组、性别组或种族组等&#xff08;点击文末“阅读原文”获取完整代码数据&#xff09;。 然而&#xff0c;…...

2025 年前端开发现状分析:卷疯了还是卷麻了?

一、前端现状&#xff1a;框架狂飙&#xff0c;开发者崩溃 如果你是个前端开发者&#xff0c;那么你大概率经历过这些场景&#xff1a; 早上打开 CSDN&#xff08;或者掘金&#xff0c;随便&#xff09;&#xff0c;发现又有新框架发布了&#xff0c;名字可能是 VueXNext.js 之…...

RDK新一代模型转换可视化工具!!!

作者&#xff1a;SkyXZ CSDN&#xff1a;SkyXZ&#xff5e;-CSDN博客 博客园&#xff1a;SkyXZ - 博客园 之前在使用的RDK X3的时候&#xff0c;吴诺老师wunuo发布了新一代量化转换工具链使用教程&#xff0c;这个工具真的非常的方便&#xff0c;能非常快速的完成X3上模型的量化…...

JVM春招快速学习指南

1.说在前面 在Java相关岗位的春/秋招面试过程中&#xff0c;JVM的学习是必不可少的。本文主要是通过《深入理解Java虚拟机》第三版来介绍JVM的学习路线和方法&#xff0c;并对没有过JVM基础的给出阅读和学习建议&#xff0c;尽可能更加快速高效的进行JVM的学习与秋招面试的备战…...

C#中的序列化和反序列化

序列化是指将对象转换为可存储或传输的格式&#xff0c;例如将对象转换为JSON字符串或字节流。反序列化则是将存储或传输的数据转换回对象的过程。这两个过程在数据持久化、数据交换以及与外部系统的通信中非常常见 把对象转换成josn字符串格式 这个过程就是序列化 josn字符…...

xcode常见设置

1、如何使用cmake构建archs为$(ARCHS_STANDARD)的xcode项目 在cmake中使用如下指令 set(CMAKE_OSX_ARCHITECTURES "$(ARCHS_STANDARD)") cmake - nomadli的博客 | nomadli Blog...

PG高可用学习@2

目录标题 一、Patroni 支持在同步复制下备库故障时自动降级为异步复制?参考依据1. PostgreSQL 官方文档2. Patroni 官方文档3. 高可用和容错设计原则 二、patroni 是如何检测备库故障的&#xff1f;1. 心跳机制2. 监控数据库进程状态3. 查询系统视图4. 复制延迟监测5. 网络连接…...

centos 8和centos 9 stream x64的区别

以下是 CentOS 8 与 CentOS Stream 9 的主要区别&#xff0c;从技术架构、更新策略到适用场景等维度进行对比&#xff1a; AI产品独立开发实战营 联系我了解 1. 定位与更新策略 特性CentOS 8CentOS Stream 9定位原为 RHEL 8 的免费稳定复刻版RHEL 9 的上游开发分支&#xff…...

C++基础学习记录—类

1、面向对象的三大特征&#xff1a;封装、继承、多态 2、类和对象 2.1、类的概念 类&#xff1a;类是一个抽象的概念&#xff0c;用于描述同一类对象的特点。 对象&#xff1a;根据类的概念所创造的实体。 类中包含属性和行为 属性&#xff1a;描述类的数据&#xff0c;一…...

云原生时代的后端开发:架构、工具与最佳实践

随着云计算的迅猛发展&#xff0c;云原生&#xff08;Cloud Native&#xff09;逐渐成为后端开发的主流趋势。云原生后端不仅能够提高应用的灵活性和可扩展性&#xff0c;还能显著优化开发和运维流程。本文将围绕云原生后端的关键概念、当前热门技术及最佳实践&#xff0c;帮助…...

ARM Cortex-M3/M4 权威指南 笔记【一】技术综述

一、Cortex-M3/M4 处理器的一般信息 1.1 处理器类型 ARM Cortex-M 为 32 位 RISC&#xff08;精简指令集&#xff09;处理器&#xff0c;其具有&#xff1a; 32位寄存器32位内部数据通路32位总线接口 除了 32 位数据&#xff0c;Cortex-M 处理器&#xff08;以及其他任何 A…...

12.项目结构

后端结构 ruoyi-admin 项目启动的入口 提供了两种启动方式 1.RuoYiApplication基于springboot,内置tomcat,直接运行。 2.RuoYiServletInitializer将springboot项目打成一个war包,用外置的servlet容器来运行。 通用功能的controller 后台登录相关的、权限控制相关的、数据字…...

保研考研机试攻略:python笔记(4)

🐨🐨🐨15各类查找 🐼🐼二分法 在我们写程序之前,我们要定义好边界,主要是考虑区间边界的闭开问题。 🐶1、左闭右闭 # 左闭右闭 def search(li, target): h = len(li) - 1l = 0#因为都是闭区间,h和l都可以取到并且相等while h >= l:mid = l + (h - l) // 2…...

高阶C语言|枚举与联合

&#x1f4ac; 欢迎讨论&#xff1a;在阅读过程中有任何疑问&#xff0c;欢迎在评论区留言&#xff0c;我们一起交流学习&#xff01; &#x1f44d; 点赞、收藏与分享&#xff1a;如果你觉得这篇文章对你有帮助&#xff0c;记得点赞、收藏&#xff0c;并分享给更多对C语言感兴…...

【天梯赛】L1-104 九宫格(C++)

易忽略的错误&#xff1a;开始习惯性地看到n就以为是n*n数组了&#xff0c;实际上应该是9*9的固定大小数组&#xff0c;查了半天没查出来 题面 L1-104 九宫格 - 团体程序设计天梯赛-练习集 代码实现 #include<bits/stdc.h> using namespace std; //易错&#xff1a;开…...

现代C++多线程基础 -忆苦思甜pthread_mutex

c 老古董 文章目录 c 老古董pthread_mutex概念常用apipthread_mutex_initpthread_mutex_lockpthread_mutex_trylockpthread_mutex_unlockpthread_mutex_destroy 案例 pthread_mutex 概念 互斥锁 mutex是一种简单的加锁的方法来控制对共享资源的访问&#xff0c;mutex只有两种…...

Qwen3-ASR-1.7B部署案例:高校科研组构建本地化学术讲座语音知识库

Qwen3-ASR-1.7B部署案例&#xff1a;高校科研组构建本地化学术讲座语音知识库 1. 项目背景与价值 高校科研团队经常举办各类学术讲座和研讨会&#xff0c;这些宝贵的学术内容通常以音频形式记录。传统的人工转录方式耗时耗力&#xff0c;且对于专业术语密集的学术内容&#x…...

Agisoft Metashape相机标定实战:从原理到精准操作

1. 相机标定为什么重要&#xff1f;从拍照误差说起 每次用手机拍文档时&#xff0c;边缘文字总会出现弯曲变形&#xff1b;航拍测绘时&#xff0c;明明飞行路线笔直&#xff0c;生成的模型却出现波浪形扭曲——这些问题的根源往往在于镜头畸变。就像近视眼看到的世界会有变形&a…...

如何通过DeepWiki实现本地部署的智能文档生成与数据安全保障?

如何通过DeepWiki实现本地部署的智能文档生成与数据安全保障&#xff1f; 【免费下载链接】deepwiki-open Open Source DeepWiki: AI-Powered Wiki Generator for GitHub Repositories 项目地址: https://gitcode.com/gh_mirrors/de/deepwiki-open 在数字化开发的浪潮中…...

Keil“魔法棒”全解析:从Device到Utilities的配置秘籍

1. 认识Keil的"魔法棒"&#xff1a;Options for Target对话框 第一次打开Keil MDK时&#xff0c;工具栏上那个带着星星的魔法棒图标总是特别引人注目。这个被开发者亲切称为"魔法棒"的按钮&#xff0c;实际上是整个开发环境中最强大的配置中心——Options …...

从RS232到112G SerDes:高速串行接口的‘逆袭’简史与FPGA工程师的生存指南

从RS232到112G SerDes&#xff1a;高速串行接口的技术革命与工程师转型指南 在数字通信领域&#xff0c;接口技术的演进犹如一场静默的革命。二十年前&#xff0c;工程师们还在为并行总线的布线复杂度和时钟偏移问题头疼不已&#xff1b;而今天&#xff0c;单通道112G PAM4 Ser…...

HAProxy热加载卡住?试试这个systemd wrapper解决方案(附完整源码解析)

HAProxy热加载卡住&#xff1f;深入解析systemd兼容性问题与高效解决方案 当你在深夜处理线上流量激增问题时&#xff0c;突然发现HAProxy的热加载命令systemctl reload haproxy竟然卡住了——这种场景足以让任何运维工程师心跳加速。这不是简单的配置错误&#xff0c;而是syst…...

Langchain与Qwen结合:如何用Python构建一个智能问答机器人(含联网搜索功能)

Langchain与Qwen结合&#xff1a;如何用Python构建一个智能问答机器人&#xff08;含联网搜索功能&#xff09; 在人工智能技术快速发展的今天&#xff0c;构建一个能够理解自然语言并提供准确回答的智能系统已不再是遥不可及的梦想。通过结合Langchain框架和Qwen大语言模型&a…...

uniapp中集成leaflet地图的3个坑与解决方案(附完整代码)

uniapp中集成leaflet地图的3个坑与解决方案&#xff08;附完整代码&#xff09; 在移动端开发领域&#xff0c;uniapp因其跨平台特性广受欢迎&#xff0c;而leaflet作为轻量级地图库也备受青睐。但当两者结合时&#xff0c;开发者往往会遇到一些意想不到的挑战。本文将深入剖析…...

Qwen3.5-27B部署教程(Docker进阶):自定义模型路径、挂载外部存储与日志卷

Qwen3.5-27B部署教程&#xff08;Docker进阶&#xff09;&#xff1a;自定义模型路径、挂载外部存储与日志卷 1. 环境准备与快速部署 在开始之前&#xff0c;请确保您的系统满足以下要求&#xff1a; 硬件要求&#xff1a;至少4张RTX 4090 D 24GB显卡软件要求&#xff1a;已…...

别再手动点点点了!用Python脚本自动化调用Dify工作流API(附完整代码)

用Python脚本实现Dify工作流API的自动化调用与生产级实践 在数据处理和AI应用开发中&#xff0c;手动操作Web界面不仅效率低下&#xff0c;也难以应对批量任务的需求。本文将介绍如何通过Python脚本将Dify工作流API封装为可复用的自动化工具&#xff0c;并分享生产环境中常见的…...