Vue3 从零上手实战:环境搭建、组合式 API 与路由配置避坑指南
2026/9/19 12:21:43 网站建设 项目流程

1. 从零上手 Vue3:一个后端转前端的人踩过的所有坑

Vue3 出来已经好几年了,但真正让我下决心系统学一遍,是因为接手了一个后台管理系统的维护工作。项目用的是 Vue3 + TypeScript + Vite,而我之前只写过 Vue2 的 Options API,连setup是啥都不知道。打开代码一看,满屏的refreactivecomputed,还有各种definePropsdefineEmits,整个人是懵的。后来花了大概两周时间,从环境搭建到路由配置再到组合式 API 的实战,总算把整个链路跑通了。这篇文章就是把我这段时间的学习路径、踩过的坑、以及那些“当时要是有人告诉我就好了”的经验,完整地整理出来。

如果你也是刚接触 Vue3 的新手,或者从 Vue2 迁移过来还在适应组合式 API 的写法,又或者你正在搭建一个 Vue3 的后台管理系统,那这篇内容应该能帮你省下不少查文档和试错的时间。我会从最基础的环境配置讲起,一直讲到路由、组件通信、常见报错排查,尽量做到“照着做就能跑起来”。

1.1 为什么现在学 Vue3 而不是继续用 Vue2

这个问题我一开始也纠结过。Vue2 我用了两年多,写起来很顺手,为什么要换?直到我真正在项目里用了一段时间 Vue3,才发现几个关键差异直接影响了开发体验。

首先是响应式系统的底层重构。Vue2 用的是Object.defineProperty,这个 API 有个天然缺陷:它没法检测对象属性的新增和删除,也没法直接监听数组索引和长度的变化。所以 Vue2 里才会有Vue.setVue.delete这些补丁方法。Vue3 换成了Proxy,直接代理整个对象,上面那些限制全没了。你在setup里随便给reactive对象加属性,视图都能自动更新,不需要额外调 API。

其次是组合式 API 带来的逻辑复用方式。Vue2 的mixin用过的都知道,命名冲突、来源不清晰、多个 mixin 混在一起根本看不出数据从哪来。Vue3 的组合式 API 把相关逻辑写在一个函数里,哪个组件用就 import 哪个,清晰得多。我后来把项目里的权限判断、表格分页、表单校验都抽成了独立的组合函数,复用起来非常舒服。

再就是性能层面的提升。Vue3 的 diff 算法做了优化,编译时会对静态节点做标记,更新时直接跳过。加上 Tree-shaking 的支持,打包体积也比 Vue2 小不少。对于一个后台管理系统来说,首屏加载速度的提升是能明显感知到的。

还有一个很现实的原因:生态已经全面转向 Vue3。Element Plus、Ant Design Vue、Naive UI 这些主流组件库,新版本都是 Vue3 优先。Vue2 已经进入维护模式,新项目再用 Vue2 确实不太合适了。

1.2 这篇文章适合谁看,能帮你解决什么

我写这篇内容的定位很明确:面向刚接触 Vue3 的新手,以及从 Vue2 转过来需要快速上手的开发者。不管你是前端科班出身,还是像我一样从后端转过来的,只要你会基本的 HTML、CSS、JavaScript,就能跟着走下来。

具体来说,这篇文章会覆盖以下几个核心问题:

  • 怎么在 Windows 上搭建一个完整的 Vue3 开发环境,包括 Node.js、包管理器、编辑器的配置
  • create-vue和 Vite 创建项目的完整流程,以及每种配置选项的含义
  • 组合式 API 的核心概念:refreactivecomputedwatch、生命周期钩子
  • Vue Router 的配置和跳转,包括动态路由、路由守卫、参数传递
  • 组件之间的通信方式,从 props 到 emit 再到 provide/inject
  • 实际开发中常见的报错和排查思路

我不会只贴代码不说原因。每个配置项、每个 API 的选择,我都会解释为什么这么用,什么场景下该用哪个。这样你遇到类似问题时,能自己判断该怎么处理,而不是只会复制粘贴。

2. 开发环境搭建:Windows 下的完整配置流程

环境搭建是很多新手的第一道坎。我见过不少人卡在 Node.js 版本不对、npm 安装慢、编辑器没装对插件这些问题上,还没开始写代码就放弃了。这一章我把 Windows 下搭建 Vue3 开发环境的完整流程拆开讲,每个步骤都说明为什么这么做。

2.1 Node.js 版本选择与安装细节

Vue3 和 Vite 对 Node.js 版本有要求。Vite 4.x 需要 Node.js 14.18+ 或 16+,Vite 5.x 要求 Node.js 18+。我建议直接装Node.js 18 LTS 或 20 LTS,这两个版本都是长期支持版,稳定性和兼容性都经过验证。

安装方式有两种:官网下载安装包,或者用版本管理工具。我推荐用nvm-windows(Node Version Manager for Windows),原因是不同项目可能依赖不同的 Node 版本,用 nvm 可以随时切换,不用反复卸载重装。

安装 nvm-windows 的步骤:

  1. 去 GitHub 搜nvm-windows,下载nvm-setup.exe
  2. 安装时注意两个路径:nvm 的安装目录和 Node.js 的 symlink 目录,后者不要有空格
  3. 安装完成后打开新的命令行窗口,输入nvm version确认安装成功
  4. 输入nvm install 18.20.0安装指定版本
  5. 输入nvm use 18.20.0切换到该版本

装完之后用node -vnpm -v检查版本。如果node -v没输出或者报错,大概率是环境变量没配好,检查一下 nvm 的 symlink 目录是否加到了 PATH 里。

注意:安装 nvm-windows 之前,如果系统里已经装了 Node.js,建议先卸载,否则可能出现版本冲突。另外 nvm 切换版本时需要管理员权限,普通命令行窗口可能报错,用管理员身份打开终端即可。

2.2 包管理器选型:npm、yarn 还是 pnpm

Node.js 自带 npm,但 npm 在安装依赖时速度较慢,而且多个项目会重复下载相同的包,占用大量磁盘空间。我现在的选择是pnpm,原因有三个:

  • 速度快:pnpm 用硬链接的方式共享依赖,安装速度比 npm 快很多
  • 省空间:同一个包在磁盘上只存一份,多个项目共用
  • 依赖严格:pnpm 的 node_modules 结构更严格,不会出现“幽灵依赖”问题(即没有在 package.json 里声明的包也能被引用)

安装 pnpm 很简单,一行命令:

npm install -g pnpm

装完之后用pnpm -v检查版本。如果你之前用的是 npm,项目里的package-lock.json可以删掉,用pnpm install重新生成pnpm-lock.yaml

当然,如果你只是学习阶段,用 npm 也完全没问题。只是当项目依赖多起来之后,pnpm 的优势会越来越明显。我自己的后台管理系统项目,用 npm 装依赖要两分多钟,换成 pnpm 之后大概三十秒就搞定了。

2.3 编辑器配置:VS Code 插件与设置

VS Code 是目前前端开发的主流编辑器,Vue3 开发需要装几个关键插件:

插件名称作用是否必装
Vue - OfficialVue3 语法高亮、智能提示、类型检查必装
TypeScript Vue Plugin让 TS 文件识别 .vue 文件用 TS 时必装
ESLint代码规范检查推荐
Prettier代码格式化推荐
Auto Rename Tag自动重命名配对标签推荐
Path Intellisense路径自动补全推荐

注意:以前很多人装的是Vetur插件,那是 Vue2 时代的产物,Vue3 项目里要禁用 Vetur,改用Vue - Official(以前叫 Volar)。两个插件同时开启会冲突,导致语法高亮异常。

VS Code 的settings.json里建议加几项配置,让开发体验更好:

{ "editor.formatOnSave": true, "editor.defaultFormatter": "esbenp.prettier-vscode", "files.autoSave": "onFocusChange", "vue.updateImportsOnFileMove.enabled": true }

vue.updateImportsOnFileMove.enabled这个配置很实用,当你移动或重命名组件文件时,VS Code 会自动更新所有引用该文件的 import 路径,省去手动修改的麻烦。

3. 创建 Vue3 项目:create-vue 与 Vite 的配合

环境配好之后,下一步就是创建项目。Vue3 官方推荐的方式是用create-vue,它底层基于 Vite,提供了交互式的配置选项。这一章我把创建项目的完整流程和每个选项的含义讲清楚。

3.1 create-vue 的交互式配置逐项解读

在终端里执行:

npm create vue@latest

或者用 pnpm:

pnpm create vue@latest

然后会进入交互式问答,我逐项说明每个选项的含义和选择建议:

Project name:项目名称,只能用英文小写和连字符,比如my-vue-app。这个名字会作为文件夹名和 package.json 里的 name 字段。

Add TypeScript?:是否使用 TypeScript。如果你之前没接触过 TS,建议先选 No,用纯 JavaScript 把 Vue3 的核心概念跑通,之后再学 TS。但如果你有后端经验或者已经会 TS,直接选 Yes,Vue3 对 TS 的支持非常好。

Add JSX Support?:是否支持 JSX 语法。Vue3 默认用模板语法,JSX 是可选的。除非你有 React 背景或者需要写渲染函数,否则选 No 就行。

Add Vue Router for Single Page Application development?:是否添加 Vue Router。做单页应用基本都要用路由,选 Yes。后面我会专门讲路由的配置和使用。

Add Pinia for state management?:是否添加 Pinia 状态管理。Pinia 是 Vue3 官方推荐的状态管理库,替代了 Vue2 时代的 Vuex。如果你做的是中小型项目,可能用不上;但后台管理系统这类需要跨组件共享状态的场景,建议选 Yes。

Add Vitest for Unit Testing?:是否添加单元测试。学习阶段可以先选 No,等需要写测试的时候再手动加。

Add an End-to-End Testing Solution?:端到端测试方案,学习阶段选 No。

Add ESLint for code quality?:是否添加 ESLint 代码检查。建议选 Yes,能帮你养成好的代码习惯。

Add Prettier for code formatting?:是否添加 Prettier 格式化。建议选 Yes,和 ESLint 配合使用。

选完之后,create-vue会自动生成项目结构并安装依赖。整个过程大概一两分钟。

3.2 项目目录结构详解

创建完成后,用编辑器打开项目,你会看到这样的目录结构:

my-vue-app/ ├── public/ # 静态资源,不会被 Vite 处理 ├── src/ │ ├── assets/ # 需要被 Vite 处理的静态资源 │ ├── components/ # 公共组件 │ ├── router/ # 路由配置 │ ├── stores/ # Pinia 状态管理 │ ├── views/ # 页面级组件 │ ├── App.vue # 根组件 │ └── main.js # 入口文件 ├── index.html # HTML 模板 ├── package.json # 项目依赖和脚本 ├── vite.config.js # Vite 配置 └── jsconfig.json # JS 项目配置(TS 项目是 tsconfig.json)

几个关键点说明:

index.html是 Vite 项目的入口 HTML,它会被 Vite 处理,<script type="module" src="/src/main.js">这行引入了入口 JS。和 Vue CLI 不同,Vite 的 index.html 在项目根目录,而不是 public 目录。

src/main.js是应用的入口,它创建 Vue 应用实例并挂载到 DOM:

import { createApp } from 'vue' import App from './App.vue' import router from './router' const app = createApp(App) app.use(router) app.mount('#app')

vite.config.js是 Vite 的配置文件,可以配置别名、代理、插件等。比如配置@指向src目录:

import { fileURLToPath, URL } from 'node:url' import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' export default defineConfig({ plugins: [vue()], resolve: { alias: { '@': fileURLToPath(new URL('./src', import.meta.url)) } } })

配好别名之后,import 组件就可以写成import HelloWorld from '@/components/HelloWorld.vue',不用写一长串相对路径。

3.3 启动项目与热更新验证

在项目根目录执行:

npm run dev

或者:

pnpm dev

终端会输出本地访问地址,通常是http://localhost:5173。打开浏览器访问,看到 Vue 的欢迎页面就说明项目跑起来了。

Vite 的热更新(HMR)非常快,你修改组件里的内容,浏览器几乎瞬间就能看到变化,不需要手动刷新。这是 Vite 相比 Webpack 的一大优势,开发体验提升明显。

提示:如果npm run dev报错,先检查 Node.js 版本是否符合要求。Vite 5.x 需要 Node.js 18+,版本太低会直接报错。另外,如果端口 5173 被占用,Vite 会自动切换到下一个可用端口,注意看终端输出的实际地址。

4. 组合式 API 核心概念:从 ref 到生命周期

组合式 API 是 Vue3 最核心的变化,也是新手最容易懵的地方。这一章我把最常用的几个 API 讲透,每个都配上实际场景和代码示例。

4.1 ref 与 reactive 的选择逻辑

refreactive都是用来创建响应式数据的,但使用场景不同。

ref可以包裹任何类型的值:基本类型(字符串、数字、布尔值)、对象、数组。在<script setup>里访问ref的值需要加.value,但在模板里会自动解包,不需要加。

import { ref } from 'vue' const count = ref(0) const user = ref({ name: '张三', age: 25 }) // 修改值 count.value++ user.value.name = '李四'

reactive只能包裹对象类型(包括数组和 Map、Set),访问时不需要.value

import { reactive } from 'vue' const state = reactive({ count: 0, user: { name: '张三', age: 25 } }) // 直接修改 state.count++ state.user.name = '李四'

那到底该用哪个?我的经验是:优先用ref。原因是ref更通用,不会丢失响应性。reactive有个坑:如果你把它的属性解构出来,响应性会丢失。比如:

const state = reactive({ count: 0 }) let { count } = state // count 失去了响应性

ref解构后仍然是响应式的(在 Vue 3.2+ 中,解构ref需要配合toRefs或直接访问.value)。

当然,如果你要管理一组相关的状态,比如一个表单的多个字段,用reactive写起来更简洁:

const form = reactive({ username: '', password: '', remember: false })

模板里直接form.username就行,不用写一堆.value

4.2 computed 与 watch 的实战用法

computed用来定义计算属性,它会根据依赖自动缓存,只有依赖变化时才重新计算。

import { ref, computed } from 'vue' const firstName = ref('张') const lastName = ref('三') const fullName = computed(() => firstName.value + lastName.value)

computed默认是只读的,如果你想修改计算属性的值,需要传一个包含getset的对象:

const fullName = computed({ get: () => firstName.value + lastName.value, set: (val) => { firstName.value = val[0] lastName.value = val.slice(1) } })

watch用来监听数据变化并执行副作用,比如发请求、操作 DOM、写日志。

import { ref, watch } from 'vue' const keyword = ref('') watch(keyword, (newVal, oldVal) => { console.log(`搜索关键词从 ${oldVal} 变成了 ${newVal}`) // 这里可以发请求搜索 })

watch的几个常用配置:

  • immediate: true:立即执行一次回调
  • deep: true:深度监听对象内部的变化
  • 监听多个源:第一个参数传数组
watch([keyword, page], ([newKeyword, newPage], [oldKeyword, oldPage]) => { // 两个值任意一个变化都会触发 })

还有一个watchEffect,它会自动追踪回调里用到的响应式数据,不需要手动指定监听源:

import { watchEffect } from 'vue' watchEffect(() => { console.log(`当前关键词:${keyword.value},当前页码:${page.value}`) })

watchEffect适合那种“依赖关系明确、不需要旧值”的场景。如果需要旧值或者要精确控制监听源,用watch

4.3 生命周期钩子在组合式 API 中的写法

Vue3 的生命周期钩子在组合式 API 里变成了onXxx的形式,在setup里调用:

Options API组合式 API调用时机
beforeCreate不需要setup 本身替代
created不需要setup 本身替代
beforeMountonBeforeMount挂载前
mountedonMounted挂载后
beforeUpdateonBeforeUpdate更新前
updatedonUpdated更新后
beforeUnmountonBeforeUnmount卸载前
unmountedonUnmounted卸载后

最常用的是onMounted,用来发初始请求、初始化第三方库:

import { onMounted } from 'vue' onMounted(() => { console.log('组件挂载完成,DOM 已经可用') fetchData() })

onUnmounted用来清理定时器、取消事件监听,防止内存泄漏:

import { onMounted, onUnmounted } from 'vue' let timer = null onMounted(() => { timer = setInterval(() => { console.log('定时任务') }, 1000) }) onUnmounted(() => { clearInterval(timer) })

注意:setup的执行时机在beforeCreate之前,所以你不能在setup里访问this。所有需要this的操作,都要用组合式 API 的方式替代。

5. Vue Router 路由配置:从基础到动态路由

路由是单页应用的核心,Vue3 对应的路由版本是 Vue Router 4。这一章我讲清楚路由的配置、跳转、传参和守卫。

5.1 路由配置文件的结构与写法

create-vue创建项目时如果选了 Router,会自动生成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

几个关键点:

createWebHistory是 HTML5 History 模式,URL 里没有#,看起来更干净。但需要服务器配置支持,否则刷新页面会 404。另一种是createWebHashHistory,URL 里带#,不需要服务器配置,但不太美观。

component: () => import(...)是路由懒加载,只有访问该路由时才会加载对应的组件文件。对于大型项目,这能显著减少首屏加载体积。

name是路由名称,用于编程式导航时指定目标,比写路径更安全(路径改了名称不用改)。

5.2 声明式导航与编程式导航

声明式导航用<router-link>组件:

<router-link to="/">首页</router-link> <router-link :to="{ name: 'about' }">关于</router-link>

编程式导航用useRouter

import { useRouter } from 'vue-router' const router = useRouter() // 跳转到指定路径 router.push('/about') // 用名称跳转 router.push({ name: 'about' }) // 带参数 router.push({ name: 'user', params: { id: 123 } }) // 带查询参数 router.push({ path: '/search', query: { keyword: 'vue3' } }) // 替换当前历史记录,不能后退 router.replace('/about') // 前进或后退 router.go(-1)

pushreplace的区别:push会往历史记录里添加一条,可以后退;replace是替换当前记录,不能后退。比如登录成功后跳转首页,用replace更合适,防止用户后退回到登录页。

5.3 动态路由与参数传递

动态路由用:参数名定义:

{ path: '/user/:id', name: 'user', component: () => import('@/views/UserView.vue') }

组件里用useRoute获取参数:

import { useRoute } from 'vue-router' const route = useRoute() console.log(route.params.id) // 获取路径参数 console.log(route.query.keyword) // 获取查询参数

paramsquery的区别:

  • params是路径的一部分,比如/user/123里的123,刷新页面参数还在
  • query是 URL 问号后面的部分,比如/search?keyword=vue3,刷新页面也在
  • params传参时,如果用name跳转,参数不会显示在 URL 里(除非路由配置了:id);用path跳转则必须配置动态段

我个人的习惯是:需要分享链接或刷新后保留的参数用query,页面内部的状态传递用params

5.4 路由守卫的实际应用场景

路由守卫用来在跳转前后执行逻辑,最常用的是全局前置守卫beforeEach,用来做登录验证:

router.beforeEach((to, from, next) => { const token = localStorage.getItem('token') if (to.meta.requiresAuth && !token) { next({ name: 'login', query: { redirect: to.fullPath } }) } else { next() } })

路由配置里给需要登录的页面加meta

{ path: '/dashboard', name: 'dashboard', component: () => import('@/views/DashboardView.vue'), meta: { requiresAuth: true } }

next函数的几种用法:

  • next():放行
  • next(false):中断当前导航
  • next('/login'):跳转到指定路径
  • next({ name: 'login' }):用名称跳转

注意:next必须被调用一次,否则导航会一直挂起。另外,Vue Router 4 里next是可选的,你也可以直接return一个路由地址或return true/false,效果一样。

6. 组件通信与常见报错排查

组件通信是实际开发中绕不开的话题,这一章我讲几种常用的通信方式,以及新手最容易遇到的报错和解决方法。

6.1 props、emit 与 v-model 的双向绑定

父组件传数据给子组件用props

// 子组件 const props = defineProps({ title: String, count: { type: Number, default: 0, required: false } })

子组件传数据给父组件用emit

// 子组件 const emit = defineEmits(['update', 'delete']) const handleClick = () => { emit('update', { id: 1, name: '新名称' }) }

父组件监听:

<ChildComponent @update="handleUpdate" @delete="handleDelete" />

v-model在 Vue3 里可以绑定多个,本质是props+emit的语法糖:

<!-- 父组件 --> <ChildComponent v-model:title="pageTitle" v-model:count="pageCount" />
// 子组件 const props = defineProps(['title', 'count']) const emit = defineEmits(['update:title', 'update:count'])

6.2 provide/inject 跨层级传递

当组件层级很深,用 props 一层层传很麻烦时,用provideinject

// 祖先组件 import { provide, ref } from 'vue' const theme = ref('dark') provide('theme', theme)
// 后代组件 import { inject } from 'vue' const theme = inject('theme', 'light') // 第二个参数是默认值

provide/inject适合传递全局配置、主题、用户信息这类数据。但要注意:inject的数据默认不是响应式的,如果传的是ref,后代组件修改时需要.value

6.3 常见报错速查与解决思路

报错信息原因解决方法
Failed to resolve component组件没注册或路径写错检查 import 路径和组件名
Property "xxx" was accessed during render but is not defined模板里用了未定义的变量检查 setup 里是否声明
Cannot read properties of undefined访问了未初始化的对象属性用可选链?.或给默认值
Component is missing template or render function组件文件没有 template 或 script检查 .vue 文件内容
Vue Router: No match found for location路由路径没配置检查 routes 数组
Module not found: Error: Can't resolve '@/xxx'别名没配置检查 vite.config.js 的 alias

我遇到最多的一个报错是Failed to resolve component,通常是因为组件名大小写不一致。Vue3 的<script setup>里,组件名推荐用 PascalCase(大驼峰),模板里可以用 PascalCase 或 kebab-case。但如果用 kebab-case,要确保和 import 的名字对应上。

另一个高频问题是响应式丢失。比如从reactive对象里解构属性,或者把ref的值赋给普通变量,都会导致视图不更新。解决方法是:解构reactivetoRefs,访问ref记得加.value

6.4 开发环境与浏览器兼容性注意事项

Vue3 默认支持现代浏览器,不支持 IE11。如果你需要兼容旧浏览器,要额外配置@vitejs/plugin-legacy,但会增大打包体积。

在 Edge 浏览器里,我遇到过一个问题:Vue3 项目有时候无法关闭浏览器右上角的最小化按钮。这其实是浏览器本身的行为,和 Vue3 无关。如果你在做 Electron 或类似桌面应用,需要自己处理窗口控制。

另外,pxtorem对 ECharts 不生效的问题,是因为 ECharts 的 canvas 渲染不受 CSS 的 rem 影响。解决方法是在 ECharts 的配置里手动根据屏幕宽度计算字体大小,或者用resize事件重新渲染。

7. 从学习到实战:搭建一个后台管理系统的思路

学完基础之后,最好的巩固方式就是做一个完整的项目。后台管理系统是很好的练手选择,因为它涵盖了路由、权限、表格、表单、状态管理等核心场景。

7.1 项目结构规划与模块拆分

一个典型的 Vue3 后台管理系统可以这样拆分:

src/ ├── api/ # 接口请求封装 ├── assets/ # 静态资源 ├── components/ # 公共组件 ├── composables/ # 组合式函数 ├── layouts/ # 布局组件 ├── router/ # 路由配置 ├── stores/ # Pinia 状态管理 ├── utils/ # 工具函数 ├── views/ # 页面组件 ├── App.vue └── main.js

composables目录放可复用的组合式函数,比如useTableuseFormusePermission。这样不同页面可以共享逻辑,代码量能减少很多。

7.2 登录鉴权与路由拦截的完整实现

登录鉴权的流程:

  1. 用户输入账号密码,调用登录接口
  2. 接口返回 token,存到 Pinia 和 localStorage
  3. 路由守卫检查 token,没有就跳转登录页
  4. 请求拦截器自动在 header 里带上 token
  5. token 过期时,响应拦截器统一处理,跳转登录页

Pinia 里的用户状态:

import { defineStore } from 'pinia' import { ref } from 'vue' export const useUserStore = defineStore('user', () => { const token = ref(localStorage.getItem('token') || '') const userInfo = ref(null) const setToken = (newToken) => { token.value = newToken localStorage.setItem('token', newToken) } const logout = () => { token.value = '' userInfo.value = null localStorage.removeItem('token') } return { token, userInfo, setToken, logout } })

7.3 动态路由与权限控制的实现思路

不同角色的用户看到的菜单不一样,这就需要动态路由。思路是:

  1. 前端定义好所有路由(包括静态路由和动态路由)
  2. 登录后根据用户角色,从后端获取可访问的路由列表
  3. router.addRoute()动态添加路由
  4. 侧边栏菜单根据最终的路由表渲染
const addDynamicRoutes = (routes) => { routes.forEach(route => { router.addRoute(route) }) }

注意:动态添加的路由在刷新页面后会丢失,所以需要在路由守卫里判断,如果路由表为空,重新获取并添加。另外,404 页面要放在动态路由添加之后再匹配,否则会先匹配到 404。

8. 我踩过的坑与独家避坑经验

最后这部分,我整理一些文档里不会写、但实际开发中一定会遇到的问题。

8.1 响应式数据的常见陷阱

陷阱一:直接替换 reactive 对象reactive返回的是 Proxy,如果你直接给它赋一个新对象,响应性会丢失。正确做法是修改属性,或者用Object.assign

陷阱二:ref 在模板里自动解包,但在 setup 里不会。模板里写{{ count }}没问题,但 setup 里必须写count.value。这个新手很容易搞混。

陷阱三:watch 监听 reactive 对象的属性。如果直接watch(state.count, ...),需要写成函数形式watch(() => state.count, ...),否则监听不到。

8.2 路由跳转后组件不渲染的排查

我遇到过一个问题:路由跳转了,URL 变了,但页面内容没变。排查下来有几个原因:

  • 路由配置里component写成了components(多了个 s)
  • 父路由没有写<router-view />
  • 路由路径匹配到了但组件加载失败,检查控制台有没有报错
  • 用了router.push但目标路由和当前路由相同,Vue Router 会忽略这次跳转

解决方法:在<router-view>上加:key="$route.fullPath",强制重新渲染。

8.3 打包部署时的路径与配置问题

npm run build之后,dist目录部署到服务器,常见问题:

  • 白屏:通常是base路径配置不对。如果部署在子目录,要在vite.config.js里设置base: '/子目录名/'
  • 刷新 404:History 模式需要服务器配置 fallback 到index.html。Nginx 的配置是try_files $uri $uri/ /index.html;
  • 接口跨域:开发环境用 Vite 的server.proxy配置代理,生产环境由后端或 Nginx 处理

Vite 代理配置示例:

server: { proxy: { '/api': { target: 'http://后端地址', changeOrigin: true, rewrite: (path) => path.replace(/^\/api/, '') } } }

8.4 性能优化的几个实用技巧

  • 路由懒加载:所有页面级组件都用() => import(...)
  • 组件按需引入:Element Plus 等组件库用按需引入,不要全量引入
  • v-if 和 v-show 的选择:频繁切换用v-show,条件很少变用v-if
  • v-for 加 key:key 要用唯一值,不要用 index
  • 大列表用虚拟滚动:数据量超过几百条时,用vue-virtual-scroller之类的库
  • 图片懒加载:用loading="lazy"或 Intersection Observer

8.5 调试工具与开发效率提升

Vue DevTools 是必备的调试工具,浏览器装插件后,可以在开发者工具里看到组件树、Pinia 状态、路由信息。Vue3 需要装 Vue DevTools 6 以上版本。

另外几个提升效率的习惯:

  • <script setup>语法,代码更简洁
  • 把重复逻辑抽成 composable
  • 配置 ESLint + Prettier,保存时自动格式化
  • 用 VS Code 的代码片段(snippet)快速生成模板

我个人在实际操作中的体会是,Vue3 的学习曲线在前一周会比较陡,尤其是组合式 API 的思维转变。但一旦理解了refreactivecomputed这几个核心概念,后面写起来会越来越顺。关键是不要只看文档,一定要动手写,哪怕是把官方示例敲一遍,也比光看强得多。遇到报错不要慌,先看控制台,再查文档,大部分问题都能自己解决。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询