资讯动态

Vue 3实战全攻略:从环境配置到路由守卫与Token处理

发布时间:2026/9/17 13:24:01 来源:尧图企业网站定制
“Vue”这个词在简历和招聘市场上出现频率高到什么程度呢过去几年我筛过的前端简历里十份有八份写着“熟练使用 Vue”。但“熟练”和“能干活”之间往往差着一个从零搭项目、被各种环境报错折磨、最后成功上线全流程的距离。这篇东西不想复述官方文档而是把我带新人时走过的完整路径拆给你从 Node 安装、Vite 创建项目、Volar 配置到路由守卫、Token 处理、打包部署、m3u8 播放这类实战需求一次捋顺。适合刚接触 Vue 的入门者也适合后端同学想快速接手一个前后端分离项目时当速查手册用。1. 环境配置先让项目在你电脑上跑起来1.1 Node 版本与 npm 镜像绕不开的第一步Vue 源码和构建工具链都跑在 Node 环境上所以第一步是装 Node。这里我只有一个建议装 LTS 版本别追最新版。很多依赖包对 Node 的最高版本有兼容限制用最新版容易踩“当前 Node 版本不受支持”的提示。装完之后命令行里验证一下node -v npm -v两个命令都能输出版本号才算环境就绪。接下来强烈建议先把 npm 镜像切到国内源否则npm install卡在 node_modules 下载上是新手劝退第一杀手npm config set registry https://registry.npmmirror.com有人喜欢用 cnpm我的实际体验是cnpm 装包快但偶尔会出现依赖目录结构和原生模块不一致的问题排查起来比多用两分钟等安装还折腾。所以常规项目里只配 registry 就够了。如果你需要同时维护多个 Node 版本比如老项目用 14新项目用 18建议装一个 nvm 管理版本切换别手动卸载重装。开发机多版本并存是工作中很常见的状态nvm 能能省掉大量重复劳动。1.2 用 Vite 创建第一个 Vue 项目官方现在的脚手架已经全面切换到 Vite命令行工具是create-vue。打开终端执行npm create vuelatest它会问你项目名、是否加 TypeScript、Vue Router、Pinia、ESLint 等。如果是纯上手练习建议这些通通选 YesTypeScript 能逼你写出更严谨的代码Router 和 Pinia 是后面必然要用的ESLint 能帮你在写错的第一时间就发现。如果是公司存量项目要接 Vue看团队已有规范再决定。创建完成后进入目录安装依赖并启动cd my-project npm install npm run dev看到Local: http://localhost:5173/的输出浏览器打开能出现默认欢迎页项目就算跑起来了。注意旧教程里常见的vue createVue CLI 那条线已经不再维护新项目别再用它阶段性的技术债没意义。Vite 启动速度极快热更新也是秒级响应这对开发体验的提升是革命性的也是我在新项目里坚持用它的核心理由。1.3 编辑器与 Volar少踩一个省级大坑写 Vue 3 的时候VS Code 里一定要装 Volar 插件它的全名是 Vue Language Features (Volar)千万不要再装旧时代的 Vetur。这两个插件同时启用会把代码提示搞混乱类型推导也会失效。Vue 3 官方在创建项目时其实已经给了提示推荐使用 Volar。这里再补充两个配套插件TypeScript Vue Plugin (Volar)用于支持*.vue文件中的 TS 服务。Vue Devtools 浏览器扩展调试组件状态、路由和 Pinia store 时非常关键。遇到代码不报错但类型提示全红、defineProps不识别等情况十有八九是插件装错或者没生效。所以环境配置这块编辑器插件的重要性不亚于 Node 本身。我之前带过一个新人代码复制过来没问题就是提示一直报错排查到最后发现装的是 Vetur社区里这种案例实在太多。1.4 初次安装依赖时的安全提示解析ignored build scripts执行npm install经常看到一大段黄色警告比如ignored build scripts: cpu-features0.0.10, esbuild0.21.5, ssh21.17.0很多新手看到这行就慌了以为是安装失败了。其实这是 npm 为了防止恶意安装脚本而默认忽略某些原生模块的 postinstall 脚本。对 Vite 全家桶来说esbuild 被忽略 build 脚本一般不影响正常使用项目照常能启动。只有当某些依赖真正需要编译原生代码且报错时再针对性处理例如执行npm rebuild esbuild这个知识点我专门列出来是因为“安装失败”的恐慌感会浪费大量时间而实际上根本没事。真正的安装失败会有 exit code 非 0并且终端会明确告诉你缺了什么模块那才需要去查。2. 核心语法与组件化Vue 到底在写什么2.1 模板、响应式与 ref/reactive 的选择Vue 3 的组合式 API 里最核心的就是ref和reactive。一个简单落地示例script setup import { ref, computed } from vue const count ref(0) const double computed(() count.value * 2) function add() { count.value } /script template div p{{ count }} 的两倍是 {{ double }}/p button clickadd加一/button /div /template模板里{{ }}插值: 属性是动态绑定click是事件绑定v-model是双向绑定。这些语法占总使用量的一半以上先把它们玩熟就够日常开发。ref和reactive的区别我用一句话总结基本类型用ref对象/数组如果整个替换就用reactive但如果你需要给对象动态新增属性这种情况在联调时特别多那用ref包对象反而更顺手因为ref内部本质也是reactive赋值时用.value更统一。团队规范里如果统一用ref也能减少大家纠结的成本。2.2 组件通信props、emit 与透传组件化开发是 Vue 的灵魂。父组件向子组件传数据用props子组件通知父组件用emit。但有一个很多人没搞懂的概念是“透传”子组件根节点会自动继承父组件传下来但没有声明为 props/emits 的属性和事件。比如我封装一个基础输入框script setup defineProps([modelValue]) defineEmits([update:modelValue]) /script template input :valuemodelValue input$emit(update:modelValue, $event.target.value) classbase-input / /template父组件这样用BaseInput classbig-input placeholder请输入 focusonFocus /class、placeholder、focus这些东西会自动落到子组件的input根元素上这就是透传。理解透传之后你再看很多 UI 组件库为什么能“无脑传参”逻辑就通了。跨层级通信用provide/inject适合隔代传数据如果子组件想暴露方法给父组件调用用defineExpose。通信方式很多记住一个原则能用 props 解决就别上全局状态否则代码维护成本指数上升。2.3 生命周期与 Composable把逻辑抽出来复用Vue 3 的生命周期钩子改了名字created没了beforeMount改成onBeforeMount挂载后改成onMounted。在组合式 API 里你可以把这些钩子放进一个独立函数这个函数就叫 Composable组合式函数其实也就是自定义 hook。比如import { ref, onMounted, onBeforeUnmount } from vue export function useWindowSize() { const width ref(window.innerWidth) function update() { width.value window.innerWidth } onMounted(() window.addEventListener(resize, update)) onBeforeUnmount(() window.removeEventListener(resize, update)) return { width } }组件里只需要const { width } useWindowSize()就可以在任何组件复用这段逻辑。Composable 是 Vue 3 最值得学的设计模式它让逻辑复用从“mixin 黑魔法”变成了普通函数调用这也是 Vue 3 相比 Vue 2 的核心优势之一。顺带说一句面试里总被问的 Angular 和 Vue 的区别Angular 是一个完整的框架强制 TypeScript依赖注入体系庞大项目复杂度高了之后约束力强但也更重Vue 是渐进式框架你可以只写一个组件、也可以搞一整个大型应用学习曲线更平缓。团队小、节奏快、希望快速出活选 Vue 很顺手如果公司平台大、类型要求严苛Angular 也有它的生存空间。这个差异直接回答“为什么选 Vue”这类面试题给出清晰的技术取舍逻辑会更加分。2.4 样式作用域与 UI 组件库选型style scoped是 Vue 单文件组件的标配。它的原理是编译时给元素加一个>:deep(.inner-box) { background-color: #f5f5f5; }UI 库选型上新项目我一般推荐 Element Plus 或 Naive UI。有人会问老牌的 layui 能不能配合 Vue 用。我的结论是不推荐。layui 的设计理念是直接操作 DOMVue 是基于虚拟 DOM 的数据驱动框架两者的思维模式天然冲突硬凑在一起会出现大量不可预期的渲染问题。懂 jQuery 时代的老开发可能对 layui 有感情但技术选型不是讲情怀Vue 生态内已经有足够成熟的组件库没必要为了一个熟悉的名字增加技术债。3. 路由与状态管理让页面真正“动”起来3.1 Vue Router 基础与路由参数的两种写法单页面应用必须有路由。创建项目时选上 Vue Router 之后项目里会生成router/index.ts文件。核心 API 就几个createRouter、createWebHistory、route、router。路由参数最让人混淆的是params和query我拆开细说params对应path风格比如路由定义为/user/:id跳转时用router.push({ name: User, params: { id: 1 } })页面上用route.params.id取。query对应查询串跳转时用router.push({ path: /user, query: { id: 1 } })页面上用route.query.id取。我的经验是查询条件、可选的筛选信息用query路径上必须存在的身份标识用params。两者混用没问题但别把重要参数放query因为用户刷新页面后query还在但如果你把对象整个塞进queryURL 会变得巨丑且可能丢失类型。嵌套路由使用children数组配合router-view /展示子页面。它和 Tab 页签、侧边栏布局的配合是后台管理系统最常见的需求一个布局组件下面挂多个子页面路由。3.2 路由拦截器与登录权限控制后台系统几乎都要做登录校验最简单也最常用的方案就是全局前置守卫import { createRouter, createWebHistory } from vue-router const router createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes: [ { path: /login, component: () import(/views/Login.vue) }, { path: /dashboard, component: () import(/views/Dashboard.vue), meta: { requiresAuth: true } } ] }) router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.meta.requiresAuth !token) { next({ path: /login, query: { redirect: to.fullPath } }) } else { next() } })这个模式能解决 90% 的登录拦截需求。再往上就是基于角色的动态路由用户登录后后端返回角色和权限列表前端用router.addRoute动态挂载可访问的路由同时配合自定义指令做按钮级权限控制。如果你拿到的是若依这类开源项目你会在代码里看到完整的一套动态路由 权限指令实现把它的权限部分读一遍相当于免费上了一次企业级权限设计课。3.3 Pinia vs Vuex新项目怎么选Vuex 统治了 Vue 2 时代但 Vue 3 时代官方已经明确推荐 Pinia。我在新项目里也全部用 Pinia主要对比看这张表维度PiniaVuex官方定位Vue 3 默认推荐维护模式适配旧项目API 风格灵活Composition Options 均可严格 Options 风格模板化TypeScript 支持类型推导强几乎零心智负担类型定义繁琐容易写出一堆重复类型模块化每个 store 独立定义天然按需需要 modules 配置集中管理调试工具Devtools 支持全面Devtools 支持全面Pinia 创建一个 store 非常简单// stores/counter.js import { defineStore } from pinia export const useCounterStore defineStore(counter, { state: () ({ count: 0 }), getters: { double: (state) state.count * 2 }, actions: { increment() { this.count } } })组件里直接const counter useCounterStore()再用storeToRefs解构出响应式数据就不会丢响应性。持久化配合pinia-plugin-persistedstate插件把 token、用户信息这些状态自动存到 localStorage刷新不丢登录态体验很好。核心原则还是那句普通组件通信、页面间传参能自己搞定的就别上 Pinia。全局状态是最后手段用多了项目就会变得难以维护。3.4 复杂业务组件的集成套路地图、图表、自定义组件有搜索热词提到“用在 vue 里的腾讯地图”“矩阵树图 vue”“sql-viewer”“多周选择组件”这类需求本质相同把第三方 JS 库整合到 Vue 响应式系统里。以腾讯地图为例最省事的做法是直接在index.html引入地图 JS SDK然后在组件里这样初始化script setup import { onMounted, onBeforeUnmount, ref } from vue const mapContainer ref(null) let mapInstance null onMounted(() { mapInstance new window.TMap.Map(mapContainer.value, { center: new window.TMap.LatLng(39.9, 116.4), zoom: 12 }) }) onBeforeUnmount(() { mapInstance null }) /script template div refmapContainer styleheight: 400px / /template这个ref模板引用拿到的是真实 DOM 节点第三方库要求在真实 DOM 上初始化所以必须等onMounted之后再操作。ECharts 做图表、AntV G6 做关系图套路完全一样引入资源、onMounted初始化、数据变化时调用实例的setOption或updateData卸载时销毁实例。摸透这一套任何第三方库在 Vue 里都能顺手集成。4. 前后端联调实战接口、Token 与构建部署4.1 封装 axios拦截器统一处理 Token 与错误前后端分离项目里axios 是事实标准的 HTTP 客户端。但我建议每个人都封装一层不要每个页面直接axios.get。核心代码就一段// utils/request.js import axios from axios import { ElMessage } from element-plus import router from /router const request axios.create({ baseURL: /api, timeout: 10000 }) request.interceptors.request.use((config) { const token localStorage.getItem(token) if (token) { config.headers.Authorization Bearer ${token} } return config }) request.interceptors.response.use( (response) response.data, (error) { if (error.response?.status 401) { localStorage.removeItem(token) router.push(/login) } ElMessage.error(error.response?.data?.message || 请求失败) return Promise.reject(error) } ) export default request请求拦截器负责给每个请求自动带 token响应拦截器负责统一处理 401 跳登录、弹错误提示。这样业务代码里就不需要到处判断if (res.code ! 200)了。token 过期自动踢回登录页是“前后端分离请求 token 处理”最常见的硬需求这个封装直接解决。实际项目中如果后端要求刷新 token就在响应拦截器里加一个“用 refreshToken 换新 token 后重发队列”的逻辑前期可以不加但设计上要留好扩展点。4.2 代理与跨域开发环境和生产环境分别怎么配开发环境最常见的模式是 Vite 代理。前端跑在5173后端跑在8080浏览器直接请求8080会跨域但通过 Vite 代理把/api开头的请求转发给后端就不存在跨域了// vite.config.ts export default defineConfig({ server: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } })changeOrigin: true的作用是让后端看到的请求来源变成目标地址避免某些后端框架对 Host 做校验时误判。后端那边Spring Boot 项目可以用CrossOrigin注解或全局 CorsFilter 配置允许来源FastAPI 项目则是在中间件里配置 CORSMiddleware 的allow_origins。生产环境通常由 Nginx 把/转发到前端静态文件、把/api反向代理到后端服务前端代码里不需要任何类似http://localhost的硬编码。4.3 WebSocket 集成心跳、断线重连与组件卸载Vue 项目里加 WebSocket核心不是 WebSocket 本身而是它的生命周期管理。我常用一个简单的 composable 来封装// composables/useWebSocket.js import { onMounted, onBeforeUnmount, ref } from vue export function useWebSocket(url) { const isConnected ref(false) let ws null let heartbeatTimer null let retryTimer null function connect() { ws new WebSocket(url) ws.onopen () { isConnected.value true heartbeatTimer setInterval(() ws.send(ping), 30000) } ws.onmessage (event) { // 这里交给页面处理具体业务消息 } ws.onclose () { isConnected.value false clearInterval(heartbeatTimer) retryTimer setTimeout(connect, 3000) } } onMounted(connect) onBeforeUnmount(() { clearTimeout(retryTimer) ws?.close() }) return { isConnected } }心跳每 30 秒发一次保证连接不被服务端主动断开断线后 3 秒自动重连组件卸载时清理定时器和连接避免内存泄漏。这套代码几乎是所有即时通讯、实时推送页面的通用骨架。4.4 打包发布从npm run build到 Nginx开发完成后打包命令是npm run build产物在dist/目录里里面是纯静态文件。发布方式根据团队基础设施不同而不同但核心思路一致。我在实际项目里最常遇到两类情况第一种前后端一起部署用 Nginx 托管前端并把 API 反向代理到后端。这类项目里最容易踩的坑是 history 模式路由刷新 404解决办法是 Nginx 配置里加location / { try_files $uri $uri/ /index.html; }第二种整包打到一个服务里比如若依这类 Spring Boot Vue 项目前端dist目录直接复制到后端src/main/resources/static下随 Spring Boot 一起启动。注意改前端静态资源路径时要设置base: ./或对应路径前缀否则打包后 CSS/JS 路径不对页面直接白屏。“vue 打包后布局异常”这个热搜词我多说一句先看浏览器控制台有没有静态资源 404再看是不是 history 路由刷新导致再看是不是某些按需引入的组件库样式没有完整引进来。90% 的情况都能在这三样里找到答案。5. 新手绕不开的坑报错排查与实用技巧5.1 依赖安装阶段esbuild、node-gyp 与缓存问题依赖报错是新手最大的劝退点。整理一个高频速查表报错关键词常见原因处理办法ignored build scripts: esbuildnpm 安全策略跳过了 postinstall通常无影响若构建报错则npm rebuild esbuildnode-gyp相关错误需要本地编译原生模块安装对应系统编译工具或换用预编译版本EACCES权限错误全局安装没有权限不要用 sudo 硬来用 nvm 管理 Node 再重装npm install卡住网络问题或缓存脏了切换镜像源npm cache verify删除 node_modules 后重装其中最常见的还是网络问题。切换 registry 后依然慢可以考虑用npm install --registryhttps://registry.npmmirror.com临时指定不必全局改配置。5.2 开发运行与打包阶段白屏、样式错乱、路由 404运行阶段的白屏先打开 Vue Devtools 看根组件是否挂载成功如果是 mounted 之后依然全白再看控制台具体报错。常见情况是某个 API 在组件渲染时返回了 undefined模板里直接访问user.avatar导致报错中断渲染。样式错乱问题主题集中在 scoped 和第三方组件上。比如全屏弹窗组件因为默认挂载到body下scoped 样式打不到它内部解决方案是组件库一般提供了append-to-body或自定义 class 透传能力需要用到:deep()穿透去覆盖样式。还有一个隐藏很深的坑是动态 import 路径写错。Vue 路由里我喜欢用() import(/views/xxx.vue)懒加载但如果你手滑把路径写错开发环境不报错、打包时才报Could not resolve排查思路是完整看构建日志不要只看第一条红色文字。5.3 业务场景三件套m3u8 视频、Excel 导出、i18n 插 HTML这三个是搜索热词里出现频率很高的具体场景。m3u8 视频播放浏览器原生不支持 HLS 流所以需要借助 hls.js逻辑极简单import Hls from hls.js const video videoRef.value if (Hls.isSupported()) { const hls new Hls() hls.loadSource(http://example.com/playlist.m3u8) hls.attachMedia(video) } else if (video.canPlayType(application/vnd.apple.mpegurl)) { // Safari 原生支持 video.src http://example.com/playlist.m3u8 }多个表格导出一个 Excel用xlsx库创建 workbook 后循环添加 sheetimport * as XLSX from xlsx const wb XLSX.utils.book_new() tables.forEach((table, index) { const ws XLSX.utils.json_to_sheet(table.data) XLSX.utils.book_append_sheet(wb, ws, Sheet${index 1}) }) XLSX.writeFile(wb, 多表导出.xlsx)vue-i18n 里插入 HTML 标签直接写在翻译文案里的a标签会被当作纯文本转义不会真正渲染成链接。正确做法是把标签片段作为插值变量传入再用v-html渲染import { computed } from vue import { useI18n } from vue-i18n const { t } useI18n() const tipHtml computed(() t(tip, { link: a href/detail这里/a }) )span v-htmltipHtml/span强调一点v-html里绝不能直接塞用户输入的内容如果内容里包含用户填写的 name、comment 这类字段一定要先做 HTML 转义再拼接否则 XSS 漏洞在所难免。团队里如果安全审计严格更稳的方案是用组件插槽替代v-html。5.4 面试速览Vue 2 vs Vue 3、响应式原理、key 的作用很多人上手 Vue 时正好赶上找工作面试题总绕不开这几个维度Vue 2 和 Vue 3 的核心区别响应式系统从Object.defineProperty换成Proxy解决了数组下标更新、动态新增属性不响应的问题组合式 API 替代了部分 Options API 的职责虚拟 DOM 重写diff 算法更快更友好的 Tree-Shaking项目体积更小。响应式原理Vue 3 用Proxy代理对象拦截get和set在get时收集依赖在set时触发更新。这个“依赖收集 派发更新”的模型就是响应式系统的核心。v-for为什么需要keykey 是虚拟 DOM diff 时的唯一标识。没有 key 时列表更新只能按顺序比较导致状态错乱有了 keyVue 可以精确复用和移动节点。模板里提供了 index 作为 key虽然能跑但删除中间项时性能会明显下降一般用业务 id 最合适。computed 与 watch 的区别computed 是派生状态有缓存依赖不变就不重新计算watch 是副作用监听适合执行异步操作、请求接口这类场景。其实掌握这些知识点不需要死记硬背把这些机制当过手代码里实际遇到的性能问题、响应丢失问题去理解面试时反而能讲得更有说服力。说实话Vue 这套东西上手真不难难的是把环境问题、工程化细节、联调流程挨个踩完。我见过很多新人第一周就卡在npm install、卡在路由跳转 404、卡在 token 失效不知道怎么处理最后就开始怀疑自己适不适合写代码。我的建议是遇到问题先读一遍英文报错原文再去搜报错里的关键片段少用中文整段复述去搜索。前端社区非常活跃你遇到的几乎每一个报错都已经有人贴过完整解决方案。把本文推荐的环境、路由拦截、axios 封装、WebSocket 封装这几个模板工程搭起来剩下的业务代码无非就是参照同样的套路往里填内容。最后再分享一个小技巧刻意给自己布置一个“从 0 到 1 搭一个带登录、路由守卫、接口联调的后台管理页面”的小项目不依赖任何脚手架模板手写一遍等你写完之后Vue 的入门关卡基本就全过了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价