TodoMVC是前端框架学习中的经典案例,它包含了一个待办事项应用的核心功能,足够简单又能覆盖框架的主要特性。本教程将带你使用Vue.js实现一个完整的TodoMVC应用,帮助你理解Vue的核心概念和实践技巧。
目录
项目结构
Vue TodoMVC应用主要由一个App.vue
文件组成,包含三个部分:
<script>
: 包含应用的数据和逻辑<template>
: 定义应用的HTML结构<style>
: 引入样式表
数据管理
在data()
函数中,我们定义了应用需要的数据:
data() {
return {
todos: [
{ id: 1, title: '吃饭', completed: false },
{ id: 2, title: '睡觉', completed: true },
{ id: 3, title: '打豆豆', completed: false }
],
visibility: 'all', // 当前过滤状态:'all', 'active', 'completed'
editingId: null, // 当前正在编辑的任务ID
beforeEditCache: '' // 编辑前的任务标题(用于取消编辑时恢复)
}
}
这些数据包括:
- 任务列表数组
- 当前过滤状态
- 编辑状态相关变量
添加任务
添加任务的功能通过addTodo
方法实现:
addTodo(e) {
const title = e.target.value.trim() // 获取输入框的值并去除首尾空格
if (!title) { // 如果输入为空,不添加任务
return
}
this.todos.push({ // 向todos数组添加新任务
id: Date.now(), // 使用当前时间戳作为唯一ID
title, // 任务标题
completed: false // 新任务默认未完成
})
e.target.value = '' // 清空输入框
}
在模板中,我们为输入框添加了@keyup.enter
事件监听器,当用户按下回车键时触发addTodo
方法:
<input @keyup.enter="addTodo" class="new-todo" placeholder="What needs to be done?" autofocus>
显示任务列表
使用v-for
指令遍历filteredTodos
计算属性,显示任务列表:
<li
v-for="todo in filteredTodos"
:key="todo.id"
:class="{completed: todo.completed, editing: editingId === todo.id}"
>
<!-- 任务内容 -->
</li>
filteredTodos
计算属性根据当前的visibility
值过滤任务:
filteredTodos() {
switch (this.visibility) {
case 'active':
return this.todos.filter(todo => !todo.completed)
case 'completed':
return this.todos.filter(todo => todo.completed)
default:
return this.todos
}
}
切换任务状态
使用v-model
双向绑定todo.completed
属性,实现任务状态的切换:
<input class="toggle" type="checkbox" v-model="todo.completed">
还可以使用toggleAll
方法一次性切换所有任务的状态:
toggleAll(e) {
this.todos.forEach(todo => todo.completed = e.target.checked)
}
<input id="toggle-all" class="toggle-all" type="checkbox" @click="toggleAll">
删除任务
通过removeTodo
方法删除任务:
removeTodo(todo) {
this.todos.splice(this.todos.indexOf(todo), 1)
}
在模板中,为删除按钮添加点击事件:
<button class="destroy" @click="removeTodo(todo)"></button>
编辑任务
编辑任务需要三个方法:
1. 开始编辑
editTodo(todo) {
this.beforeEditCache = todo.title // 保存编辑前的标题
this.editingId = todo.id // 设置当前编辑的任务ID
}
2. 完成编辑
doneEdit(todo) {
if (!this.editingId) return
todo.title = todo.title.trim() // 去除首尾空格
if (!todo.title) { // 如果标题为空,删除任务
this.removeTodo(todo)
}
this.editingId = null // 退出编辑模式
}
3. 取消编辑
cancelEdit(todo) {
this.editingId = null // 退出编辑模式
todo.title = this.beforeEditCache // 恢复原标题
}
在模板中,我们需要:
- 为标签添加双击事件,进入编辑模式
- 为编辑输入框添加事件处理器,处理保存和取消操作
- 使用自定义指令
v-focus
自动聚焦编辑框
<label @dblclick="editTodo(todo)" v-text="todo.title"></label>
<input class="edit"
v-model="todo.title"
@blur="doneEdit(todo)"
@keyup.enter="doneEdit(todo)"
@keyup.escape="cancelEdit(todo)"
v-focus="editingId === todo.id">
自定义指令v-focus
的实现:
directives: {
focus: {
inserted(el, binding) {
if (binding.value) {
el.focus()
}
},
update(el, binding) {
if (binding.value) {
el.focus()
}
}
}
}
过滤任务
通过URL哈希值实现任务过滤:
onHashChange() {
const hash = window.location.hash.replace(/#\/?/, '')
if (['all', 'active', 'completed'].includes(hash)) {
this.visibility = hash
} else {
window.location.hash = ''
this.visibility = 'all'
}
}
在mounted
钩子中添加哈希变化的事件监听器:
mounted() {
window.addEventListener('hashchange', this.onHashChange)
this.onHashChange()
}
在模板中,为过滤链接添加激活状态:
<ul class="filters">
<li>
<a :class="{ active: visibility === 'all' }" href="#/all">All</a>
</li>
<li>
<a :class="{ active: visibility === 'active' }" href="#/active">Active</a>
</li>
<li>
<a :class="{ active: visibility === 'completed' }" href="#/completed">Completed</a>
</li>
</ul>
清除已完成任务
通过clearCompleted
方法清除已完成的任务:
clearCompleted() {
this.todos = this.todos.filter(todo => !todo.completed)
}
在模板中,为清除按钮添加点击事件:
<button class="clear-completed" @click="clearCompleted">Clear completed</button>
显示剩余任务数量
使用activeTodoCount
计算属性获取未完成任务的数量:
activeTodoCount() {
return this.todos.filter(todo => !todo.completed).length
}
在模板中显示数量:
<span class="todo-count">
<strong>{{ activeTodoCount }}</strong> {{ activeTodoCount === 1 ? 'item' : 'items' }} left
</span>
隐藏空列表
当没有任务时,隐藏主要内容和页脚:
<section v-show="todos.length" class="main">
<!-- 主要内容 -->
</section>
<footer v-show="todos.length" class="footer">
<!-- 页脚内容 -->
</footer>
完整代码解析
下面是完整的App.vue
文件代码:
<script>
export default {
// 自定义指令:自动聚焦编辑框
directives: {
focus: {
inserted(el, binding) {
if (binding.value) {
el.focus()
}
},
update(el, binding) {
if (binding.value) {
el.focus()
}
}
}
},
// 应用数据
data() {
return {
todos: [
{ id: 1, title: '吃饭', completed: false },
{ id: 2, title: '睡觉', completed: true },
{ id: 3, title: '打豆豆', completed: false }
],
visibility: 'all',
editingId: null,
beforeEditCache: ''
}
},
// 方法
methods: {
// 添加任务
addTodo(e) {
const title = e.target.value.trim()
if (!title) {
return
}
this.todos.push({
id: Date.now(),
title,
completed: false
})
e.target.value = ''
},
// 切换所有任务状态
toggleAll(e) {
this.todos.forEach(todo => todo.completed = e.target.checked)
},
// 处理哈希变化
onHashChange() {
const hash = window.location.hash.replace(/#\/?/, '')
if (['all', 'active', 'completed'].includes(hash)) {
this.visibility = hash
} else {
window.location.hash = ''
this.visibility = 'all'
}
},
// 删除任务
removeTodo(todo) {
this.todos.splice(this.todos.indexOf(todo), 1)
},
// 清除已完成任务
clearCompleted() {
this.todos = this.todos.filter(todo => !todo.completed)
},
// 开始编辑任务
editTodo(todo) {
this.beforeEditCache = todo.title
this.editingId = todo.id
},
// 完成编辑
doneEdit(todo) {
if (!this.editingId) return
todo.title = todo.title.trim()
if (!todo.title) {
this.removeTodo(todo)
}
this.editingId = null
},
// 取消编辑
cancelEdit(todo) {
this.editingId = null
todo.title = this.beforeEditCache
}
},
// 计算属性
computed: {
// 过滤后的任务列表
filteredTodos() {
switch (this.visibility) {
case 'active':
return this.todos.filter(todo => !todo.completed)
case 'completed':
return this.todos.filter(todo => todo.completed)
default:
return this.todos
}
},
// 未完成任务数量
activeTodoCount() {
return this.todos.filter(todo => !todo.completed).length
}
},
// 生命周期钩子
mounted() {
window.addEventListener('hashchange', this.onHashChange)
this.onHashChange()
}
}
</script>
学习要点
这个TodoMVC应用展示了Vue的多种功能:
- 数据绑定:使用
v-model
实现表单输入和数据的双向绑定 - 条件渲染:使用
v-show
控制元素的显示和隐藏 - 列表渲染:使用
v-for
渲染任务列表 - 事件处理:处理用户交互,如点击、双击、按键等
- 计算属性:动态计算派生数据
- 自定义指令:扩展Vue的功能,如自动聚焦
- 生命周期钩子:在适当的时机执行代码
总结
通过实现这个TodoMVC应用,我们学习了Vue的核心概念和实践技巧。这个案例虽然简单,但包含了前端应用开发中的常见功能,如数据管理、用户交互、状态过滤等。掌握了这些基础知识,你就能够开始构建更复杂的Vue应用了。
希望这个教程能帮助你更好地理解Vue的工作原理和使用方法。接下来,你可以尝试扩展这个应用,添加更多功能,如本地存储、拖拽排序、任务分类等,进一步提升你的Vue开发技能。