🚀 从DOM操作到Vue3:一个Todo应用的思维革命
前言 :当我第一次学习前端时,导师让我实现一个Todo应用。我花了2小时写了50行代码,导师看了一眼说:"试试Vue3吧。" 我用30分钟重写了同样的功能,代码减少到20行。那一刻,我明白了什么是真正的数据驱动开发。今天,我想通过这个Todo应用,带你体验这场思维革命。
第一章:传统开发方式的困境
让我们先回顾一下用原生JavaScript实现的Todo应用:
html
<!-- demo.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>传统Todo应用</title>
</head>
<body>
<h2 id="app"></h2>
<input type="text" id="todo-input">
<script>
// 传统做法:命令式编程
const app = document.getElementById('app')
const todoInput = document.getElementById('todo-input')
// 手动监听事件
todoInput.addEventListener('change', function(event){
const todo = event.target.value.trim()
if(!todo){
console.log('请输入任务')
return
}
// 手动更新DOM
app.innerHTML = todo
})
</script>
</body>
</html>
🔍 传统方式的三大痛点:
- 命令式编程:你需要像指挥官一样告诉浏览器每一步该做什么
- DOM操作繁琐:每次数据变化都要手动查找和更新DOM
- 关注点错位:80%的代码在处理界面操作,只有20%在处理业务逻辑
这就像每次想改变房间布局,都要亲自搬砖砌墙。
第二章:Vue3的数据驱动革命
现在,让我们看看用Vue3实现的完整Todo应用:
vue
<!-- App.vue -->
<template>
<div>
<!-- 1. 数据绑定 -->
<h2>{{title}}</h2>
<!-- 2. 双向数据绑定 -->
<input
type="text"
v-model="title"
@keydown.enter="addTodo"
placeholder="输入任务后按回车"
>
<!-- 3. 条件渲染 -->
<ul v-if="todos.length">
<!-- 4. 列表渲染 -->
<li v-for="todo in todos" :key="todo.id">
<!-- 5. 双向绑定到对象属性 -->
<input type="checkbox" v-model="todo.done">
<!-- 6. 动态class绑定 -->
<span :class="{done: todo.done}">{{todo.title}}</span>
</li>
</ul>
<!-- 7. v-else指令 -->
<div v-else>
暂无任务
</div>
<!-- 8. 计算属性使用 -->
<div>
进度:{{activeTodos}} / {{todos.length}}
</div>
<!-- 9. 计算属性的getter/setter -->
全选<input type="checkbox" v-model="allDone">
</div>
</template>
<script setup>
// 10. Composition API导入
import { ref, computed, watch } from 'vue'
// 11. 响应式数据
const title = ref("Todos任务清单")
const todos = ref([
{
id: 1,
title: '学习vue',
done: false
},
{
id: 2,
title: '打王者',
done: false
},
{
id: 3,
title: '吃饭',
done: true
}
])
// 12. 计算属性
const activeTodos = computed(() => {
return todos.value.filter(todo => !todo.done).length
})
// 13. 方法定义
const addTodo = () => {
if(!title.value) return
todos.value.push({
id: Date.now(), // 更好的ID生成方式
title: title.value,
done: false
})
title.value = ""
}
// 14. 计算属性的getter/setter
const allDone = computed({
get() {
return todos.value.length > 0 &&
todos.value.every(todo => todo.done)
},
set(val) {
todos.value.forEach(todo => todo.done = val)
}
})
// 15. 监听器 - 补充知识点
watch(todos, (newTodos) => {
console.log('任务列表发生变化:', newTodos)
// 可以在这里实现本地存储
}, { deep: true })
// 16. 生命周期钩子 - 补充知识点
import { onMounted } from 'vue'
onMounted(() => {
console.log('组件挂载完成')
// 可以在这里从本地存储读取数据
})
</script>
<style>
.done {
color: gray;
text-decoration: line-through;
}
/* 17. 组件样式作用域 - 补充知识点 */
/* 这里的样式只作用于当前组件 */
</style>
第三章:Vue3核心API深度解析
🎯 1. ref - 响应式数据的基石
代码:
javascript
const title = ref("Todos任务清单")
补充:
ref用于创建响应式引用- 访问值需要使用
.value - 为什么需要
.value?因为Vue需要知道哪些数据需要被追踪变化
javascript
// ref的内部原理简化版
function ref(initialValue) {
let value = initialValue
return {
get value() {
// 这里可以收集依赖
return value
},
set value(newValue) {
value = newValue
// 这里可以通知更新
}
}
}
🎯 2. v-model - 双向绑定的魔法
代码:
html
<input type="text" v-model="title">
补充: v-model实际上是语法糖,它等于:
html
<input
:value="title"
@input="title = $event.target.value"
>
对于复选框,v-model的处理有所不同:
html
<input type="checkbox" v-model="todo.done">
<!-- 等价于 -->
<input
type="checkbox"
:checked="todo.done"
@change="todo.done = $event.target.checked"
>
🎯 3. 指令系统详解
v-show vs v-if:
vue
<!-- v-if是真正的条件渲染 -->
<div v-if="show">条件渲染</div> <!-- 会从DOM中移除/添加 -->
<!-- v-show只是控制display -->
<div v-show="show">显示控制</div> <!-- 始终在DOM中,只是display切换 -->
动态参数:
vue
<!-- 动态指令参数 -->
<a :[attributeName]="url">链接</a>
<button @[eventName]="doSomething">按钮</button>
🎯 4. computed - 智能计算属性
细节
javascript
// 计算属性的缓存特性
const expensiveCalculation = computed(() => {
console.log('重新计算') // 只有依赖变化时才会执行
return todos.value
.filter(todo => !todo.done)
.map(todo => todo.title.toUpperCase())
.join(', ')
})
// 依赖没有变化时,直接返回缓存值
console.log(expensiveCalculation.value) // 输出并打印"重新计算"
console.log(expensiveCalculation.value) // 直接返回缓存值,不打印
🎯 5. watch - 数据监听器
重要知识点:
javascript
// 1. 监听单个ref
watch(title, (newTitle, oldTitle) => {
console.log(`标题从"${oldTitle}"变为"${newTitle}"`)
})
// 2. 监听多个数据源
watch([title, todos], ([newTitle, newTodos], [oldTitle, oldTodos]) => {
// 处理变化
})
// 3. 立即执行的watch
watch(todos, (newTodos) => {
localStorage.setItem('todos', JSON.stringify(newTodos))
}, { immediate: true }) // 组件创建时立即执行一次
// 4. 深度监听
watch(todos, (newTodos) => {
// 可以检测到对象内部属性的变化
}, { deep: true })
🎯 6. 生命周期钩子
完整生命周期:
javascript
import {
onBeforeMount,
onMounted,
onBeforeUpdate,
onUpdated,
onBeforeUnmount,
onUnmounted,
onErrorCaptured
} from 'vue'
onBeforeMount(() => {
console.log('组件挂载前')
})
onMounted(() => {
console.log('组件已挂载,可以访问DOM')
})
onBeforeUpdate(() => {
console.log('组件更新前')
})
onUpdated(() => {
console.log('组件已更新')
})
onBeforeUnmount(() => {
console.log('组件卸载前')
})
onUnmounted(() => {
console.log('组件已卸载')
})
onErrorCaptured((error) => {
console.error('捕获到子组件错误:', error)
})
第四章:Vue3开发模式的优势
🚀 1. 开发效率对比
| 功能 | 传统JS代码量 | Vue3代码量 | 效率提升 |
|---|---|---|---|
| 数据绑定 | 10-15行 | 1行 | 90% |
| 列表渲染 | 15-20行 | 3行 | 85% |
| 事件处理 | 5-10行 | 1行 | 80% |
| 样式绑定 | 5-10行 | 1行 | 80% |
🎯 2. 思维模式转变
传统开发思维(怎么做):
markdown
1. 找到DOM元素
2. 监听事件
3. 获取数据
4. 操作DOM更新界面
Vue3开发思维(要什么):
markdown
1. 定义数据状态
2. 描述UI与数据的关系
3. 修改数据
4. 界面自动更新
💡 3. 性能优化自动化
Vue3自动为你做了这些优化:
javascript
// 1. 虚拟DOM减少真实DOM操作
// 2. Diff算法最小化更新
// 3. 响应式系统精确追踪依赖
// 4. 计算属性缓存避免重复计算
// 5. 组件复用减少渲染开销
第五章:实战技巧与最佳实践
📝 1. 代码组织建议
vue
<script setup>
// 1. 导入部分
import { ref, computed, watch, onMounted } from 'vue'
// 2. 响应式数据
const title = ref('')
const todos = ref([])
// 3. 计算属性
const activeCount = computed(() => { /* ... */ })
// 4. 方法定义
const addTodo = () => { /* ... */ }
// 5. 生命周期
onMounted(() => { /* ... */ })
// 6. 监听器
watch(todos, () => { /* ... */ })
</script>
🎨 2. 样式管理技巧
vue
<style scoped>
/* scoped属性让样式只作用于当前组件 */
.todo-item {
padding: 10px;
}
/* 深度选择器 */
:deep(.child-component) {
color: red;
}
/* 全局样式 */
:global(.global-class) {
font-size: 16px;
}
</style>
🔧 3. 调试技巧
javascript
// 1. 在模板中调试
<div>{{ debugInfo }}</div>
// 2. 使用Vue Devtools浏览器插件
// 3. 使用console.log增强
watch(todos, (newTodos) => {
console.log('todos变化:', JSON.stringify(newTodos, null, 2))
}, { deep: true })
结语:从学习者到实践者
通过这个Todo应用,我们看到了Vue3如何将我们从繁琐的DOM操作中解放出来,让我们能更专注于业务逻辑。这种声明式编程的思维方式,不仅让代码更简洁,也让开发更高效。
记住:
- Vue3不是魔法,但它让开发变得像魔法一样简单
- 学习Vue3不仅是学习一个框架,更是学习一种更好的编程思维
- 从今天开始,尝试用数据驱动的方式思考问题
下一步建议:
- 在Vue Playground中多练习
- 阅读Vue3官方文档
- 尝试实现更复杂的功能(过滤、搜索、排序)
- 学习Vue Router和Pinia
📚 资源推荐:
希望这篇文章能帮助你更好地理解Vue3的强大之处!如果你有任何问题或想法,欢迎在评论区讨论交流。🌟
一起进步,从今天开始!