📝 用 Vue 开发 Todos 任务清单
你有没有过这样的经历?想做个简单的任务清单,结果写着写着就陷入了 "找 DOM、改 DOM、DOM 又乱了" 的死循环🤯?今天咱们就来聊聊,从传统 JS 的 "手动搬砖" 到 Vue 的 "数据驱动躺平",开发一个 Todos 任务清单到底能有多爽!
🔙 一、传统做法:被 DOM 支配的恐惧
先回忆一下,没学 Vue 的时候咱们是怎么写任务清单的?就像demo.html里展示的那样,全程被 DOM 牵着鼻子走:
html
<!-- 传统JS操作DOM示例 -->
<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;
}
app.innerHTML = todo;
});
</script>
这段代码翻译成人话就是:先费劲吧啦找到<h2>和输入框(getElementById),然后监听输入框的变化(addEventListener),拿到输入内容后,再手动把内容塞给<h2>(innerHTML)。这还只是改个标题,要是想加个列表、弄个复选框、统计完成数量... 光是想想那些appendChild、removeChild就头大!
传统 DOM 编程就像用手一块块砌砖:每加一块砖(改一个元素)都得亲自搬、亲自放,稍有不慎就砌歪了(DOM 结构乱了)。效率低不说,还特别容易出错 —— 这大概就是前端开发者早期的 "工伤" 吧😂。
✨ 二、响应式数据驱动:让数据自己 "动" 起来
Vue 的出现,直接把我们从 "搬砖工地" 拉到了 "全自动生产线"!它的核心思想就是响应式数据驱动界面—— 简单说就是:你只管管好数据,界面怎么变,Vue 替你操心。
1、什么是响应式数据驱动?
就像你在手机上改了通讯录名字,通话记录里的名字会自动更新一样:数据变了,依赖这个数据的界面元素会 "聪明地" 自动更新,完全不用你手动操作 DOM。
2、为啥非要用它?
- 省脑子:不用记哪个 DOM 元素对应哪个数据,不用写一堆
xxx.innerHTML - 性能好:Vue 会智能追踪数据变化,只更新需要改的地方(比手动操作 DOM 高效多了)
- 好维护:数据逻辑和界面展示分开,代码像整理好的衣柜,而不是堆成山的衣服
3、Vue 怎么实现响应式数据驱动?
Vue 不会自动监控所有数据,需要你用专属 API 把普通数据 “包装” 成响应式数据,常用的就是 ref 和 reactive:
(1) ref:给基本类型数据(字符串 / 数字)装监控
适合单个值(比如标题、数量),底层是 “把基本类型包成对象,再用 Proxy 监控”:
javascript
import { ref } from 'vue';
// 普通字符串 → 响应式数据(带.value的包装对象)
const title = ref("Todos任务清单");
// 修改时必须用.value(因为是包装对象)
title.value = '我的今日计划';
// 修改后,所有依赖title的界面元素(h2、输入框)自动更新
(2) reactive:给对象 / 数组装监控
适合复杂数据(比如任务列表、用户信息),直接返回 Proxy 代理对象,不用.value:
javascript
import { reactive } from 'vue';
// 普通数组 → 响应式数组
const todos = reactive([{ id:1, title:'打王者', done:false }]);
// 直接修改,不用.value
todos.push({ id:2, title:'学Vue' });
💡 小技巧:Todos 案例里用ref包裹数组也能行(const todos = ref([])),本质是 ref 自动把数组转成 reactive 的代理对象,只是需要.value访问。
🚀 三、Todos 任务清单项目:Vue 实战秀
说了这么多,咱们来看看用 Vue 做的 Todos 任务清单长啥样 —— 这可是个 "麻雀虽小,五脏俱全" 的经典案例!
1、项目效果亮个相
- 顶部有个大标题,还能通过输入框改(双向绑定的魔力✨)
- 输入框敲回车,就能新增任务(@keydown.enter 立功了)
- 任务列表里的复选框一点,任务就会变灰加删除线(:class 动态样式)
- 任务为空时显示 "暂无计划",有任务时才显示列表(v-if/v-else 切换)
- 底部有全选按钮,还能显示 "已完成数 / 总任务数"(computed 计算属性的功劳)
2、代码速览
vue
<template>
<div>
<h2>
<!-- 数据绑定 -->
{{ title }}
</h2>
<!-- 双向数据绑定 -->
<!-- @ v-bind: 的缩写,不用addEventListener -->
<!-- @event-name.enter 监听键盘输入,当按下回车的时候 -->
<input type="text" v-model="title" @keydown.enter="addTodo">
<!-- 条件渲染指令 -->
<ul v-if="todos.length">
<!-- key 唯一属性 -->
<li v-for="todo in todos" :key="todo.id">
<input type="checkbox" v-model="todo.done">
<!-- // : v-bind:缩写 js表达式
vue 有一定的学习 api 对用户非常友好,好上手
-->
<span :class="{done:todo.done}">{{ todo.title }}</span>
</li>
</ul>
<div v-else>
暂无计划
</div>
<div>
全选<input type="checkbox" v-model="allDone">
<!-- {{ 数据绑定 表达式结果绑定 }} -->
<!-- {{ todos.filter(todo => todo.done).length }} -->
{{ active }}
/
{{ todos.length }}
</div>
</div>
</template>
<script setup>
// 业务是界面上要动态展示标题,且编辑标题
// vue focus 标题数据业务,修改数据,余下的dom更新vue 替我们做了
// setup vue3的composition 组合式 API
// vue2 options API
import {ref,computed} from 'vue'
// 响应式数据
const title = ref("Todos任务清单");
const todos = ref([
{
id:1,
title:'打王者',
done:false
},
{
id:1,
title:'吃饭',
done:true
}
// '睡觉',
// '学习Vue'
])
// 依赖于todos 响应式数据的 计算属性
// 形式上是函数(计算过程),结果(计算属性)返回
// 也是响应式的 依赖todos
// computed 缓存性能优化 只有todos 变化时才会重新计算
const active = computed(() => {
return todos.value.filter(todo => todo.done).length;
})
const addTodo = ()=>{
// focus 业务
if(!title.value) {
return;
}
todos.value.push({
id:Math.random(),
title: title.value,
done:false});
title.value ='';
}
// computed 高级技巧
// get set 属性概念
const allDone = computed({
get() {
return todos.value.every(todo => todo.done);
},
set(val) {
todos.value.forEach(todo => todo.done = val);
}
})
</script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background-color: #ffc0cb;
min-height: 100vh;
padding: 20px;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
#app {
background-color: #ffc0cb;
min-height: 100vh;
}
#app > div {
display: flex !important;
flex-direction: column !important;
align-items: center;
gap: 20px;
max-width: 600px;
margin: 40px auto;
padding: 30px;
background-color: #fff;
border-radius: 20px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
}
h2 {
font-size: 28px;
color: #d63384;
margin-bottom: 10px;
text-align: center;
font-weight: 600;
}
input[type="text"] {
width: 100%;
padding: 12px 16px;
font-size: 16px;
border: 2px solid #ffc0cb;
border-radius: 10px;
outline: none;
transition: all 0.3s ease;
}
input[type="text"]:focus {
border-color: #d63384;
box-shadow: 0 0 0 3px rgba(214, 51, 132, 0.1);
}
ul {
width: 100%;
list-style: none;
display: flex;
flex-direction: column;
gap: 12px;
}
li {
display: flex;
align-items: center;
gap: 12px;
padding: 12px;
background-color: #fff5f7;
border-radius: 10px;
transition: all 0.3s ease;
}
li:hover {
background-color: #ffeef2;
transform: translateX(5px);
}
input[type="checkbox"] {
width: 20px;
height: 20px;
cursor: pointer;
accent-color: #d63384;
}
span {
flex: 1;
font-size: 16px;
color: #333;
transition: all 0.3s ease;
}
.done {
color: #999;
text-decoration: line-through;
opacity: 0.6;
}
div[v-else] {
text-align: center;
color: #999;
font-size: 16px;
padding: 20px;
}
div:last-child {
width: 100%;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
gap: 10px;
padding-top: 20px;
border-top: 2px solid #ffeef2;
font-size: 16px;
color: #666;
}
div:last-child input[type="checkbox"] {
margin-right: 5px;
}
</style>
🔍 四、代码详解:Vue 黑科技大揭秘
接下来咱们拆拆看,这个项目里藏了哪些 Vue 的 "黑科技",让代码这么简洁又强大!
核心指令:Vue 的 "快捷键"
1. 双向数据绑定 v-model —— 数据和界面 "手拉手"
html
<input type="text" v-model="title">
v-model就像个 "传话筒":输入框里的内容变了,title数据会自动跟着变;反过来,title数据改了,输入框显示的内容也会立刻更新。比如你在输入框里敲 "今日计划",上面的<h2>{{ title }}</h2>会同步显示,完全不用写额外代码 —— 这就是双向绑定的快乐!
复选框也能用:
html
<input type="checkbox" v-model="todo.done">
勾选复选框,todo.done会变成true;todo.done设为true,复选框会自动勾选,简直天作之合~
2. 事件监听 v-on(缩写@)—— 给元素装 "感应器"
html
<input type="text" @keydown.enter="addTodo">
@是v-on:的缩写,相当于给元素装了个 "感应器"。这里的@keydown.enter表示:" 当用户在输入框按下回车键时,执行addTodo函数 "。
对比传统 JS 的addEventListener('keydown', ...),Vue 的写法直接把事件和处理函数绑在一起,可读性拉满!而且还能加修饰符(比如.enter只监听回车键),不用自己写if(event.key === 'Enter'),细节控狂喜🥳!
3. 条件渲染 v-if和v-else —— 界面的 "开关"
html
<ul v-if="todos.length">...</ul>
<div v-else>暂无计划</div>
这对组合就像个智能开关:当todos数组有内容(todos.length > 0),就显示<ul>列表;否则显示 "暂无计划"。完全不用手动display: none或者删除 DOM,Vue 会根据数据自动切换,省心到想鼓掌!
4. 列表渲染 v-for —— 批量生产元素的 "复印机"
html
<li v-for="todo in todos" :key="todo.id">...</li>
v-for就像个复印机,能根据数组todos里的每个元素,复制出一个<li>。比如todos有 3 个任务,就会生成 3 个<li>,完全不用手动写循环拼字符串~
这里的:key是个小细节(:key是v-bind:key的缩写),它给每个元素一个唯一标识,让 Vue 能准确识别哪个元素变了,更新时更高效 —— 就像给每个学生发个学号,点名时不会认错人😉。
核心 API:Vue 的 "发动机"
1. 响应式 API ref —— 数据的 "超能力"
javascript
const title = ref("Todos任务清单");
const todos = ref([{ id:1, title:'打王者', done:false }]);
ref能让普通数据拥有 "响应式超能力"。注意哦,用ref创建的基本类型数据(比如字符串、数字),修改时要加.value(比如title.value = '新标题');数组或对象虽然也需要.value,但修改内部元素(比如 todos.value.push(...))时,Vue 能自动感知到。
有了ref,我们再也不用关心 "数据变了怎么更界面",专心处理数据逻辑就行 —— 这就是 Vue 的 "数据驱动" 精髓!
2. 计算属性 computed —— 会 "自动算账" 的工具人
javascript
// 统计已完成的任务数
const active = computed(() => {
return todos.value.filter(todo => todo.done).length;
});
// 全选功能
const allDone = computed({
get() { return todos.value.every(todo => todo.done); },
set(val) { todos.value.forEach(todo => todo.done = val); }
});
computed就像个 "自动计算器":它依赖的数据(比如todos)变了,它会自动重新计算结果,而且会缓存计算结果(不是每次用到都算),性能超好!
active用来统计已完成的任务数,依赖todos,所以todos里的任务状态变了,active会自动更新allDone更厉害,是个 "读写两用" 的计算属性:get判断是否全选,set在全选框变化时,批量更新所有任务的状态 —— 几行代码就实现了全选功能,简直不要太高效!
🎯 五、总结:Vue 让开发变成 "享受"
回顾一下,从传统 DOM 编程的 "手动搬砖",到 Vue 的 "数据驱动躺平",Todos 任务清单的开发难度直接降了好几个 level:
- 不用再写一堆
getElementById、addEventListener,专注数据逻辑就行 - 响应式数据让界面自动更新,省了 N 行 DOM 操作代码
- 指令(v-model、v-for 等)和 API(ref、computed)就像现成的 "零件",拼一拼就出功能
其实 Vue 的魅力远不止这些,它就像个贴心的助手,把复杂的 DOM 操作藏在底层,让我们能以更直观、更高效的方式写代码。下次再做项目,试试用 Vue 的思路想想 —— 也许你会发现,前端开发原来可以这么快乐😊!
最后送大家一句话:别和 DOM 死磕,让数据替你说话,Vue 会帮你搞定剩下的一切~ 下次见!👋