导读:本期聚焦于上海GEO公司创作的《如何用Vue从零开发一个任务管理应用?详细步骤与常见问题解析》,敬请观看详情。想用Vue做一个任务管理应用,应该从哪里起步?本文梳理从项目初始化、数据结构设计到增删改查与本地持久化的完整实现路径。文章会给出可直接运行的组件代码,说明v-model双向绑定、事件处理、计算属性和watch监听在任务场景中的用法,并解释为什么刷新后数据不能直接用data初始化。同时归纳了唯一ID生成、空状态处理、localStorage异常捕获、日期字段比较等常见问题与注意事项。按照步骤操作即可得到一个小型单页任务工具,适合Vue初学者巩固基础并体会组件化开发思路。

任务管理应用是前端学习中的经典练手项目,它覆盖了列表渲染、表单输入、事件处理、条件显示和本地存储等高频知识点。本文使用 Vue 3 的 <script setup> 语法,从项目创建开始,逐步实现任务新增、编辑、删除、完成状态切换、筛选统计以及浏览器本地持久化。你不需要引入后端服务,数据完全保存在浏览器中,适合快速上手和巩固 Vue 基础。

如何用Vue从零开发一个任务管理应用?详细步骤与常见问题解析

一、项目初始化与任务数据结构

先用 Vite 创建项目。执行 npm create vite@latest task-manager -- --template vue 后进入目录安装依赖,再运行 npm run dev 即可看到默认页面。Vite 生成的结构中,我们主要修改 src/App.vue。清理默认样式后,可以在组件里声明一个响应式任务数组。任务对象建议至少包含 id、title、done 三个字段,如果后续需要排序或展示创建时间,可以再补充 createdAt。

id 的生成方式直接影响删除和编辑是否稳定。小项目里可以使用自增变量,每次添加任务时让 nextId 加一。如果数据需要跨刷新保存,id 不能依赖数组长度,因为删除中间项后长度会变化。也可以使用 crypto.randomUUID 生成更可靠的唯一值。下面先给出最基础的渲染结构,后续再逐步加入交互。

<script setup>
import { ref } from 'vue'

const tasks = ref([
  { id: 1, title: '学习Vue组件', done: false, createdAt: '2025-01-01 10:00:00' },
  { id: 2, title: '完成周报', done: true, createdAt: '2025-01-01 09:00:00' }
])
</script>

<template>
  <div class="task-app">
    <h1>任务管理</h1>
    <ul>
      <li v-for="task in tasks" :key="task.id">
        <input type="checkbox" :checked="task.done" disabled />
        <span>{{ task.title }}</span>
      </li>
    </ul>
  </div>
</template>

<style scoped>
.done {
  text-decoration: line-through;
  color: #999;
}
</style>

二、实现任务的增删改查与状态切换

新增任务的核心是 v-model 绑定输入框和 newTask 变量。提交动作可以绑在按钮的 click 事件上,也可以监听输入框的 keyup.enter,让用户按回车直接添加。提交前需要用 trim 去掉首尾空格,避免保存空任务。

编辑功能的思路是引入两个辅助变量:editingId 表示正在编辑的任务 id,draftTitle 保存编辑中的标题。双击任务文本时进入编辑态,输入框显示草稿内容,按回车保存,按 Esc 取消。保存时如果发现标题为空,可以直接删除该任务,这样交互更自然。

删除和完成状态相对简单。删除时用 filter 返回新数组,替换原 tasks,而不是直接修改下标,这样 Vue 的响应式更新更可靠。完成状态可以用 checkbox 直接 v-model="task.done",因为任务对象保存在响应式数组中,深度响应可以监听到 done 的变化。以下代码整合了这三个功能。

<script setup>
import { ref } from 'vue'

const tasks = ref([])
const newTask = ref('')
const editingId = ref(null)
const draftTitle = ref('')
let nextId = 1

function addTask() {
  const title = newTask.value.trim()
  if (!title) return
  tasks.value.push({
    id: nextId++,
    title,
    done: false,
    createdAt: new Date().toLocaleString()
  })
  newTask.value = ''
}

function startEdit(task) {
  editingId.value = task.id
  draftTitle.value = task.title
}

function saveEdit(id) {
  const title = draftTitle.value.trim()
  if (!title) {
    removeTask(id)
  } else {
    const task = tasks.value.find(item => item.id === id)
    if (task) task.title = title
  }
  editingId.value = null
  draftTitle.value = ''
}

function cancelEdit() {
  editingId.value = null
  draftTitle.value = ''
}

function removeTask(id) {
  tasks.value = tasks.value.filter(task => task.id !== id)
}
</script>

<template>
  <div class="task-app">
    <h2>任务管理</h2>
    <div class="task-form">
      <input v-model="newTask" @keyup.enter="addTask" placeholder="输入任务后回车" />
      <button @click="addTask">添加</button>
    </div>
    <ul>
      <li v-for="task in tasks" :key="task.id">
        <input type="checkbox" v-model="task.done" />
        <template v-if="editingId === task.id">
          <input v-model="draftTitle" @keyup.enter="saveEdit(task.id)" @keyup.esc="cancelEdit" />
          <button @click="saveEdit(task.id)">保存</button>
          <button @click="cancelEdit">取消</button>
        </template>
        <template v-else>
          <span :class="{ done: task.done }" @dblclick="startEdit(task)">{{ task.title }}</span>
          <button @click="removeTask(task.id)">删除</button>
        </template>
      </li>
    </ul>
  </div>
</template>

三、本地持久化与筛选统计

刷新页面后,仅存在内存里的 tasks 会丢失。解决办法是使用 watch 深度监听 tasks 数组,数据变化时把数组序列化后写入 localStorage。初始化时从 localStorage 读取,并用 try/catch 包住 JSON.parse,避免数据损坏导致应用无法启动。

筛选功能不建议在模板里直接使用方法处理数组,因为每次渲染都会重新执行。更合适的做法是使用计算属性 computed,根据当前筛选条件返回全新数组。同时可以用另一个计算属性统计未完成任务数量。

给筛选按钮绑定 filter 值,再让列表遍历计算属性返回的 filteredTasks,就能实现全部、进行中、已完成三种视图。代码如下。

import { ref, watch, computed } from 'vue'

const STORAGE_KEY = 'task-manager-data'
const tasks = ref(loadTasks())
const filter = ref('all')

function loadTasks() {
  try {
    const raw = localStorage.getItem(STORAGE_KEY)
    return raw ? JSON.parse(raw) : []
  } catch (error) {
    return []
  }
}

watch(tasks, (value) => {
  localStorage.setItem(STORAGE_KEY, JSON.stringify(value))
}, { deep: true })

const filteredTasks = computed(() => {
  if (filter.value === 'active') {
    return tasks.value.filter(task => !task.done)
  }
  if (filter.value === 'done') {
    return tasks.value.filter(task => task.done)
  }
  return tasks.value
})

const remaining = computed(() => tasks.value.filter(task => !task.done).length)
<template>
  <div class="filters">
    <button @click="filter = 'all'">全部</button>
    <button @click="filter = 'active'">进行中</button>
    <button @click="filter = 'done'">已完成</button>
  </div>
  <p>剩余 {{ remaining }} 项任务</p>
  <ul>
    <li v-for="task in filteredTasks" :key="task.id">
      <input type="checkbox" v-model="task.done" />
      <span :class="{ done: task.done }" @dblclick="startEdit(task)">{{ task.title }}</span>
      <button @click="removeTask(task.id)">删除</button>
    </li>
  </ul>
</template>

四、常见问题与注意事项

这个项目虽然结构简单,但有几个容易忽视的细节。首先是 id 冲突,如果使用 Date.now 生成 id,快速连续添加两个任务时可能出现相同值,导致 key 重复和状态错乱。建议使用自增变量或 crypto.randomUUID。其次是 localStorage 在某些隐私模式下可能被禁用,写入前要判断是否可用并捕获异常。

还要注意空状态提示。任务列表为空时应该显示占位内容,否则用户会误以为渲染失败。日期字段如果要用于排序,建议存储时间戳而不是格式化字符串,因为字符串比较在日期格式不一致时会出错。最后,使用 v-for 时不要用 index 作为 key,删除或筛选后 index 变化会导致输入状态和 DOM 复用异常。

完成这些基础功能后,可以继续扩展任务分类、截止日期提醒、拖拽排序等功能。若状态逻辑继续增加,再考虑引入 Pinia 做集中管理。先把当前单页版本跑通,对理解 Vue 的响应式数据和组件化开发很有帮助。

Vue任务管理任务管理应用Vue实战项目修改时间:2026-09-22 20:12:07

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/0922/60611.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。