ARTICLE DETAIL

建站实战干货

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

Vue 3 极简入门:2小时掌握组合式API与项目实战

2026/9/2 18:35:59 拓冰建站 浏览量
Vue 3 极简入门:2小时掌握组合式API与项目实战 在实际前端开发中Vue 3 以其更优的性能、更好的 TypeScript 支持和更灵活的组合式 API已成为新项目的首选。但对于初学者或需要快速上手一个新项目的开发者来说面对官方文档的全面性和社区教程的碎片化往往感到无从下手不知道哪些是核心哪些可以后续再学。本文旨在提供一个“极简版”的 Vue 3 学习路径目标是在 2 小时内让你理解 Vue 3 的核心概念并能够动手搭建一个可运行、可扩展的项目骨架。我们将聚焦于“即学即用”的部分跳过深奥的原理和边缘特性直接进入编码实践并附上完整的项目源码供参考和调试。本文适合有一定 HTML、CSS、JavaScript 基础但 Vue 3 经验为零或接触不多的开发者。通过本文你将学会如何从零开始配置开发环境、理解 Vue 3 的核心语法、编写一个包含基础交互的组件并最终构建和运行一个完整的单页应用。学完后你将有能力基于此骨架开发简单的后台管理系统、数据看板或移动端 H5 页面。1. 理解 Vue 3 的核心组合式 API 与响应式系统在开始写代码之前必须先理解 Vue 3 与 Vue 2 最根本的区别组合式 API (Composition API)和响应式系统的重构。这决定了你编写 Vue 3 代码的思维方式。1.1 为什么是组合式 APIVue 2 的选项式 API (Options API) 将代码按照data、methods、computed、生命周期等选项进行组织。这在组件简单时很清晰但当组件逻辑复杂后同一功能的代码会被分散到不同选项块中导致阅读和维护困难。例如一个处理用户表单的逻辑其数据、方法和监听器可能散落在各处。组合式 API 的核心思想是将同一逻辑关注点的代码组织在一起。你可以自由地将响应式状态、计算属性、函数等组合成一个可复用的“逻辑组合”并且这个组合可以轻松地在多个组件间复用。这带来了更好的逻辑复用、更灵活的代码组织和更强的类型推断支持。1.2 响应式系统的基石ref和reactiveVue 3 的响应式系统基于 ES6 的 Proxy 重构提供了两个创建响应式数据的核心函数ref: 用于创建一个响应式的引用可以包装任何类型的值。在模板中访问时.value会被自动解包。它更适合包装基本类型如字符串、数字或需要保持引用稳定的对象。import { ref } from vue; const count ref(0); // 创建一个响应式引用初始值为0 console.log(count.value); // 访问值需要使用 .value count.value; // 修改值reactive: 用于创建一个响应式的对象。它返回的是原始对象的 Proxy 代理。只能用于对象类型Object, Array, Map, Set。import { reactive } from vue; const user reactive({ name: Alice, age: 25 }); console.log(user.name); // 直接访问属性无需 .value user.age 26; // 直接修改简单记忆基本类型或需要替换整个引用时用ref复杂的对象或集合用reactive。在模板中ref会自动解包所以你写{{ count }}而不是{{ count.value }}。1.3 生命周期钩子的变化Vue 3 的生命周期钩子名称前都加了on前缀并且需要在setup()函数或script setup中使用。Vue 2 选项式 APIVue 3 组合式 API (在 setup 中)触发时机beforeCreate使用setup()本身替代组件实例初始化之前created使用setup()本身替代组件实例创建完成beforeMountonBeforeMount挂载开始之前mountedonMounted组件挂载到 DOM 后beforeUpdateonBeforeUpdate响应式数据变更DOM 更新前updatedonUpdated响应式数据变更DOM 更新后beforeUnmountonBeforeUnmount组件卸载前unmountedonUnmounted组件卸载后注意在组合式 API 中setup()函数执行时机约等于beforeCreate和created所以在这两个钩子中执行的逻辑直接写在setup()顶部即可。2. 环境准备与项目初始化我们将使用 Vite 作为构建工具它比传统的 Vue CLI 启动更快、热更新更迅速是 Vue 3 官方推荐的构建工具。2.1 安装 Node.js 与包管理器首先确保你的系统安装了 Node.js (版本 14.18 16 更佳)。你可以在终端中运行以下命令检查node -v npm -v如果未安装请前往 Node.js 官网下载 LTS 版本进行安装。npm 会随 Node.js 一同安装。你也可以选择安装yarn或pnpm本文使用npm。2.2 使用 Vite 创建 Vue 3 项目打开终端进入你希望创建项目的目录执行以下命令npm create vuelatest这个命令会安装并执行create-vue这是 Vue 官方的项目脚手架工具。接下来会有一系列交互式选项Project name: 输入你的项目名例如vue3-quick-start。Add TypeScript?: 对于快速上手可以选择No。但在实际项目中强烈建议使用 TypeScript。Add JSX Support?: 选择No。JSX 是另一种模板写法我们先学习单文件组件。Add Vue Router for Single Page Application?: 选择Yes。即使是简单项目路由也是常见需求。Add Pinia for state management?: 选择Yes。Pinia 是 Vue 官方推荐的状态管理库比 Vuex 更简单。Add Vitest for Unit Testing?: 选择No。快速上手阶段可以跳过测试。Add an End-to-End Testing Solution?: 选择No。Add ESLint for code quality?: 选择Yes。代码规范有助于团队协作。Add Prettier for code formatting?: 选择Yes。选择完成后脚手架会自动创建项目结构并安装依赖。进入项目目录并安装剩余依赖cd vue3-quick-start npm install2.3 项目结构速览创建完成后你的项目目录结构大致如下vue3-quick-start/ ├── node_modules/ # 项目依赖包 ├── public/ # 静态资源不会被Vite处理 ├── src/ # 源代码目录 │ ├── assets/ # 图片、字体等资源 │ ├── components/ # 可复用组件 │ ├── router/ # 路由配置 (由create-vue生成) │ ├── stores/ # Pinia状态存储 (由create-vue生成) │ ├── views/ # 页面级组件 │ ├── App.vue # 根组件 │ └── main.js # 应用入口文件 ├── .eslintrc.cjs # ESLint配置 ├── .prettierrc # Prettier配置 ├── index.html # HTML入口模板 ├── package.json # 项目配置和依赖 ├── vite.config.js # Vite配置 └── README.md这个结构清晰地区分了组件、页面、路由和状态是开发 Vue 3 单页应用的典型结构。3. 编写第一个 Vue 3 组件计数器与待办列表我们将通过编写两个经典示例组件来实践组合式 API一个计数器和一个简单的待办事项列表。3.1 单文件组件与script setup语法糖Vue 3 推荐使用单文件组件 (.vue文件)并提供了script setup语法糖让组合式 API 的写法更简洁。在src/components/下创建HelloWorld.vue文件。传统setup()函数写法script import { ref } from vue; export default { setup() { const count ref(0); function increment() { count.value; } return { count, increment }; // 必须返回模板中需要使用的变量和方法 } } /scriptscript setup语法糖写法script setup import { ref } from vue; const count ref(0); function increment() { count.value; } // 无需 return模板中可直接使用 count 和 increment /scriptscript setup中的代码会被编译成组件setup()函数的内容任何顶层绑定变量、函数、import都能直接在模板中使用。这极大地简化了代码。3.2 完整组件示例计数器与待办列表我们将创建一个包含两个功能的组件。替换src/components/HelloWorld.vue的内容如下template div classhello-world h2Vue 3 极简示例/h2 !-- 1. 计数器示例 -- section classcounter-demo h3计数器/h3 p当前计数{{ count }}/p button clickincrement1/button button clickdecrement-1/button button clickreset重置/button p计数是 {{ isEvenOrOdd }}/p /section !-- 2. 待办列表示例 -- section classtodo-demo h3待办事项/h3 div input v-modelnewTodo keyup.enteraddTodo placeholder输入后按回车添加 / button clickaddTodo添加/button /div ul v-iftodos.length 0 li v-for(todo, index) in todos :keytodo.id span :class{ done: todo.completed } clicktoggleTodo(todo.id) {{ todo.text }} /span button clickremoveTodo(todo.id)删除/button /li /ul p v-else暂无待办事项添加一条吧/p p总计{{ totalTodos }} 项已完成{{ completedTodos }} 项/p /section /div /template script setup import { ref, computed, reactive } from vue; // --- 计数器逻辑 --- const count ref(0); const increment () count.value; const decrement () count.value--; const reset () (count.value 0); // 计算属性根据 count 的奇偶性返回字符串 const isEvenOrOdd computed(() (count.value % 2 0 ? 偶数 : 奇数)); // --- 待办列表逻辑 --- // 使用 reactive 创建响应式对象 const todoState reactive({ newTodo: , // 绑定输入框 todos: [ // 待办数组 { id: 1, text: 学习 Vue 3 基础, completed: true }, { id: 2, text: 理解组合式 API, completed: false }, { id: 3, text: 完成这个示例项目, completed: false }, ], }); // 生成唯一ID的简单方法 let nextId 4; const addTodo () { const text todoState.newTodo.trim(); if (text) { todoState.todos.push({ id: nextId, text, completed: false, }); todoState.newTodo ; // 清空输入框 } }; const removeTodo (id) { const index todoState.todos.findIndex((todo) todo.id id); if (index -1) { todoState.todos.splice(index, 1); } }; const toggleTodo (id) { const todo todoState.todos.find((t) t.id id); if (todo) { todo.completed !todo.completed; } }; // 计算属性总项数和完成数 const totalTodos computed(() todoState.todos.length); const completedTodos computed( () todoState.todos.filter((t) t.completed).length ); /script style scoped .hello-world { font-family: Arial, sans-serif; padding: 20px; max-width: 600px; margin: 0 auto; } section { margin-bottom: 30px; padding: 15px; border: 1px solid #eee; border-radius: 8px; } button { margin: 0 5px; padding: 5px 10px; cursor: pointer; } ul { list-style: none; padding-left: 0; } li { padding: 8px; border-bottom: 1px solid #ddd; display: flex; justify-content: space-between; align-items: center; } .done { text-decoration: line-through; color: #888; } input { padding: 5px; margin-right: 10px; } /style3.3 关键代码与配置详解script setup: 这是 Vue 3 的组合式 API 语法糖。所有顶层导入和声明都自动暴露给模板。ref与reactive:count使用ref因为它是一个基本类型的数字。todoState使用reactive因为它是一个包含多个属性的复杂对象。在模板中访问其属性时直接使用todoState.newTodo和todoState.todos。computed: 用于声明依赖其他响应式状态的计算属性。isEvenOrOdd、totalTodos、completedTodos都是计算属性它们会根据依赖的count或todoState.todos自动更新。v-model:v-modeltodoState.newTodo是双向数据绑定的语法糖它相当于:valuetodoState.newTodo inputtodoState.newTodo $event.target.value。v-for与:key: 循环渲染列表时必须为每一项提供一个唯一的key这里使用todo.id。这能帮助 Vue 高效地更新虚拟 DOM。click与keyup.enter: Vue 使用作为v-on:的简写用于监听 DOM 事件。style scoped:scoped属性使得样式只作用于当前组件避免了全局样式污染。4. 集成路由与状态管理并运行项目我们已经有了一个功能组件现在需要将它集成到应用中并了解如何通过路由访问它以及如何使用 Pinia 进行简单的状态管理。4.1 配置路由并显示组件脚手架已经为我们生成了路由配置。打开src/router/index.js你会看到类似以下内容import { createRouter, createWebHistory } from vue-router import HomeView from ../views/HomeView.vue const router createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes: [ { path: /, name: home, component: HomeView }, { path: /about, name: about, component: () import(../views/AboutView.vue) } ] }) export default router我们添加一个指向HelloWorld组件的路由。首先在src/views/目录下创建DemoView.vue作为页面容器!-- src/views/DemoView.vue -- template div classdemo-view HelloWorld / /div /template script setup import HelloWorld from /components/HelloWorld.vue; /script然后在src/router/index.js的路由数组中添加一个新路由{ path: /demo, name: demo, component: () import(../views/DemoView.vue) // 使用懒加载 }最后修改src/App.vue添加一个导航链接到我们的演示页template nav router-link to/Home/router-link | router-link to/aboutAbout/router-link | router-link to/demoVue 3 Demo/router-link !-- 新增链接 -- /nav router-view/ /template4.2 使用 Pinia 管理全局状态假设我们想让计数器的值在所有组件间共享。Pinia 非常适合做这个。首先查看src/stores/counter.js脚手架可能已经生成了一个示例。我们修改它以匹配我们的计数器逻辑// src/stores/counter.js import { defineStore } from pinia import { ref, computed } from vue export const useCounterStore defineStore(counter, () { // state const count ref(0) // getters (计算属性) const doubleCount computed(() count.value * 2) const isEvenOrOdd computed(() (count.value % 2 0 ? 偶数 : 奇数)) // actions (方法) function increment() { count.value } function decrement() { count.value-- } function reset() { count.value 0 } return { count, doubleCount, isEvenOrOdd, increment, decrement, reset } })这是一个使用组合式 API 风格定义的 Store。现在在HelloWorld.vue组件中使用这个 Store 来替换本地的计数器逻辑!-- 在 HelloWorld.vue 的 script setup 部分修改 -- script setup import { ref, computed, reactive } from vue; import { useCounterStore } from /stores/counter; // 导入 Store // --- 计数器逻辑使用 Pinia--- const counterStore useCounterStore(); // 现在可以直接使用 counterStore.count, counterStore.increment 等 // --- 待办列表逻辑保持不变--- // ... 保持之前的 todoState 和其相关函数 ... /script template !-- 修改模板中计数器的部分 -- section classcounter-demo h3计数器 (使用 Pinia)/h3 p当前计数{{ counterStore.count }}/p p双倍计数{{ counterStore.doubleCount }}/p button clickcounterStore.increment1/button button clickcounterStore.decrement-1/button button clickcounterStore.reset重置/button p计数是 {{ counterStore.isEvenOrOdd }}/p /section !-- ... 其余模板保持不变 ... -- /template这样计数器的状态就被提升到了全局的 Store 中。你可以在任何组件中导入useCounterStore来访问和修改这个状态。4.3 运行与构建项目在项目根目录下运行开发服务器npm run devVite 会启动一个本地开发服务器通常在http://localhost:5173。打开浏览器访问该地址点击导航栏中的 “Vue 3 Demo”你应该能看到我们编写的计数器与待办列表应用。尝试操作按钮和输入框体验响应式更新。当你完成开发需要构建生产版本时运行npm run build构建产物将生成在dist目录下你可以将其部署到任何静态文件服务器。5. 常见问题与排查路径在学习和开发过程中你可能会遇到以下典型问题。5.1 开发服务器无法启动或端口占用问题现象可能原因检查与解决方式npm run dev报错或无法访问1. 端口被占用 (默认 5173)2. Node.js 版本过低3. 依赖未安装或损坏1. 检查端口占用netstat -ano | findstr :5173(Windows) 或lsof -i :5173(Mac/Linux)或修改vite.config.js中的server.port。2. 确认 Node.js 版本node -v升级到 LTS 版本。3. 删除node_modules和package-lock.json重新运行npm install。5.2 组件模板渲染错误或数据不更新问题现象可能原因检查与解决方式模板中显示[object Object]直接在模板中渲染了一个对象使用{{ JSON.stringify(someObject) }}调试或访问对象的特定属性{{ someObject.key }}。数据修改了但视图不更新1. 数据不是响应式的2. 直接修改了数组索引或对象属性1. 确保使用ref或reactive创建响应式数据。2. 对于reactive对象确保使用push、splice等方法修改数组或直接为对象属性赋值。对于ref包装的数组/对象修改.value或使用.value.push()。console.log输出正确但页面不变在异步操作如定时器、网络请求中修改响应式数据Vue 可能无法追踪确保在异步回调中正确访问.value对于ref或使用reactive包装的对象。5.3 路由或 Pinia 相关错误问题现象可能原因检查与解决方式路由跳转后页面空白或 4041. 路由配置错误2. 生产环境部署路径问题1. 检查router/index.js中的path和component导入是否正确。2. 如果部署到子路径需配置createWebHistory(‘/your-sub-path/’)和 Vite 的base配置。访问 Pinia store 时报getActivePinia错误在 Pinia 实例被挂载到应用之前就尝试使用 store确保在main.js中正确安装 Piniaapp.use(createPinia())并且useCounterStore()的调用发生在组件setup或script setup中而不是模块顶层。5.4 样式不生效或作用域问题问题现象可能原因检查与解决方式style scoped内的样式不生效选择器深度不够被 scoped 添加的属性选择器覆盖对于想影响子组件的样式使用:deep()深度选择器例如:deep(.child-class) { color: red; }。引入的第三方组件库样式丢失Vite 构建时未处理检查是否需要在main.js中导入组件的样式文件例如import ‘element-plus/dist/index.css’。6. 最佳实践与下一步学习方向掌握了以上基础后遵循以下实践能让你的 Vue 3 项目更健壮、更易维护。6.1 组件设计与代码组织单一职责每个组件只做一件事。如果组件变得庞大超过 200 行考虑拆分成更小的子组件或使用组合式函数抽离逻辑。使用组合式函数复用逻辑将可复用的响应式逻辑如获取数据、监听窗口大小封装成组合式函数。例如创建一个useMousePosition.js文件使用ref和onMounted、onUnmounted来追踪鼠标位置然后在多个组件中导入使用。明确的 Props 和 Emits使用defineProps和defineEmits来明确组件接收的参数和触发的事件并尽可能使用 TypeScript 定义类型。script setup const props defineProps({ title: { type: String, required: true }, count: Number }); const emit defineEmits([update:count, success]); /script6.2 状态管理策略按需使用 Pinia不是所有状态都需要放到全局 Store。组件内部状态就用ref/reactive需要跨组件共享且层级较深时再考虑 Pinia。模块化 Store将不同业务域的状态拆分到不同的 Store 文件中如userStore.js、productStore.js避免单个 Store 过于臃肿。谨慎使用持久化对于需要持久化的状态如用户 token可以使用pinia-plugin-persistedstate这类插件但要注意敏感信息的安全。6.3 性能与生产环境考量组件懒加载对于路由组件使用() import(‘…’)语法实现懒加载减少初始包体积。列表渲染优化始终为v-for提供唯一的key。对于超长列表考虑使用虚拟滚动库如vue-virtual-scroller。避免不必要的响应式对于永远不会改变的数据如常量配置不要用ref/reactive包装直接使用普通变量即可。构建分析使用npm run build -- --report或rollup-plugin-visualizer分析构建产物体积优化过大的依赖。6.4 下一步深入学习路径完成这个极简入门后你可以按以下路径系统学习核心深入官方文档的《深入响应式系统》、《渲染机制》、《组合式函数》章节。TypeScript 集成在项目中引入 TypeScript学习如何为 Props、Emits、Composable、Pinia Store 定义类型这将极大提升开发体验和代码可靠性。生态工具Vue Router: 学习嵌套路由、路由守卫、路由元信息、动态路由。Pinia: 学习 Store 间通信、插件开发、服务端渲染 (SSR) 支持。VueUse: 一个强大的 Vue 组合式工具集合提供了大量开箱即用的逻辑函数。UI 组件库根据项目需求选择一款 UI 库如 Element Plus、Ant Design Vue、Vuetify 等学习其按需引入和主题定制。高级模式学习 Teleport、Suspense、渲染函数、自定义指令等高级特性。工程化配置更完善的 ESLint Prettier Husky 提交前检查学习单元测试 (Vitest) 和组件测试 (Vue Test Utils)。这个极简项目源码已经包含了路由和状态管理的基础集成你可以以此为基础逐步添加新的页面、组件和业务逻辑在实践中不断巩固和扩展你的 Vue 3 技能栈。记住理解核心概念响应式、组合式 API比记忆所有 API 更重要遇到问题时优先查阅官方文档。