深入 Vue 3 响应式开发:从零构建一个功能完整的 Todo 应用(超详细实战解析)

66 阅读7分钟

深入 Vue 3 响应式开发:从零构建一个功能完整的 Todo 应用(超详细实战解析)

在当今前端开发领域,Vue.js 凭借其简洁的语法、强大的响应式系统和高效的开发体验,赢得了全球数百万开发者的青睐。尤其自 Vue 3 引入 Composition API 后,代码组织更灵活、逻辑复用更便捷,使得复杂应用的开发变得前所未有的清晰与高效。

本文将以一个完整、可运行的 Vue 3 Todo 任务清单应用为案例,带你从零开始,逐行、逐概念、深入底层地理解 Vue 的核心思想——“数据驱动视图”。我们将不仅解释“怎么写”,更要讲清楚“为什么这样写”、“背后发生了什么”以及“它比传统方式好在哪里”。无论你是刚入门的新手,还是希望巩固 Vue 原理的进阶者,这篇文章都将为你提供一次沉浸式的深度学习体验。


一、项目概览:我们要构建什么?

这个 Todo 应用具备以下功能:

  • 动态标题:顶部显示标题(如“Todos任务清单”),用户可直接编辑
  • 任务添加:在输入框中输入内容,按下回车键即可新增一条任务
  • 任务状态管理:每条任务左侧有复选框,点击可标记为“已完成”
  • 视觉反馈:已完成的任务文字变为灰色并带有删除线
  • 智能计数:底部实时显示“未完成任务数 / 总任务数”
  • 全选/全不选:一个复选框即可一键切换所有任务的状态
  • 空状态提示:当任务列表为空时,显示“暂无计划”

这些功能看似简单,但在传统 JavaScript 中,需要大量繁琐的 DOM 操作、事件监听和状态同步。而在 Vue 中,你只需关注数据本身,框架会自动处理所有 UI 更新。


二、核心哲学:Vue 的“数据驱动”思想

传统开发 vs Vue 开发

在没有框架的时代,开发者必须手动操作 DOM:

// 找到元素
const titleEl = document.getElementById('title');
// 修改内容
titleEl.innerText = newTitle;
// 监听输入
input.addEventListener('input', (e) => {
  // 手动同步数据
  currentTitle = e.target.value;
  // 手动更新其他依赖
  updateCount();
});

这种方式存在三大痛点:

  1. 代码冗余:每个交互都需要重复的查找 + 修改
  2. 状态不同步:容易遗漏更新,导致界面与数据不一致
  3. 难以维护:逻辑分散,修改一处可能影响多处

而 Vue 的做法截然不同:

“你描述世界应该是什么状态,我负责让页面变成那样。”

你不再写“如何改页面”,而是写“数据现在是什么”。例如:

  • 想让标题变?→ 改 title 的值
  • 想加新任务?→ 往 todos 数组里 push 一个对象
  • 想标记完成?→ 设 todo.done = true

Vue 会自动追踪这些变化,并精准更新所有受影响的 UI 部分。正如你在注释中所写:“vue focus 数据业务,修改这个数据,余下的 dom 更新 vue 替我们做了。”——这正是 Vue 的灵魂所在。


三、模板层详解:声明式 UI 的力量

1. 数据绑定:{{ title }}

<h2>{{ title }}</h2>

这是 Vue 最基础的插值语法。它告诉 Vue:“在这里显示 title 的当前值”。当 title 是一个响应式变量(通过 ref 创建)时,任何对其 .value 的修改都会触发 <h2> 内容的自动更新。无需 innerHTML,无需 innerText,一切自动发生。

2. 双向绑定与事件处理

<input type="text" v-model="title" @keydown.enter="addTodo">
  • v-model="title" 是 Vue 的双向绑定指令,等价于:

    :value="title" @input="title = $event.target.value"
    

    它同时完成了“数据 → 视图”和“视图 → 数据”的同步。

  • @keydown.enter="addTodo" 中:

    • @ 是 v-on: 的缩写,用于监听 DOM 事件
    • .enter 是事件修饰符,表示“仅在按下回车键时触发”
    • addTodo 是定义在 <script setup> 中的函数

这种写法彻底告别了 addEventListener,让事件处理变得声明式且集中。

3. 条件渲染与列表循环

<ul v-if="todos.length">
  <li v-for="todo in todos" :key="todo.id"> ... </li>
</ul>
<div v-else>暂无计划</div>
  • v-if / v-else 实现条件渲染:根据 todos.length 是否大于 0 决定显示列表还是提示信息。

  • v-for 是列表渲染指令,遍历 todos 数组,为每个元素生成一个 <li>

  • :key="todo.id" 至关重要:

    • key 是 Vue 识别每个节点的“唯一身份证”
    • 当列表发生变化(如删除中间项),Vue 能通过 key 精准判断哪些节点需要移动、更新或销毁
    • 若不使用 key 或使用数组索引,可能导致状态错乱或性能下降

4. 动态样式与状态联动

<input type="checkbox" v-model="todo.done">
<span :class="{'done': todo.done}">{{ todo.title }}</span>
  • 复选框通过 v-model 与 todo.done 双向绑定,点击即改变数据状态

  • :class 是 v-bind:class 的缩写,用于动态绑定 CSS 类

    • {'done': todo.done} 表示:当 todo.done 为 true 时,添加 done 类
  • 在 <style> 中:

    .done {
      color: gray;
      text-decoration: line-through;
    }
    

    浏览器自动应用样式,实现视觉反馈

整个过程无需一行 JavaScript 操作样式,完全由数据驱动。

5. 底部状态栏:计算属性的完美舞台

全选 <input type="checkbox" v-model="allDone">
{{ active }} / {{ todos.length }}

这里引入了两个关键概念:

  • allDone:一个可写的计算属性,实现全选逻辑
  • active:一个只读计算属性,表示未完成任务数量

它们共同展示了 Vue 如何将复杂逻辑封装为简洁的模板表达式。


四、逻辑层剖析:Composition API 的优雅实践

1. 响应式数据:ref

const title = ref("Todos任务清单");
const todos = ref([ ... ]);
  • ref() 创建一个响应式引用对象,其值通过 .value 访问
  • Vue 3 使用 Proxy 对 ref 进行拦截,实现依赖收集派发更新
  • 当模板中使用 {{ title }} 时,Vue 自动建立依赖关系:一旦 title.value 改变,相关 DOM 就会更新

2. 计算属性:computed

只读计算属性:active
const active = computed(() => {
  return todos.value.filter(todo => !todo.done).length;
});
  • computed 返回一个记忆化(memoized)的响应式值

  • 优势

    • 性能优化:只要 todos 未变,多次访问 active 不会重复执行过滤操作
    • 自动响应:当 todos 中任一任务的 done 状态改变,active 自动重新计算
  • 在模板中直接使用 {{ active }},Vue 会自动解包 .value

可写计算属性:allDone
const allDone = computed({
  get() {
    return todos.value.length > 0 && todos.value.every(todo => todo.done);
  },
  set(val) {
    todos.value.forEach(todo => todo.done = val);
  }
});

这是 Vue 中实现“全选”功能的最佳实践

  • get:判断是否所有任务都已完成(注意处理空列表情况)
  • set:当用户点击“全选”复选框,将所有任务的 done 设为 val
  • 通过 v-model="allDone",一个复选框就实现了双向控制,代码极其简洁且健壮

3. 方法:addTodo

const addTodo = () => {
  if (!title.value.trim()) return;
  todos.value.push({
    id: Date.now(),
    title: title.value,
    done: false
  });
  title.value = '';
};
  • 核心思想:“focus 数据业务”——只操作数据,不关心 UI
  • 推入的是结构一致的对象(含 idtitledone),确保模板中 {{ todo.title }} 不会报错
  • 清空输入框,准备下一次输入

整个过程没有创建任何 DOM 元素,没有调用任何 appendChild,一切由 Vue 的响应式系统和虚拟 DOM 机制自动处理。


五、为什么 Vue 开发如此高效?

  1. 声明式编程
    你描述“UI 应该是什么样子”,而不是“如何一步步操作 DOM”,大幅降低认知负担。
  2. 响应式系统
    数据变化 → 自动更新视图,避免手动同步带来的 bug。
  3. 组合式 API
    逻辑按功能组织(如 addTodo, allDone),易于测试、复用和维护。
  4. 指令系统
    v-ifv-forv-model 等封装了常见模式,语义清晰,减少样板代码。
  5. 计算属性
    自动缓存 + 响应式,性能与开发体验兼得。

六、结语:从“操作 DOM”到“管理状态”

这段代码,不仅是一个功能完整的 Todo 应用,更是一份对 Vue 核心思想的完美诠释。你准确地抓住了现代前端开发的本质转变:

从前,我们操作 DOM;现在,我们管理状态。

掌握这一思想,你不仅能写出更健壮的 Vue 应用,也能更好地理解 React、Svelte 等其他响应式框架的设计哲学。继续深入 Vue 的世界吧——响应式、组件化、组合式 API……每一层都藏着让开发变得更简单、更快乐的魔法。

愿你在编程之路上,越走越远,越写越爽!🌟