ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Vue3极简入门:2小时构建待办事项应用,掌握Composition API与Pinia

2026/9/2 18:51:04 拓冰建站 浏览量
Vue3极简入门:2小时构建待办事项应用,掌握Composition API与Pinia 你是不是也遇到过这样的情况想快速上手 Vue3却被各种复杂的配置、概念和生态工具搞得晕头转向官方文档虽然全面但内容太多不知道从哪里开始网上教程要么太旧要么太散很难在短时间内构建起一个可用的知识框架。这篇文章就是为你准备的。我们不谈“生态”不谈“最佳实践”只聚焦一个核心目标用最短的时间让你能真正动手写出一个可运行的 Vue3 项目。我们称之为“极简版·即学即用”意味着我们将剔除所有非必要的干扰项只保留最核心、最必要的知识点和代码。读完本文你将能独立完成一个包含组件、状态管理和路由的简单应用并拿到一份可以直接运行的源码。1. 这篇文章真正要解决的问题对于初学者或从 Vue2 迁移的开发者来说Vue3 的学习曲线并不平缓。Composition API、script setup、ref、reactive、Vite... 这些新概念扑面而来。很多教程试图面面俱到结果反而让学习者迷失在细节中。本文要解决的核心痛点就是“快速建立最小可行知识体系”。我们不追求大而全而是通过一个具体的、可运行的“待办事项列表”项目串联起 Vue3 最关键的几个部分项目创建与环境搭建用最主流、最快的方式。核心语法与响应式理解ref和reactive的本质区别。组件化开发如何拆分、组合和传递数据。状态管理在小项目中何时需要以及如何使用 Pinia。路由导航如何实现页面切换。通过完成这个项目你不仅能看懂代码更能理解其背后的设计意图从而具备继续探索更复杂功能的能力。本文适合有一定 HTML、CSS、JavaScript 基础但 Vue3 经验为零或很少的开发者。2. 基础概念与核心原理在动手之前我们需要快速理解几个 Vue3 的核心概念这能让你写代码时心里有底。2.1 Composition API vs Options API这是 Vue3 最大的变化之一。Options API (Vue2 风格)按照data、methods、computed、watch等选项来组织代码。逻辑分散在各个选项中当组件复杂时关联的逻辑可能被割裂。Composition API (Vue3 主打)允许你使用导入的函数如ref,reactive来组织代码。相关逻辑可以集中在一个函数内大大提升了代码的可读性和可维护性尤其是在复杂组件中。本文全程使用 Composition API。2.2 响应式系统ref和reactiveVue3 的响应式核心是这两个函数。ref用于定义响应式的基本类型值如字符串、数字、布尔值。在模板中访问时Vue 会自动解包无需加.value在 JavaScript 中操作时必须通过.value属性。import { ref } from vue; const count ref(0); // 定义 console.log(count.value); // 访问0 count.value; // 修改reactive用于定义响应式的对象。返回一个对象的响应式代理修改其属性会自动触发更新。访问和修改都直接操作属性没有.value。import { reactive } from vue; const state reactive({ count: 0 }); // 定义 console.log(state.count); // 访问0 state.count; // 修改简单记忆基本类型用ref对象用reactive。ref也可以包装对象内部其实也是调用了reactive。2.3script setup语法糖这是单文件组件.vue文件中使用 Composition API 的编译时语法糖。它让代码极其简洁。所有顶层的导入、变量和函数都自动暴露给模板。无需再写setup()函数并返回一个对象。定义组件props和emit事件需要使用defineProps和defineEmits编译器宏。2.4 Vite新一代前端构建工具取代了 Vue CLI 和 Webpack 的复杂配置。核心优势是极速的启动和热更新利用原生 ES 模块。我们的项目将基于 Vite 创建。3. 环境准备与前置条件确保你的开发环境已经就绪。Node.jsVue3 和 Vite 都基于 Node.js。请安装Node.js 18或20的 LTS长期支持版本。前往 Node.js 官网 下载安装。 安装后打开终端Windows 用 CMD 或 PowerShellMac/Linux 用 Terminal验证安装node --version npm --version代码编辑器推荐使用Visual Studio Code (VS Code)并安装官方 Vue 扩展Volar。它提供了强大的语法高亮、智能提示和类型支持。包管理器可以使用npm随 Node.js 安装或yarn、pnpm。本文使用npm进行演示pnpm速度更快可根据喜好选择。4. 核心流程拆解创建一个待办事项应用我们将创建一个具有以下功能的极简待办事项应用显示待办事项列表。添加新的待办事项。标记待办事项为完成/未完成。删除待办事项。使用 Pinia 管理全局状态演示用。使用 Vue Router 实现一个“关于”页面演示用。4.1 第一步使用 Vite 创建 Vue3 项目打开终端进入你希望创建项目的目录执行以下命令npm create vuelatest这个命令会启动一个交互式的项目创建向导。按照提示进行选择项目名称输入vue3-todo-demo或你喜欢的名字。是否添加 TypeScript选择 No本文为简化使用 JavaScript。是否添加 JSX 支持选择 No。是否添加 Vue Router选择 Yes我们需要它。是否添加 Pinia选择 Yes我们需要它。是否添加测试工具选择 No。是否添加 ESLint选择 No为简化先跳过代码检查。创建完成后进入项目目录并安装依赖cd vue3-todo-demo npm install4.2 第二步项目结构概览使用 VS Code 打开项目文件夹你会看到类似以下结构vue3-todo-demo/ ├── node_modules/ ├── public/ ├── src/ │ ├── assets/ │ ├── components/ │ ├── router/ │ ├── stores/ │ ├── views/ │ ├── App.vue │ └── main.js ├── index.html ├── package.json └── vite.config.jssrc/main.js应用入口文件。src/App.vue根组件。src/components/存放可复用的子组件。src/views/存放页面级组件由路由控制。src/router/index.jsVue Router 的配置。src/stores/Pinia 状态存储的配置。4.3 第三步创建 Pinia Store 管理待办事项状态在src/stores/目录下新建一个文件todo.js// src/stores/todo.js import { ref, computed } from vue import { defineStore } from pinia // 使用 defineStore 定义 store第一个参数是 store 的唯一 ID export const useTodoStore defineStore(todo, () { // 状态待办事项列表使用 ref 包装数组 const todos ref([ { id: 1, text: 学习 Vue3 基础, done: true }, { id: 2, text: 完成这个待办事项项目, done: false }, { id: 3, text: 阅读 Pinia 官方文档, done: false } ]) // 获取器计算未完成的任务数量类似于 Vuex 的 getters const unfinishedTodos computed(() { return todos.value.filter(todo !todo.done).length }) // 操作添加待办事项 function addTodo(text) { if (!text.trim()) return todos.value.push({ id: Date.now(), // 简单用时间戳作为 ID text: text.trim(), done: false }) } // 操作切换待办事项完成状态 function toggleTodo(id) { const todo todos.value.find(t t.id id) if (todo) { todo.done !todo.done } } // 操作删除待办事项 function deleteTodo(id) { const index todos.value.findIndex(t t.id id) if (index -1) { todos.value.splice(index, 1) } } // 返回所有需要在组件中使用的状态和函数 return { todos, unfinishedTodos, addTodo, toggleTodo, deleteTodo } })4.4 第四步创建核心组件4.4.1 创建待办事项列表组件在src/components/目录下新建TodoList.vue!-- src/components/TodoList.vue -- template div classtodo-list h2待办事项 (剩余{{ store.unfinishedTodos }})/h2 ul li v-fortodo in store.todos :keytodo.id :class{ done: todo.done } input typecheckbox :checkedtodo.done changestore.toggleTodo(todo.id) / span{{ todo.text }}/span button clickstore.deleteTodo(todo.id)删除/button /li /ul /div /template script setup // 导入并使用我们定义的 store import { useTodoStore } from /stores/todo const store useTodoStore() /script style scoped .todo-list { margin: 20px 0; } ul { list-style: none; padding: 0; } li { padding: 10px; border-bottom: 1px solid #eee; display: flex; align-items: center; } li.done span { text-decoration: line-through; color: #888; } input[typecheckbox] { margin-right: 10px; } button { margin-left: auto; background-color: #ff6b6b; color: white; border: none; padding: 5px 10px; border-radius: 3px; cursor: pointer; } /style4.4.2 创建添加待办事项的组件在src/components/目录下新建AddTodo.vue!-- src/components/AddTodo.vue -- template div classadd-todo input typetext v-modelnewTodoText keyup.enterhandleAddTodo placeholder输入新任务按回车添加 / button clickhandleAddTodo添加/button /div /template script setup import { ref } from vue import { useTodoStore } from /stores/todo const store useTodoStore() const newTodoText ref() // 使用 ref 管理输入框的值 function handleAddTodo() { store.addTodo(newTodoText.value) newTodoText.value // 清空输入框 } /script style scoped .add-todo { display: flex; margin-bottom: 20px; } input { flex-grow: 1; padding: 10px; border: 1px solid #ccc; border-radius: 4px; margin-right: 10px; } button { padding: 10px 20px; background-color: #42b883; /* Vue 主题色 */ color: white; border: none; border-radius: 4px; cursor: pointer; } /style4.5 第五步修改主页面和路由4.5.1 修改主页视图打开src/views/HomeView.vue将其内容替换为我们的应用主界面!-- src/views/HomeView.vue -- template div classhome header h1Vue3 极简待办事项/h1 p一个使用 Composition API, Pinia, Vue Router 构建的示例/p /header main AddTodo / TodoList / /main footer router-link to/about关于这个项目/router-link /footer /div /template script setup // 导入组件 import AddTodo from /components/AddTodo.vue import TodoList from /components/TodoList.vue /script style scoped .home { max-width: 600px; margin: 0 auto; padding: 20px; font-family: sans-serif; } header { text-align: center; margin-bottom: 30px; } footer { margin-top: 40px; text-align: center; } a { color: #42b883; text-decoration: none; } /style4.5.2 修改关于页面打开src/views/AboutView.vue可以简单修改一下!-- src/views/AboutView.vue -- template div classabout h1关于这个示例项目/h1 p这是一个为了快速学习 Vue3 核心概念而构建的极简待办事项应用。/p p它涵盖了/p ul liVite 项目搭建/li liComposition API 与 script setup/li li响应式数据 (ref, reactive)/li li组件化开发/li liPinia 状态管理/li liVue Router 路由/li /ul router-link to/返回首页/router-link /div /template4.5.3 修改根组件检查src/App.vue确保它使用了router-view!-- src/App.vue -- template div idapp router-view / /div /template4.6 第六步运行项目在终端中确保位于项目根目录运行开发服务器npm run devVite 会启动开发服务器。通常它会输出一个本地地址如http://localhost:5173。在浏览器中打开这个地址。5. 运行结果与效果验证成功运行后你将在浏览器中看到一个标题为“Vue3 极简待办事项”的页面。一个输入框和“添加”按钮。一个初始的待办事项列表包含三条任务其中一条已勾选。列表下方显示剩余未完成任务的数量。页面底部有一个“关于这个项目”的链接。验证功能添加在输入框输入文字按回车或点击“添加”按钮新任务应出现在列表底部。切换状态点击任务前的复选框任务文本应出现/取消删除线同时“剩余”数量会相应变化。删除点击某个任务后的“删除”按钮该任务应从列表中消失。路由点击“关于这个项目”链接页面应跳转到关于页面展示项目简介。点击“返回首页”链接应回到待办事项主页。如果页面空白或功能异常请首先检查浏览器控制台F12是否有红色错误信息。最常见的错误是组件导入路径错误或store使用不当。6. 代码逻辑深度解析让我们回头看看几个关键点理解为什么这样写。6.1 Pinia Store 的妙用在todo.js中我们使用了defineStore。在 Composition API 风格下它像一个自定义的、可复用的 Composition 函数。todos状态被ref包装意味着任何对todos.value的修改如push,splice都会触发依赖它的组件更新。unfinishedTodos使用computed创建它会自动根据todos的变化重新计算。6.2 组件中的 Store 使用在TodoList.vue和AddTodo.vue中我们通过const store useTodoStore()来获取 store 实例。这个store是响应式的。在模板中直接使用store.todos当store.todos变化时模板会自动重新渲染。调用store.addTodo()等方法会修改 store 中的状态进而驱动所有使用该状态的组件更新。这就是状态管理的核心将状态从组件中抽离实现跨组件共享和逻辑复用。6.3script setup的简洁性注意在所有.vue文件中我们都使用了script setup。这意味着导入的组件如AddTodo可以直接在模板中使用无需components选项注册。定义的变量如newTodoText和函数如handleAddTodo自动暴露给模板。代码更紧凑逻辑更集中。7. 常见问题与排查思路问题现象可能原因排查方式解决方案运行npm run dev失败提示命令不存在未安装依赖或package.json中无dev脚本1. 检查是否在项目根目录。2. 运行npm install安装依赖。3. 检查package.json的scripts部分是否有dev: vite。确保在正确目录安装依赖。页面空白控制台报错Failed to resolve import组件导入路径错误检查报错信息指向哪个文件哪一行。确认/别名是否指向src目录Vite 默认配置。使用相对路径../或确认vite.config.js中别名配置。点击按钮或操作后页面无反应1. 事件绑定错误。2. 状态修改未触发响应式更新。1. 检查click等事件绑定是否正确指向函数名。2. 检查修改ref值时是否使用了.value。3. 在函数内添加console.log调试。1. 确保函数在script setup中正确定义。2. 操作ref必须使用.value。使用store时报undefined1. Store 未正确导入或定义。2. Pinia 未在 main.js 中安装。1. 检查导入路径/stores/todo是否正确。2. 检查src/main.js中是否使用了app.use(createPinia())。1. 确保 store 文件导出正确。2. 确保 main.js 中正确安装 Pinia。热更新HMR不工作浏览器缓存或编辑器问题1. 尝试手动刷新浏览器。2. 检查 VS Code 是否安装了 Volar 并禁用旧版 Vetur。3. 重启开发服务器。确保使用最新版 Volar并尝试禁用浏览器缓存。8. 最佳实践与工程建议基于这个极简项目当你开始正式开发时可以参考以下建议组件设计单一职责每个组件只做一件事。如AddTodo只负责添加TodoList只负责展示和操作列表。Props 向下Events 向上父组件通过props向子组件传递数据子组件通过$emit事件通知父组件内部变化。本例中因为使用了 Pinia跨组件通信通过 Store 完成简化了流程。使用defineProps和defineEmits在script setup中使用它们来明确组件的接口有利于类型检查和代码可读性。状态管理何时用 Pinia当状态需要在多个不相关的组件间共享或者逻辑复杂需要抽离时。对于简单的父子组件通信用props/emit或provide/inject即可。结构化 Store像我们示例一样将状态、计算属性和操作集中在一个 Store 文件中逻辑清晰。样式管理Scoped CSS在组件style标签上添加scoped属性可以确保样式只应用于当前组件避免全局污染。CSS 预处理器对于复杂项目可以考虑使用Sass或LessVite 内置支持。项目结构随着项目变大可以按功能模块组织components例如components/todo/、components/user/。将常量、工具函数、API 请求封装到单独的utils、constants、api目录中。性能与安全Key 属性在v-for循环中始终提供唯一的:key帮助 Vue 高效更新 DOM。避免直接修改 Props如果需要修改应该在子组件内部定义一个局部变量或触发事件让父组件修改。表单输入处理对于复杂表单考虑使用v-model修饰符如.lazy,.number或库如VeeValidate进行验证。9. 总结与后续学习方向通过这个“2小时速成”项目你已经亲手实践了 Vue3 最核心的链条Vite 搭建 - Composition API 写逻辑 - Pinia 管状态 - 组件化构建界面 - Router 做导航。你得到的不是一堆孤立的概念而是一个可以运行、可以修改、可以扩展的完整应用。本文的核心价值在于“最小化可行路径”。我们刻意避开了 TypeScript、深度响应式原理、Render 函数、自定义指令、Teleport、Suspense 等高级主题也简化了样式和错误处理。这不是因为它们不重要而是因为在入门阶段先跑起来获得正反馈比追求完美更重要。接下来你可以做什么修改和扩展尝试给待办事项添加“优先级”、“截止日期”字段并实现筛选和排序功能。探索官方文档带着项目中的疑问去 Vue3 官方文档 查阅computed、watch、生命周期钩子等更多 API。学习 TypeScript将本项目用 TypeScript 重写体验强类型带来的开发便利和可靠性。集成 UI 库尝试引入Element Plus或Ant Design Vue等 UI 组件库替换原生的输入框和按钮学习第三方库的集成。连接后端使用axios或fetch调用一个模拟的 REST API实现待办事项的持久化增删改查。记住学习框架最好的方式就是动手。把这篇文章的源码作为你的起点不断提出“如果我要实现 XX 功能该怎么做”的问题然后去搜索、去尝试、去调试。在这个过程中积累的经验远比被动阅读要深刻得多。建议收藏本文在后续实践中随时回顾这些基础概念和步骤。