1. 为什么选择Vue.js开发Todo应用?
Vue.js作为当前最流行的前端框架之一,特别适合用来构建交互式的清单应用。我最初接触Vue是在2016年,当时就被它的响应式数据绑定和组件化开发模式所吸引。相比其他框架,Vue的学习曲线更为平缓,这使它成为新手入门前端开发的绝佳选择。
Todo应用看似简单,实则涵盖了现代Web开发的多个核心概念:
- 数据绑定(v-model)
- 条件渲染(v-if/v-show)
- 列表渲染(v-for)
- 事件处理(@click)
- 组件通信
- 状态管理(Vuex/Pinia)
在Vue 3版本中,组合式API(Composition API)的引入让代码组织更加灵活。我建议新手从Vue 3开始学习,因为这是未来的方向,而且其逻辑复用能力在实际项目中非常实用。
提示:虽然Vue 2仍然被广泛使用,但新项目建议直接采用Vue 3。两者在基础概念上是一致的,学会Vue 3后很容易适应Vue 2。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备与项目初始化
2.1 安装Node.js与包管理器
首先需要安装Node.js(建议16.x或18.x LTS版本),它会自带npm包管理器。我个人更推荐使用yarn或pnpm,它们在速度和依赖管理上更有优势:
bash复制# 使用npm安装yarn
npm install -g yarn
# 或安装pnpm
npm install -g pnpm
2.2 创建Vue项目
Vue官方提供了两种创建项目的方式:
- 通过Vue CLI(适合传统选项式API项目)
- 通过Vite(现代构建工具,速度更快)
对于新手,我推荐使用Vite,因为它的配置更简单,开发服务器启动更快:
bash复制# 使用npm
npm create vite@latest vue-todo --template vue
# 或使用yarn
yarn create vite vue-todo --template vue
# 或使用pnpm
pnpm create vite vue-todo --template vue
创建完成后,进入项目目录并安装依赖:
bash复制cd vue-todo
npm install # 或yarn/pnpm install
2.3 项目结构解析
典型的Vue项目结构如下:
code复制vue-todo/
├── public/ # 静态资源
├── src/
│ ├── assets/ # 图片等资源
│ ├── components/ # 组件
│ ├── App.vue # 根组件
│ └── main.js # 应用入口
├── index.html # 主HTML文件
├── package.json # 项目配置
└── vite.config.js # Vite配置
3. 核心功能实现
3.1 数据模型设计
Todo应用的核心是任务列表,每个任务包含以下属性:
- id:唯一标识
- title:任务标题
- completed:是否完成
- createdAt:创建时间
- priority:优先级
在Vue 3中,我们使用ref或reactive来定义响应式数据:
javascript复制// src/components/TodoList.vue
import { ref } from 'vue'
const todos = ref([
{
id: 1,
title: '学习Vue 3',
completed: false,
createdAt: new Date(),
priority: 'high'
}
])
const newTodo = ref('')
3.2 添加新任务
实现添加功能的几个关键点:
- 表单双向绑定(v-model)
- 提交事件处理(@submit.prevent)
- 数据验证
html复制<!-- src/components/TodoList.vue -->
<form @submit.prevent="addTodo">
<input
v-model="newTodo"
placeholder="输入新任务..."
required
>
<button type="submit">添加</button>
</form>
javascript复制const addTodo = () => {
if (!newTodo.value.trim()) return
todos.value.push({
id: Date.now(),
title: newTodo.value.trim(),
completed: false,
createdAt: new Date(),
priority: 'medium'
})
newTodo.value = ''
}
3.3 任务列表展示
使用v-for渲染列表,结合v-bind动态绑定class:
html复制<ul>
<li
v-for="todo in todos"
:key="todo.id"
:class="{ completed: todo.completed }"
>
<input
type="checkbox"
v-model="todo.completed"
>
<span>{{ todo.title }}</span>
<button @click="removeTodo(todo.id)">删除</button>
</li>
</ul>
css复制.completed {
text-decoration: line-through;
opacity: 0.6;
}
3.4 任务筛选功能
实现按状态筛选(全部/已完成/未完成):
javascript复制const filter = ref('all')
const filteredTodos = computed(() => {
switch (filter.value) {
case 'active':
return todos.value.filter(t => !t.completed)
case 'completed':
return todos.value.filter(t => t.completed)
default:
return todos.value
}
})
html复制<div class="filters">
<button @click="filter = 'all'">全部</button>
<button @click="filter = 'active'">未完成</button>
<button @click="filter = 'completed'">已完成</button>
</div>
4. 进阶功能实现
4.1 本地存储持久化
使用localStorage保存数据,避免刷新后丢失:
javascript复制// 保存到本地存储
const saveToLocalStorage = () => {
localStorage.setItem('vue-todos', JSON.stringify(todos.value))
}
// 从本地存储加载
const loadFromLocalStorage = () => {
const saved = localStorage.getItem('vue-todos')
if (saved) {
todos.value = JSON.parse(saved)
}
}
// 在组件挂载时加载
onMounted(() => {
loadFromLocalStorage()
})
// 在数据变化时保存
watch(todos, () => {
saveToLocalStorage()
}, { deep: true })
4.2 编辑任务功能
实现双击编辑功能:
html复制<span
v-if="!todo.isEditing"
@dblclick="startEditing(todo)"
>
{{ todo.title }}
</span>
<input
v-else
v-model="todo.title"
@blur="stopEditing(todo)"
@keyup.enter="stopEditing(todo)"
>
javascript复制const startEditing = (todo) => {
todo.isEditing = true
nextTick(() => {
// 自动聚焦到输入框
document.querySelector(`input[data-id="${todo.id}"]`).focus()
})
}
const stopEditing = (todo) => {
todo.isEditing = false
if (!todo.title.trim()) {
removeTodo(todo.id)
}
}
4.3 使用Pinia进行状态管理
当应用变得复杂时,建议使用Pinia(Vue官方推荐的状态管理库):
bash复制npm install pinia
创建store:
javascript复制// src/stores/todo.js
import { defineStore } from 'pinia'
export const useTodoStore = defineStore('todo', {
state: () => ({
todos: [],
filter: 'all'
}),
getters: {
filteredTodos(state) {
// 筛选逻辑
}
},
actions: {
addTodo(title) {
// 添加逻辑
},
removeTodo(id) {
// 删除逻辑
}
}
})
在组件中使用:
javascript复制import { useTodoStore } from '@/stores/todo'
const store = useTodoStore()
// 访问状态
store.todos
// 调用action
store.addTodo('新任务')
5. 样式优化与用户体验
5.1 使用CSS变量实现主题切换
css复制:root {
--primary-color: #42b983;
--bg-color: #ffffff;
--text-color: #2c3e50;
}
[data-theme="dark"] {
--primary-color: #42d392;
--bg-color: #1a1a1a;
--text-color: #ffffff;
}
body {
background-color: var(--bg-color);
color: var(--text-color);
transition: all 0.3s ease;
}
5.2 添加动画效果
使用Vue的过渡组件:
html复制<transition-group name="fade" tag="ul">
<li v-for="todo in filteredTodos" :key="todo.id">
<!-- 任务内容 -->
</li>
</transition-group>
css复制.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
5.3 响应式设计
确保在移动设备上也有良好体验:
css复制@media (max-width: 600px) {
.todo-app {
width: 90%;
margin: 20px auto;
}
input[type="text"] {
width: 70%;
}
}
6. 测试与部署
6.1 单元测试
使用Vitest(与Vite集成良好的测试框架):
bash复制npm install -D vitest @vue/test-utils
编写测试用例:
javascript复制// tests/todo.spec.js
import { mount } from '@vue/test-utils'
import TodoList from '../src/components/TodoList.vue'
test('添加新任务', async () => {
const wrapper = mount(TodoList)
const input = wrapper.find('input[type="text"]')
await input.setValue('测试任务')
await wrapper.find('form').trigger('submit')
expect(wrapper.vm.todos).toHaveLength(1)
})
6.2 部署到生产环境
使用Vite构建生产版本:
bash复制npm run build
这会生成dist目录,包含优化后的静态文件。你可以:
- 部署到GitHub Pages
- 使用Netlify/Vercel等平台
- 部署到自己的服务器
对于简单的演示,可以使用Vercel:
bash复制npm install -g vercel
vercel
7. 常见问题与解决方案
7.1 响应式数据不更新
确保你正确使用了ref或reactive,直接修改数组或对象属性时:
javascript复制// 错误
todos.value[0].completed = true // 可能不会触发更新
// 正确
todos.value = todos.value.map(t =>
t.id === id ? { ...t, completed: !t.completed } : t
)
7.2 组件间通信
对于父子组件:
- 父传子:props
- 子传父:$emit
对于非父子组件:
- 使用provide/inject
- 使用Pinia状态管理
7.3 性能优化
- 对于长列表,使用虚拟滚动(vue-virtual-scroller)
- 避免不必要的重新渲染,使用v-once或v-memo
- 合理使用computed属性缓存计算结果
8. 项目扩展思路
完成基础功能后,可以考虑添加:
- 用户认证系统
- 多清单分类
- 任务提醒功能
- 数据同步到云端
- PWA支持(离线使用)
- 拖拽排序功能
我在实际项目中发现,Todo应用虽然简单,但可以无限扩展。每个新增功能都是学习Vue新特性的好机会。比如添加拖拽排序时,我学会了使用vue-draggable-next库;实现PWA时,了解了Service Worker的工作原理。
