2026 年了,Vue3 早就不是“要不要学”的问题,而是“怎么学才最省时间”的问题。如果你正打算零基础入门 Web 前端,又不想被网上一堆碎片化教程绕晕,这篇可以按顺序从头看到尾。本文会用一条完整的路线,带你从环境安装、项目创建、组合式 API、组件通信、路由、Pinia,一直走到项目实战和面试题。
Vue3 当前最值得关注的是组合式 API(Composition API)和<script setup>语法,它们让组件逻辑更集中、复用更简单。同时 Vite 构建工具的普及让冷启动和热更新变得非常快,开发体验比 Vue2 + Webpack 时代提升明显。文章会按住 ESC 拆解关键知识点,并给出大量可直接复制的代码示例。
如果你已经在用 Vue2,想了解 Vue2 和 Vue3 的核心差异,或者是刚学会 HTML/CSS/JavaScript、准备系统学习 Vue3 的小白,这篇都适合。为了让你学完能真正上手,文章还会穿插高频 Vue3 面试题(computed和watch怎么选、defineEmits怎么用、动态路由怎么配置等等)。
1. Vue3 核心能力速览
在学习之前,先用一张表搞清楚 Vue3 的生态现状和基本诉求:
| 维度 | 说明 |
|---|---|
| 框架类型 | 渐进式 JavaScript 前端框架,主打组件化和响应式 UI |
| 当前主流版本 | Vue 3.x,搭配 Vite 构建工具使用 |
| 核心语法 | 组合式 API(Composition API)、<script setup>、响应式 API |
| 常用生态 | Vue Router 4、Pinia、Element Plus、Axios、Vite |
| 技术门槛 | 前置要求:HTML、CSS、JavaScript 基础;不需要后端知识也能学 |
| 启动方式 | npm run dev/pnpm dev,浏览器访问本地开发服务器 |
| 是否支持 TypeScript | 官方一等公民,Vue3 + TypeScript 是当前企业项目标配 |
| 适合场景 | Web 后台管理系统、中台项目、PC 端应用、移动端 H5、跨端开发(uni-app) |
| 就业方向 | Web 前端开发、Vue 全栈开发、低代码平台开发、可视化大屏 |
从技能树看,Vue3 核心要掌握的知识点包括:模板语法、指令、响应式原理、组件化、生命周期、组件通信、Composition API、路由、状态管理、组件库使用、接口请求封装、项目部署。下面从零开始搭建。
2. 适用场景与学习路线
2.1 适合谁学
- 刚学完 HTML / CSS / JavaScript,想快速进入前端框架阶段的小白。
- 学校课程或自学过程中接触过 Vue2,正准备升级到 Vue3 的开发者。
- 工作中维护 Vue3 后台管理系统,但一直对组合式 API 不熟的页面开发者。
- 准备前端面试,需要系统梳理 Vue3 高频考点的求职者。
2.2 一条主线学习路径
推荐按下面的顺序推进,每步都以“能写出来”为验收标准:
- 掌握 JavaScript ES6+ 语法:解构赋值、箭头函数、Promise、async/await、模块化。
- 安装 Node.js 和包管理工具,学会创建 Vue3 项目。
- 理解 Vue3 模板语法:插值、指令、事件、表单双向绑定。
- 掌握响应式核心:
ref、reactive、computed、watch。 - 掌握组件化:组件定义、Props、事件、插槽、
v-model。 - 掌握组合式 API:
setup、<script setup>、自定义 Hook。 - 学习 Vue Router 4:路由配置、嵌套路由、动态路由、导航守卫。
- 学习 Pinia:状态管理、模块拆分、持久化。
- 结合 Element Plus / Vant 开发完整页面。
- 接口请求封装 + Axios 拦截器 + 环境变量配置。
- 用 Vite 构建部署,输出静态文件到服务器。
这套路径在速成时也是最稳的,每一步都有可以运行的结果,不容易产生“学了不会用”的挫败感。
3. 开发环境准备与前置条件
3.1 安装 Node.js
Vue3 项目依赖 Node.js,建议直接安装 LTS 长期支持版本。安装完成后打开终端(Windows 用 PowerShell / CMD,macOS 用 Terminal)检查版本:
node -v npm -v如果提示node不是内部或外部命令,说明没装好或没配置 PATH 环境变量。Windows 用户安装时注意勾选 “Add to PATH”。
3.2 安装包管理工具
npm 会随 Node.js 一起安装。另一个推荐的工具是 pnpm,安装更快、磁盘占用更少:
npm install -g pnpm3.3 开发工具推荐
- VS Code
- 插件:Vue - Official(Volar 的正式版,Vue3 语法提示必备)、ESLint、Prettier
注意:如果你之前装过 Vetur,建议在 Vue3 项目中禁用,因为 Volar 对<script setup>的支持更完整。
3.4 浏览器与调试工具
推荐 Chrome 或 Edge,并安装 Vue.js Devtools 浏览器扩展,用于查看组件树、响应式数据、Pinia 状态和路由信息。这对后续调试组合式 API 非常有用。
4. 创建 Vue3 项目与目录结构分析
4.1 用 Vite 创建 Vue3 项目
官方推荐使用 Vite 创建新项目:
# 方式一:npm npm create vue@latest # 方式二:pnpm pnpm create vue@latest这个命令会创建基于 Vue3 + Vite 的官方脚手架,创建过程中可以按需选择 TypeScript、Vue Router、Pinia、ESLint 等:
✔ Project name: … vue3-demo ✔ Add TypeScript? … No / Yes ✔ Add JSX Support? … No / Yes ✔ Add Vue Router for Single Page Application development? … No / Yes ✔ Add Pinia for state management? … No / Yes ✔ Add ESLint for code quality? … No / Yes进入项目并启动:
cd vue3-demo npm install npm run dev终端会显示本地访问地址,例如http://localhost:5173/。在浏览器打开就能看到默认页面。
如果你之前用的是vue create(Vue CLI),这里建议尽快切到 Vite 创建方式。Vue CLI 已经进入维护期,新项目基本不再推荐。
4.2 Vite 项目目录结构
vue3-demo/ ├── index.html ├── package.json ├── vite.config.js ├── public/ │ └── favicon.ico └── src/ ├── main.js ├── App.vue ├── assets/ └── components/其中index.html是页面入口,src/main.js负责创建应用实例:
import { createApp } from 'vue' import App from './App.vue' createApp(App).mount('#app')App.vue是根组件。Vue3 项目一切皆组件,后续所有页面和通用模块都可以按组件拆分。
5. Vue3 基础语法:模板、指令与响应式
5.1 模板语法
Vue3 模板中可以直接使用{{ }}插值表达式输出数据:
<template> <div> <h1>{{ title }}</h1> <p>count 的值为:{{ count }}</p> </div> </template>5.2 响应式数据:ref 与 reactive
Vue3 组合式 API 中最常用的两个响应式 API 是ref和reactive。
ref可以定义基本类型和引用类型,在<script setup>中使用:
<script setup> import { ref } from 'vue' const count = ref(0) const increment = () => { count.value++ } </script> <template> <div> <p>当前计数:{{ count }}</p> <button @click="increment">+1</button> </div> </template>在模板中count会自动解包,不需要写count.value;但在 JavaScript 逻辑中修改时必须使用.value。
reactive更适合定义对象的响应式状态:
<script setup> import { reactive } from 'vue' const user = reactive({ name: '张三', age: 20 }) const updateName = () => { user.name = '李四' } </script>reactive直接访问属性即可,不需要.value。但注意它只能用于对象、数组等引用类型,不能用于基本类型。
5.3 常用指令
v-if/v-else-if/v-else:条件渲染v-show:切换 display 显隐v-for:列表渲染v-model:表单双向绑定v-bind(简写:):动态绑定属性v-on(简写@):绑定事件v-html:渲染 HTML 字符串v-once:只渲染一次v-memo:性能优化用,按依赖条件缓存渲染结果
一个典型例子:
<script setup> import { ref } from 'vue' const inputValue = ref('') const list = ref(['Vue', 'React', 'Angular']) </script> <template> <input v-model="inputValue" placeholder="请输入内容" /> <p>你输入的是:{{ inputValue }}</p> <ul> <li v-for="(item, index) in list" :key="index">{{ item }}</li> </ul> </template>6. 组合式 API 核心:setup、computed 与 watch
6.1 setup 与 script setup
在 Vue3 中,setup是组合式 API 的入口。推荐直接使用<script setup>语法,更简洁,定义的数据和函数默认都能在模板中使用:
<script setup> import { ref } from 'vue' const message = ref('Hello Vue3') </script> <template> <div>{{ message }}</div> </template>不需要return,代码量明显减少,这也是现在企业项目中最主流的写法。
6.2 computed 计算属性
computed用于派生状态,必须依赖其他响应式数据。典型场景:全选反选、商品总价、搜索过滤。
<script setup> import { ref, computed } from 'vue' const price = ref(100) const count = ref(2) const totalPrice = computed(() => { return price.value * count.value }) </script> <template> <p>单价:{{ price }}</p> <p>数量:{{ count }}</p> <p>总价:{{ totalPrice }}</p> </template>6.3 watch 与 watchEffect
watch用于监听数据变化并执行副作用,例如监听搜索关键词发请求、监听路由变化、监听表单值变化做校验。
<script setup> import { ref, watch } from 'vue' const keyword = ref('') watch(keyword, (newVal, oldVal) => { console.log('keyword 变化', newVal, oldVal) }) </script>watchEffect不需要指定监听源,会自动收集依赖:
<script setup> import { ref, watchEffect } from 'vue' const count = ref(0) watchEffect(() => { console.log('count 变化了', count.value) }) </script>这里有一个 Vue3 面试高频题:computed和watch怎么选?
computed侧重计算派生值,必须有返回值,适合依赖响应式数据的同步计算结果。watch侧重监听并执行操作,可以没有返回值,适合异步请求、DOM 操作、日志上报等副作用场景。
7. 组件通信:Props、defineEmits 与 v-model
7.1 父传子:Props
子组件通过defineProps声明接收的属性:
<!-- Child.vue --> <script setup> const props = defineProps({ title: { type: String, default: '' }, count: { type: Number, default: 0 } }) </script> <template> <div> <h3>{{ title }}</h3> <p>count: {{ count }}</p> </div> </template>父组件传值:
<script setup> import { ref } from 'vue' import Child from './Child.vue' const title = ref('子组件标题') const count = ref(10) </script> <template> <Child :title="title" :count="count" /> </template>7.2 子传父:defineEmits
这是一个非常高频的 Vue3 面试点。子组件通过defineEmits声明事件,在需要时触发,父组件监听事件并处理:
<!-- Child.vue --> <script setup> const emit = defineEmits(['updateData', 'delete']) const handleClick = () => { emit('updateData', '这是子组件传给父组件的数据') } </script> <template> <button @click="handleClick">点击触发事件</button> </template>父组件:
<script setup> import Child from './Child.vue' const handleUpdate = (data) => { console.log('父组件接收到的数据:', data) } </script> <template> <Child @update-data="handleUpdate" /> </template>模板里的事件名update-data会自动匹配emit('updateData'),不需要额外处理。
7.3 v-model 的组件化用法
Vue3 中v-model可以在自定义组件上使用,默认对应modelValue属性和update:modelValue事件:
<!-- CustomInput.vue --> <script setup> const props = defineProps({ modelValue: { type: String, default: '' } }) const emit = defineEmits(['update:modelValue']) const onInput = (e) => { emit('update:modelValue', e.target.value) } </script> <template> <input :value="modelValue" @input="onInput" /> </template>父组件:
<script setup> import { ref } from 'vue' import CustomInput from './CustomInput.vue' const text = ref('') </script> <template> <CustomInput v-model="text" /> </template>这种模式在封装表单组件时非常常用。
8. 生命周期与组件挂载顺序
Vue3 组合式 API 中,生命周期钩子带上on前缀:
| Vue2 Option API | Vue3 Composition API | 说明 |
|---|---|---|
| beforeCreate | 不再需要 | setup 阶段 |
| created | 不再需要 | setup 阶段 |
| beforeMount | onBeforeMount | 挂载前 |
| mounted | onMounted | 挂载后 |
| beforeUpdate | onBeforeUpdate | 更新前 |
| updated | onUpdated | 更新后 |
| beforeUnmount | onBeforeUnmount | 卸载前 |
| unmounted | onUnmounted | 卸载后 |
一个基础示例:
<script setup> import { onMounted, onUnmounted } from 'vue' onMounted(() => { console.log('组件挂载完成') }) onUnmounted(() => { console.log('组件卸载,清理定时器/事件监听') }) </script>建议在onMounted中发初始化请求,在onUnmounted中清理定时器、取消订阅,避免内存泄漏。
9. Vue Router 4:路由配置与动态路由
9.1 基础路由配置
安装 Vue Router 4:
npm install vue-router@4在src/router/index.js中配置:
import { createRouter, createWebHistory } from 'vue-router' import Home from '../views/Home.vue' import About from '../views/About.vue' const routes = [ { path: '/', name: 'Home', component: Home }, { path: '/about', name: 'About', component: About } ] const router = createRouter({ history: createWebHistory(), routes }) export default router在main.js中注册:
import { createApp } from 'vue' import App from './App.vue' import router from './router' const app = createApp(App) app.use(router) app.mount('#app')9.2 编程式导航与路由跳转不刷新页面
模板中使用<router-link>,逻辑中使用useRouter或useRoute:
<script setup> import { useRouter } from 'vue-router' const router = useRouter() const goHome = () => { router.push('/') } const goBack = () => { router.back() } </script>如果你想跳转到相同路由但参数变化时页面不刷新,可以用watch监听route.params或route.query:
<script setup> import { useRoute } from 'vue-router' import { watch } from 'vue' const route = useRoute() watch( () => route.params.id, (newId) => { console.log('路由参数变化', newId) } ) </script>9.3 动态路由与权限路由
后台管理系统中,菜单和页面权限通常由后端返回,前端再动态添加路由。Vue Router 4 提供了router.addRoute方法:
import { createRouter, createWebHistory } from 'vue-router' const router = createRouter({ history: createWebHistory(), routes: [ { path: '/login', component: () => import('../views/Login.vue') } ] }) // 登录成功后根据权限动态添加路由 export function addDynamicRoutes(menus) { menus.forEach(menu => { router.addRoute({ path: menu.path, name: menu.name, component: () => import(`../views/${menu.component}.vue`) }) }) } export default router动态路由的重点是权限控制和刷新后路由丢失问题,需要配合 Pinia 或本地存储保存路由结构,并在路由守卫中重新添加。
10. Pinia 状态管理与 provide/inject
10.1 Pinia 基本使用
Pinia 是 Vue3 官方推荐的状态管理库,相比 Vuex 更轻量,去掉了mutations,直接在actions中修改状态。
安装:
npm install pinia创建 store,例如src/stores/user.js:
import { defineStore } from 'pinia' export const useUserStore = defineStore('user', { state: () => ({ token: '', userInfo: {} }), getters: { isLogin: (state) => !!state.token }, actions: { setToken(token) { this.token = token }, setUserInfo(info) { this.userInfo = info }, logout() { this.token = '' this.userInfo = {} } } })在组件中使用:
<script setup> import { useUserStore } from '../stores/user' const userStore = useUserStore() const handleLogin = () => { userStore.setToken('token-xxx') } </script> <template> <p>登录状态:{{ userStore.isLogin }}</p> </template>10.2 Pinia 模块化与持久化
大型项目中 store 按业务拆分为多个模块,例如user.js、menu.js、app.js,使用时分别 import 即可。持久化可以自己写 localStorage 同步,也可以使用pinia-plugin-persistedstate插件。
10.3 provide 与 inject 跨层级通信
跨多级组件传递数据时,不需要逐层传 Props,可以用provide和inject:
<!-- 父组件 --> <script setup> import { provide, ref } from 'vue' const theme = ref('dark') provide('theme', theme) </script><!-- 任意后代组件 --> <script setup> import { inject } from 'vue' const theme = inject('theme') </script>适合主题切换、全局配置、登录信息等跨层级场景。
11. Vue3 中接口请求与 Axios 封装
11.1 在 setup 中使用 axios
npm install axios简单请求示例:
<script setup> import { ref, onMounted } from 'vue' import axios from 'axios' const list = ref([]) const loading = ref(false) const fetchList = async () => { loading.value = true try { const res = await axios.get('/api/list') list.value = res.data.data } catch (error) { console.error('请求失败', error) } finally { loading.value = false } } onMounted(() => { fetchList() }) </script>11.2 封装请求工具与拦截器
企业项目中建议把 axios 封装成独立模块:
// src/utils/request.js import axios from 'axios' import { useUserStore } from '../stores/user' import { ElMessage } from 'element-plus' const request = axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL || '/api', timeout: 10000 }) // 请求拦截器:自动携带 token request.interceptors.request.use( (config) => { const userStore = useUserStore() if (userStore.token) { config.headers.Authorization = `Bearer ${userStore.token}` } return config }, (error) => { return Promise.reject(error) } ) // 响应拦截器:统一处理错误码 request.interceptors.response.use( (response) => { const res = response.data if (res.code !== 200) { ElMessage.error(res.message || '接口异常') return Promise.reject(new Error(res.message)) } return res }, (error) => { ElMessage.error(error.message || '网络异常') return Promise.reject(error) } ) export default request在 Vite 中跨域配置可以在vite.config.js中设置 proxy:
import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' export default defineConfig({ plugins: [vue()], server: { port: 5173, proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true, rewrite: (path) => path.replace(/^\/api/, '') } } } })12. 组件库集成:Element Plus 与 Vant
12.1 Element Plus 后台管理常用组件库
npm install element-plus完整引入:
// main.js import { createApp } from 'vue' import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' import App from './App.vue' const app = createApp(App) app.use(ElementPlus) app.mount('#app')推荐按需自动导入,配合unplugin-vue-components和unplugin-auto-import:
npm install -D unplugin-vue-components unplugin-auto-import在vite.config.js中配置:
import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import AutoImport from 'unplugin-auto-import/vite' import Components from 'unplugin-vue-components/vite' import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' export default defineConfig({ plugins: [ vue(), AutoImport({ resolvers: [ElementPlusResolver()] }), Components({ resolvers: [ElementPlusResolver()] }) ] })12.2 移动端 Vant 组件库
移动端 H5 项目常用 Vant:
npm install vant页面中使用:
<script setup> import { ref } from 'vue' import { showToast } from 'vant' const text = ref('') const onSubmit = () => { showToast('提交成功') } </script> <template> <van-form @submit="onSubmit"> <van-field v-model="text" name="内容" label="内容" placeholder="请输入" /> <van-button round block type="primary" native-type="submit">提交</van-button> </van-form> </template>13. Vue3 面试高频考点与实战注意点
结合社区最常见的问题,这里按速查方式整理:
| 考点 | 核心要点 |
|---|---|
| Vue2 和 Vue3 区别 | 响应式原理(Object.defineProperty -> Proxy)、组合式 API、多根节点、Teleport、Fragments、Vite 构建 |
ref和reactive怎么选 | 基本类型用 ref,对象用 reactive;ref 更通用,模板访问更友好 |
computed和watch区别 | computed 有缓存且有返回值,watch 执行副作用 |
defineEmits写法 | const emit = defineEmits(['updateData']),模板中@update-data监听 |
| 动态路由 | router.addRoute+ 路由守卫 + 权限控制 |
setup中如何使用 axios | 直接在<script setup>中调用封装好的请求函数 |
| 路由跳转不刷新页面 | 用watch监听route.params/route.query变化 |
| Vue3 强制更新 | v-if加 key 重新渲染、reactive赋值整个对象、nextTick后操作 DOM |
| JSX 支持 | Vite 创建项目时选择 JSX,或安装@vitejs/plugin-vue-jsx |
| 自定义指令 | const vFocus = { mounted(el) { el.focus() } } |
14. Vue3 常见问题与排查方法
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 页面打不开 | 开发服务未启动或端口被占用 | 检查终端日志、netstat -ano查看端口 | 换端口npm run dev -- --port 5174或重启 |
| 控制台提示 “Cannot read properties of undefined” | 响应式数据还没赋值就访问嵌套属性 | 打印数据,确认接口返回结构 | 使用可选链?.或默认值兜底 |
| 修改了数组内容但页面没更新 | 直接通过索引修改数组 | 在 Vue3 中检查是否用了reactive包裹 | 用splice/ 赋值新数组 /ref+ 整个替换 |
| 路由切换后组件不刷新 | 动态路由或相同组件复用 | 控制台观察路由参数变化 | watch监听route参数 |
| 组件事件触发后父组件收不到 | defineEmits没声明 | 检查子组件事件名和模板事件大小写 | 统一用 kebab-case 监听 |
| 接口请求跨域 | Vite 代理未配置 | 检查 Network 面板请求 URL | 配置server.proxy |
| 打包后白屏 | 静态资源路径错误 | 检查线上 URL | vite.config.js设置base: './' |
| ElMessage 无法正常显示 | 组件样式未引入或自动导入配置问题 | 检查控制台样式报错 | 完整引入 Element Plus 样式 |
15. Vue3 最佳实践与学习建议
15.1 项目规范建议
- 目录按业务模块拆分,组件、页面、路由、store、接口分目录管理。
- API 请求统一封装,每个业务模块对应一个 api 文件。
- 公共组件放到
components下,命名大写开头。 - 路由使用路由懒加载,页面首次加载更快。
- store 中只放跨页面共享的全局状态,页面内部状态不放入 Pinia。
- 所有请求加 loading 状态,异常必须有用户可感知的提示。
- 批量接口任务注意取消重复请求和超时处理。
15.2 学习方法建议
- 第一遍先跟着项目把代码敲通,不要纠结响应式原理,先知道怎么写,再理解为什么。
- 第二遍带着问题回看官方文档,重点看
ref、reactive、computed、watch、生命周期。 - 第三遍自己从零开发一个后台管理系统,把组合式 API、路由、Pinia、Axios 全部用上。
- 遇到报错先看控制台,再查官方文档,最后再搜索,不要一上来就复制网上代码。
15.3 版本与依赖注意点
- Node.js 版本不要太老,建议 18+。
npm install后如果出现 lock 文件版本冲突,优先删除node_modules和 lock 文件后重装。- 尽量保持 Vue、Vite、Vue Router、Pinia 均为最新稳定版本。
- 遇到新项目不要混合 Vue2 和 Vue3 的写法,
Vue.extend、filters、Vue.use这种 Vue2 写法在 Vue3 中不可用。
16. 总结
Vue3 学习路径并不复杂:先搭环境,再写基础语法,然后进入组件化开发和组合式 API,最后用 Vue Router、Pinia、Axios 和组件库串起一个完整项目。很多人学不会,不是 Vue3 难,而是跳过了“先抄后懂”的过程,直接啃原理。
如果你是从 Vue2 过来的,重点只需要搞清楚三件事:setup代替了大部分 Options API 写法、ref和reactive替代了data、响应式原理从 defineProperty 换成了 Proxy。剩下的模板语法和组件通信思路是相通的。
如果是零基础,建议先确认自己是否掌握了 JavaScript 的 ES6 语法,环境装好之后,把本文中的代码示例全部在本地跑一遍。前端开发的核心能力是“能写出来”,不是“看起来会了”。
下一步根据自己的方向继续扩展:做后台系统就深入 Element Plus 和权限动态路由,做移动端就看看 Vant 和 uni-app,做可视化就学 ECharts 配合 Vue3 使用。这套路线走完,你已经可以独立开发完整的前端项目了。