Vue 3 响应式任务清单应用详解:从基础到高级实践(含完整注释与知识点解读)📝

131 阅读12分钟

📝在现代前端开发中,Vue.js 凭借其简洁的语法、强大的响应式系统和灵活的组合式 API,成为构建用户界面的首选框架之一。本文将围绕一个典型的 Todos 任务清单 应用,深入剖析 Vue 3 的核心概念与最佳实践,并结合源码中的详尽中文注释,逐层揭示其设计思想与实现细节。

作者有话说:Vue3-todos 说它想插队,所以我发了~

点赞、评论、收藏支持一波Vue3-todos吧~


零、🛠️ 项目初始化——从零开始搭建 Vue 3 + Vite 项目

要运行本文中的 Todos 应用,首先需要创建一个 Vue 3 项目。推荐使用官方脚手架工具 Vite,它具有极快的启动速度和现代化的开发体验。

1. 使用 npm init vite 创建项目

bash

npm init vite

或使用:

bash
编辑
npx create-vite

这将启动一个交互式引导流程,帮助你选择框架和语言:

text
编辑
Project name: vue3-todos
Select a framework: Vue
Select a variant: JavaScript
Use rollup-vite (Experimental)? No
Install with npm and start now? Yes

2. 进入项目并安装依赖

项目创建完成后,进入目录并安装依赖:

bash
编辑
cd vue3-todos
npm install

3. 启动开发服务器

bash
编辑
npm run dev

浏览器会自动打开 http://localhost:5173,即可看到你的Vue 项目!

image.png

Vue3 官网+开发文档


一、👀 效果预览 —— 让我们先去 Vue3-todos 先瞅一瞅

f2347708-5e04-4d6d-a5cc-17a7f000ac89.png

🔗你也可以通过以下链接查看:

👉 Todos 任务清单 Demo 预览


二、🧠 核心思想:数据驱动视图

传统开发中,我们常常需要手动查找 DOM 元素、监听事件、再更新内容——这种方式不仅繁琐,还容易引发状态不一致的问题。而现代响应式框架的核心理念是:

“你只需关心数据如何变化,界面会自动随之更新。”

这一思想在代码注释中被清晰表达:

“不再需要思考页面的元素怎么操作,而是要思考数据是怎么变化的。”
“聚焦的是数据业务逻辑,而不是页面元素操作。”

换句话说,开发者只需维护一份“真实状态”(如任务列表、输入内容),框架会自动将这份状态映射到界面上。这种声明式编程范式,极大提升了开发效率与代码可维护性。

在没有现代前端框架的时代,开发者必须通过原生 JavaScript 直接操作 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; // 手动更新 DOM
  });
</script>

这种方式存在明显缺陷:

  • 命令式逻辑:每一步都要明确“做什么”(找元素 → 监听事件 → 修改内容)。
  • 性能瓶颈:频繁操作真实 DOM 会触发浏览器重排重绘,效率低下。
  • 可维护性差:当功能复杂(如增删改查、筛选、统计)时,代码迅速变得混乱。
  • 状态管理困难:数据分散在 DOM 和变量中,难以统一追踪。

本质上,这种开发模式是 “以 DOM 为中心” 的,开发者需要时刻关注页面结构如何变化。

Vue 彻底改变了这一范式。它的核心哲学是:

“你不需要关心页面如何更新,只需要描述数据应该是什么样子。”

在 Vue 中,视图是数据的映射。只要数据变了,框架会自动、高效地更新对应的 DOM。开发者只需专注于 业务逻辑和数据流,而无需手动操作元素。

这种 “以数据为中心” 的声明式编程,不仅提升了开发效率,也大幅降低了出错概率。尤其对初学者而言,Vue 能让他们写出结构清晰、逻辑严谨的代码,避免陷入 DOM 操作的泥潭。


三、🎯 应用功能概览

该任务清单应用实现了以下典型功能:

  • 动态显示当前输入标题
  • 回车提交新任务(支持空值过滤)
  • 渲染可勾选的任务列表
  • 支持全选/取消全选
  • 实时统计未完成任务数量
  • 空状态友好提示
  • 视觉反馈(已完成任务置灰并加删除线)
  • 响应式布局与美观背景

所有这些功能,均通过响应式数据流与声明式模板实现,无任何直接 DOM 操作。


四、🔌 响应式数据:ref

// 响应式数据
const title = ref("")
const todos = ref([ /* ... */ ])

✅ 知识点详解:

  • ref 是创建响应式引用的基础工具。
  • 注释明确指出这是 “响应式数据”,意味着对它的修改会自动触发视图更新。
  • 对于字符串、数字等基本类型,必须使用 ref 才能获得响应性。
  • <script setup> 中,内部通过 .value 访问;模板中则自动解包,可直接使用变量名。
  • 数组或对象包裹在 ref 中后,其内部属性依然保持响应性,得益于 Vue 底层的 Proxy 机制。

💡 注释还强调: “业务式页面上要动态展示标题”
这说明变量不仅是存储容器,更是业务状态的一部分。

ref 用于包裹基本类型(如字符串、数字)或对象。
<script setup> 中,ref 创建的变量在模板中可直接使用(无需 .value),但在逻辑中访问/修改需通过 .value
例如:title.value = '' 清空输入内容,Vue 会自动更新绑定的输入框。


五、✍️ 双向绑定:v-model

<!-- 双向数据绑定 -->
<input type="text" v-model="title" @keydown.enter="addTodo">

✅ 知识点详解:

  • v-model 是 Vue 提供的语法糖,用于在表单控件上建立双向数据绑定
  • 注释解释其优势:

    “不用addEventListener 事件绑定”

  • 它替代了传统的 input.addEventListener('input', handler) 手动监听方式。
  • 在 Vue 3 中,v-model 默认监听 input 事件,并智能处理中文输入法(在 composition 结束后才更新),避免中间状态干扰。

六、⌨️ 事件处理:@keydown.enter

<!-- @event-name:enter 监听键盘输入,当按下回车的时候 -->
<input type="text" v-model="title" @keydown.enter="addTodo">

✅ 知识点详解:

  • @v-on: 的缩写,用于绑定事件监听器。
  • 注释精准描述了其作用:

    “@event-name:enter 监听键盘输入,当按下回车的时候”

  • .enter键盘事件修饰符,仅在用户按下 Enter 键时触发回调函数。
  • 这种设计提升了交互体验,无需额外按钮即可快速添加任务。

这是 Vue 对 addEventListener 的语法糖,更简洁直观。


七、➕ 新增任务:业务逻辑聚焦

// 新增任务
const addTodo = () => {
  // focus 数据业务
  // 检查标题是否为空或只包含空格
  if(!title.value.trim()) return;
  todos.value.push({ id: Math.random(), title: title.value, done: false });
  title.value = ''; // 清空输入框
}

✅ 知识点详解:

  • 注释强调:

    “focus 数据业务” 表明该函数的核心是维护数据状态,而非操作 DOM。

  • 使用 trim() 过滤纯空格输入,防止无效任务入库。
  • 虽然 Math.random() 作为 ID 在生产环境中不够严谨,但在演示场景中足够轻量。
  • 清空 title.value 后,因 v-model 的双向绑定特性,输入框自动清空——再次体现“改数据即改视图”

在更规范的实践中,可使用 Date.now() 作为简易 ID 生成方案,例如:

id: Date.now()

这比 Math.random() 更具时间顺序性和唯一性保障。


八、📋 列表渲染:v-for 与唯一键

<!-- 任务列表 -->
<!-- v-if 条件渲染 满足条件渲染 不满足条件不渲染 -->
<ul v-if="todos.length">
  <!-- key 唯一属性 -->
  <li v-for="todo in todos" :key="todo.id">

✅ 知识点详解:

  • 注释明确指出:

    “key 唯一属性”

  • v-for 必须配合唯一 key,以帮助框架高效识别节点身份,实现最小化 DOM 更新。
  • 若使用数组索引作为 key,在列表发生插入、删除等操作时,可能导致组件状态错乱(如复选框选中状态错位)。
  • 注释还说明:

    “v-if 条件渲染 满足条件渲染 不满足条件不渲染” 强调 v-if 是“真·条件渲染”——当条件为假时,元素不会存在于 DOM 中


九、✅ 任务完成状态:嵌套响应式绑定

<input type="checkbox" v-model="todo.done">

✅ 知识点详解:

  • 每个复选框直接绑定到对应任务的 done 属性。
  • 由于整个 todos 数组是响应式的,修改任一任务的 done 状态会立即触发局部 UI 更新。
  • 这体现了 Vue 的细粒度响应式更新能力——只重绘变化的部分,而非整个列表。

十、🎨 动态样式::class 与语义化表达

<!-- v-bind:缩写 js 表达式
vue 有一定的学习 api 对用户非常友好 好上手 -->
<span :class="{'done': todo.done}">{{ todo.title}}</span>

✅ 知识点详解:

  • 注释解释了语法:

    “v-bind:缩写 js 表达式”:classv-bind:class 的简写形式。

  • 使用对象语法 { 'done': todo.done },当任务完成时自动添加 done 类。
  • 注释还评价:

    “vue 有一定的学习 api 对用户非常友好 好上手” 说明框架在降低学习成本上的用心设计。

  • 对应的 CSS 规则:
    /* 已完成任务样式 */
    .done {
      color: gray;
      text-decoration: line-through;
    }
    
    通过视觉反馈直观传达任务状态。

配合 CSS 可实现更友好的用户体验:

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

十一、❌ 条件渲染:空状态处理

<ul v-if="todos.length"> ... </ul>
<!-- v-else 没有v-if 渲染 -->
<div v-else>
  暂无计划
</div>

✅ 知识点详解:

  • 注释说明:

    “v-else 没有v-if 渲染” 即当任务列表为空时,显示友好的提示信息。

  • v-else 必须紧跟在 v-ifv-else-if 之后,否则无效。
  • 这种模式提供了良好的用户体验,避免空白界面带来的困惑。

空状态提示可进一步优化文案,例如:

📝 暂无计划,快添加你的第一个任务吧!


十二、📊 计算属性:性能与语义的统一

1. 未完成任务数量:active

// 依赖于todos 响应式数据的 计算属性
// 形式上是函数(计算过程),结果(计算属性)返回
// 也是响应式的 依赖于todos
// computed 缓存 性能优化 只有 todos 变化时才会重新计算
const active = computed(() => {
  return todos.value.filter(todo => !todo.done).length
})

✅ 知识点详解:

  • 注释完整阐述了 computed 的核心价值:

    “依赖于todos 响应式数据的 计算属性”
    “形式上是函数(计算过程),结果(计算属性)返回”
    “也是响应式的 依赖于todos”
    “computed 缓存 性能优化 只有 todos 变化时才会重新计算”

  • 相比在模板中直接写 {{ todos.filter(...).length }},使用 computed 避免了每次渲染都重复执行过滤逻辑。
  • 它具有缓存机制:只要 todos 未变,多次访问 active 不会重新计算。
  • 同时,命名 active 使代码更具语义性,提升可读性。

computed 是“计算属性”,本质是一个带缓存的函数。
优势:只有当依赖的数据(如 todos)发生变化时,才会重新计算。避免不必要的重复运算。
在本例中,active 实时反映未完成任务数,但不会在每次渲染时都执行 filter

2. 全选控制:可写计算属性

// computed 高级技巧
// get set 属性的概念
const allDone = computed({
  get(){
    return todos.value.every(todo => todo.done)
  },
  set(val){
    todos.value.forEach(todo => todo.done = val)
  }
})

✅ 知识点详解:

  • 注释点明这是:

    “computed 高级技巧”
    “get set 属性的概念”

  • get 函数判断是否所有任务都已完成(使用 Array.every)。
  • set 函数接收布尔值 val,批量设置所有任务的完成状态。
  • 将其绑定到复选框:
    <input type="checkbox" v-model="allDone">
    
    即可实现双向同步:点击全选 → 所有任务完成;取消任一任务 → 全选自动取消。
  • 这种模式避免了额外的状态变量和监听逻辑,是处理聚合状态的经典方案。

更健壮的 get 实现应考虑空列表情况:

get() {
  return todos.value.length > 0 && todos.value.every(todo => todo.done);
}

避免在无任务时误判为“全选”。


十三、🎨 样式与布局:组件级封装与视觉优化

<style scoped>
  /* 添加页面背景 - 仅应用于根容器 */
  .app-container {
    /* 使用 assets 目录下的图片 作为背景图片 */
    background-image: url('./assets/image5.png');
    background-size: cover;
    min-height: 100vh;
    /* ... */
  }

  /* 为内容添加半透明背景,提高可读性 */
  h2, input, ul, div:last-of-type {
    background-color: rgba(255, 255, 255, 0.8);
    /* ... */
  }
</style>

✅ 知识点详解:

  • <style scoped>:确保样式仅作用于当前组件,避免全局污染。这是组件化开发的重要保障。
  • 注释说明背景图用途:

    “使用 assets 目录下的图片 作为背景图片”
    “添加页面背景 - 仅应用于根容器”

  • background-size: cover 保证图片完整覆盖容器且不失真。
  • 半透明遮罩注释:

    “为内容添加半透明背景,提高可读性” 这是 UI 设计中的常见技巧,尤其适用于复杂或深色背景。

  • min-height: 100vh 确保即使任务为空,页面也占满整个视口高度,提升视觉完整性。

通过简单的 CSS,我们让完成的任务呈现删除线效果,界面更加直观友好。scoped 属性确保样式仅作用于当前组件,避免全局污染。

可进一步增强底部统计区域的样式:

.stats {
  margin-top: 16px;
  font-size: 14px;
  color: #666;
}
input[type="text"] {
  padding: 8px;
  width: 100%;
  margin-bottom: 12px;
  border: 1px solid #ddd;
  border-radius: 4px;
}

十四、🧠 开发范式演进:从选项式到组合式

// setup vue3 composition 组合式 API 
// vue2 options API
import {ref, computed} from 'vue'

✅ 知识点详解:

  • 注释对比了两种范式:

    “setup vue3 composition 组合式 API”
    “vue2 options API”

  • 组合式 API(Composition API) 允许按逻辑关注点组织代码。例如,所有与“任务列表”相关的数据、计算属性、方法可以放在一起,而非分散在 datacomputedmethods 等选项中。
  • <script setup> 是 Vue 3 的编译时语法糖,进一步减少样板代码,提升开发体验。
  • 这种组织方式更利于逻辑复用(通过自定义 Hook)和 TypeScript 类型推导。

为什么 Vue 特别适合初学者?

  • 低门槛上手:模板语法贴近 HTML,指令命名直观(v-if 就是“如果”),无需深入理解虚拟 DOM 或响应式原理即可开发。
  • 自动状态同步:告别 getElementByIdinnerHTML,数据即视图。
  • 组合式逻辑组织:Composition API 允许将相关逻辑(如“任务增删”、“全选控制”)集中编写,比 Options API 更灵活。
  • 内置最佳实践:如 key 提示、computed 缓存、事件修饰符等,引导开发者写出高性能代码。
  • 渐进式框架:可从简单组件开始,逐步引入路由、状态管理等高级功能。

十五、💻 完整实现代码

<template>
  <div class="app-container">
    <h1>Todos 任务清单</h1>
    <!-- 数据绑定 -->
    <h2>{{ title }}</h2>
    <!-- 双向数据绑定 -->
    <!-- @ v-bind: 缩写 不用addEventListener 事件绑定 -->
    <!-- @event-name:enter 监听键盘输入,当按下回车的时候 -->
    <input type="text" v-model="title" @keydown.enter="addTodo">
    <!-- 任务列表 -->
    <!-- v-if 条件渲染 满足条件渲染 不满足条件不渲染 -->
    <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>
    <!-- v-else 没有v-if 渲染 -->
    <div v-else>
      📝 暂无计划,快添加你的第一个任务吧!
    </div>
    
    <div class="stats">
      <!-- 全选复选框 -->
      全选 <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, onMounted } from 'vue'
  import {ref, computed} from 'vue'
  // 响应式数据
  const title = ref("")
  const todos = ref([
    {
      id: 1,
      title: '打游戏',
      done: false
    },
    {
      id: 2,
      title: '吃饭',
      done: false
    },
    {
      id: 3,
      title: '睡觉',
      done: false
    },
    {
      id: 4,
      title: '学习Vue',
      done: false
    }
  ])
  // 依赖于todos 响应式数据的 计算属性
  // 形式上是函数(计算过程),结果(计算属性)返回
  // 也是响应式的 依赖于todos
  // computed 缓存 性能优化 只有 todos 变化时才会重新计算
  const active = computed(() => {
    return todos.value.filter(todo => !todo.done).length
  })
  // 新增任务
  const addTodo = () => {
    // focus 数据业务
    // 检查标题是否为空或只包含空格
    if(!title.value.trim()) return;
    todos.value.push({
        id: Date.now(),
        title: title.value,
        done: false
      }); // 新增任务
    title.value = ''; // 清空输入框
  }
  // computed 高级技巧
  // get set 属性的概念
  const allDone = computed({
    get(){
      return todos.value.length > 0 && todos.value.every(todo => todo.done)
    },
    set(val){
      todos.value.forEach(todo => todo.done = val)
    }
  })
</script>
<style scoped>
    /* 添加页面背景 - 仅应用于根容器 */
    .app-container {
      /* 使用 assets 目录下的图片 作为背景图片 */
      background-image: url('./assets/image5.png');
      /* 背景图片设置 */
      background-size: cover; /* 图片覆盖整个容器 */
      background-repeat: no-repeat; /* 不重复 */
      background-position: center; /* 居中显示 */
      min-height: 100vh; /* 至少占满整个视口高度 */
      width: 100%; /* 确保占满整个视口宽度 */
      padding: 20px;
      box-sizing: border-box;
    }
  
  /* 为内容添加半透明背景,提高可读性 */
  h2, input, ul, .stats {
    background-color: rgba(255, 255, 255, 0.8);
    padding: 10px;
    border-radius: 5px;
    margin: 5px 0;
  }
  
  /* 已完成任务样式 */
  .done{
    color: #888;
    text-decoration: line-through;
  }

  .stats {
    margin-top: 16px;
    font-size: 14px;
    color: #666;
  }

  input[type="text"] {
    padding: 8px;
    width: 100%;
    margin-bottom: 12px;
    border: 1px solid #ddd;
    border-radius: 4px;
  }
</style>

十六、🚀 总结

这个看似简单的任务清单应用,实则完整展示了现代前端框架的核心能力:

  • 响应式系统 → “响应式数据”
  • 声明式渲染 → “v-if 条件渲染”、“v-bind:缩写”
  • 计算属性缓存 → “computed 缓存 性能优化”
  • 事件修饰符 → “@event-name:enter 监听键盘输入”
  • 组件级样式封装 → “仅应用于根容器”
  • 组合式逻辑组织 → “setup vue3 composition”

更重要的是,它始终贯彻一个原则:

“你只需聚焦数据如何变化,其余交给框架。”

正如注释所言:

“修改数据,余下的 DOM 更新 Vue 替我们做了。”

这正是响应式编程的魅力所在——让开发者从繁琐的界面同步中解放出来,专注于业务本身。

通过这个 Todos 示例,我们完成了从前端开发范式的根本转变:

  • 过去:思考“如何找到元素并修改它”。
  • 现在:思考“数据应该变成什么样子”。

Vue 的响应式系统就像一位智能助手,默默监听你的数据变化,并精准、高效地更新页面。你只需专注于业务规则:

“当用户按下回车,就把输入内容加入任务列表。”
“全选框的状态取决于所有任务是否完成。”

这种 声明式 + 响应式 的开发模式,不仅提升了生产力,也让代码更具可读性和可维护性。

对于初学者而言,掌握 Vue 的核心不是记住 API,而是建立 数据驱动视图 的思维方式。一旦理解这一点,无论是开发 Todos、购物车还是复杂后台系统,都能游刃有余。

记住:在 Vue 的世界里,数据是国王,视图只是它的影子。

动手实践这个 Todos 应用吧!它是通往现代前端开发的重要第一步。