Vue 3全流程开发实战:从环境搭建到部署优化

Vue 3全流程开发实战:从环境搭建到部署优化 从最开始用 jQuery 写页面交互到后来第一次在项目里引入 Vue那种“数据变了页面自动跟着变”的体验确实会让人一下子上头。用了三年多 Vue从 Vue 2 一路跟到 Vue 3踩过不少坑也沉淀了不少自己的心得这篇就把我做网站开发时积累的经验好好梳理一遍希望对正在学习或者刚上手 Vue 的朋友有点帮助。这文章不只是讲语法更多是我在实际项目里的试错记录。会从环境搭建讲到项目初始化、从路由状态管理讲到前后端联调再到打包部署和面试高频点基本就是你从零开始用 Vue 开发一个完整网站会经历的全流程。无论你是刚装好 Node 准备踩坑的新手还是已经写过一段时间想系统补补短板的同学都可以参考看看。1. 环境准备与项目初始化把这些基础打牢后面能省大量时间很多新手学 Vue 一开始就卡在装环境上看着一堆报错就放弃了。其实这块没什么玄学把版本和镜像两个关键点搞定后面就是一路顺畅。1.1 Node.js 版本与 npm 镜像源配置Vue 的开发工具链离不开 Node.js。官方脚手架、Vite、Webpack、各种插件都是跑在 Node 环境上的。需要注意的是Node 的版本不能太旧Vue 3 的官方脚手架 Vite 要求 Node 版本至少是 18如果你本地还是 Node 16 甚至更老的版本启动项目很可能会直接报“You are using Node version xxx, but Vite requires Node version 18”之类的提示。我个人的建议是直接安装 Node 的 LTS长期支持版本不用特意追最新版。LTS 版本稳定性有保障大部分工具链都兼容。另外就是 npm 的镜像源国内直接使用默认源下载依赖经常卡在某个包上半天不动换成淘宝镜像源是大多数人的选择在终端里执行这一句就能永久生效npm config set registry https://registry.npmmirror.com换完之后可以通过npm config get registry确认一下。这一步做完后面npm install的体验会好非常多。实际上我后来用 pnpm 比较多它在依赖安装速度和磁盘占用上比 npm 更好Vue 官方生态也默认支持新手不想折腾的话直接用 npm 也完全够用。另外说一句 Yarn如果你的项目用了 Yarn 1.x 或者新版 Yarn Berry要注意命令差异和 PlugnPlay 带来的 Node 模块解析问题。初期建议尽量统一使用 npm 或 pnpm避免多个包管理器混用导致node_modules出现莫名其妙的问题。1.2 使用 Vite 创建 Vue 项目的几种方式现在创建 Vue 项目主流推荐的方式是 Vite。Vite 的开发服务器启动速度极快热更新也几乎是无感的相比以前 Webpack 动不动要等好几秒甚至十几秒才能看到改动效果体验好了不止一个档次。最经典的一条命令是这样的npm create vuelatest这个命令会唤起 Vue 官方的脚手架工具过程中会让你选择是否安装 TypeScript、Vue Router、Pinia 等你需要的依赖全程交互式操作非常友好。我在实际创建项目时一般会勾上 Vue Router 和 PiniaTypeScript 看项目要求如果团队都还没用过 TS可以先不加避免上手成本太高。如果你用的是 VS Code 写 Vue记得务必安装官方推荐的Vue Language Features (Volar)插件并且把旧版 Vetur 插件禁用掉不然两个插件同名功能会有冲突代码提示和格式化会变得很诡异。Vue 3 项目中对这个有明确推荐新项目还是建议直接用 Volar 建立起肌肉记忆。1.3 安装依赖时常见的几个报错与处理依赖安装这步很多新手会在npm install时撞到报错我自己也碰到过好多次。这里把几种常见情况对应解法整理出来报错现象常见原因解决办法ERESOLVE unable to resolve dependency tree依赖版本冲突使用npm install --legacy-peer-deps绕过 peerDependencies 的严格校验ESOCKETTIMEDOUT或网络卡住网络问题或镜像源访问慢切换镜像源或使用pnpm install重试ignored build scripts: esbuild...pnpm 默认不执行依赖的构建脚本在pnpm-workspace.yaml或package.json中配置pnpm.onlyBuiltDependencies把需要的包加入白名单node_modules太乱装啥都报错依赖缓存损坏或残留旧包删除node_modules和锁文件重新安装其中那个ignored build scripts的提示很典型尤其是 esbuild 这类原生模块包它在安装时需要执行 postinstall 脚本去下载或构建二进制文件如果这个步骤被忽略了启动项目时可能会报esbuild: Command failed之类的错误。处理方式是找到项目根目录下的pnpm-workspace.yaml或者在package.json里加一个字段把esbuild加进允许执行构建脚本的列表里。新手容易忽略的一点是依赖安装完成之后最好去node_modules里确认一下关键包是否存在。有些时候安装日志看着没报错但因为各种原因真的有包没装上深查浪费时间不说还容易让人误判是代码问题。2. 项目架构设计路由、状态管理和组件化的思路沉淀环境搭建好之后就进入写代码阶段了。一个 Vue 项目能不能长期好维护架构设计比写业务代码更重要。我自己的体会是路由和状态管理这两块从一开始就要规划清楚不然后面改起来特别痛苦。2.1 路由基础到进阶传参、嵌套路由和路由守卫Vue Router 是 Vue 项目里几乎必用的路由库。基础用法很简单就是在router/index.js里定义路由表然后通过RouterLink和RouterView /控制页面跳转和展示。但真正写业务的时候很快会遇到几个进阶问题。路由传参这块是最多人问的。Vue Router 4 支持两种传参方式query 方式和 params 方式。query 方式是router.push({ path: /detail, query: { id: 1 } })参数会挂在 URL 后面页面刷新后参数还在params 方式的典型写法是router.push({ name: detail, params: { id: 1 } })需要配合路由表里的:id占位符使用比如/detail/:id。这里有个坑要提醒params 方式在页面刷新时参数很容易丢失因为在 history 模式下刷新后路由重新解析如果没有把参数拼进 URL 的路径里组件内是拿不到参数的。所以我的原则是——需要持久化的参数一律用 query 或路径参数params 只用来传递刷新无关紧要的临时数据。路由守卫是控制页面访问权限的核心手段。比如一个需要登录才能访问的后台页面可以在全局前置守卫router.beforeEach里判断是否登录没有登录就重定向到登录页。还有动态设置页面标题、埋点上报这些需求也都可以在守卫里统一处理。实际项目中我用得最多的是全局守卫和路由内的beforeEnter按需选择即可不需要在所有地方都用守卫的写法平铺。2.2 状态管理选型Vuex 还是 Pinia很多初学者在面试或实际项目中都会纠结 Vuex 和 Pinia 怎么选。简单直接地说Vue 3 新项目直接选 Pinia没有特殊情况不需要再用 Vuex。Pinia 在 API 设计上更接近 Vue 3 的组合式 API 风格使用起来更直观简洁。它去掉了 Vuex 中 mutations 这些让很多人绕晕的概念同步更新状态直接通过store.count或调用 action 方法就行。而且 Pinia 是 Vue 官方团队维护的Vue 3 生态里它就是标准方案。下面是一个简单的 Pinia store 示例import { defineStore } from pinia export const useUserStore defineStore(user, { state: () ({ token: localStorage.getItem(token) || , userInfo: null }), actions: { setToken(token) { this.token token localStorage.setItem(token, token) }, logout() { this.token this.userInfo null localStorage.removeItem(token) } } })Pinia 的数据响应式是基于 Vue 3 的ref和reactive实现的所以在组件里使用 store 时不能直接解构 state否则会丢失响应性要用它提供的storeToRefs工具来保留响应式特性import { storeToRefs } from pinia const userStore useUserStore() const { token, userInfo } storeToRefs(userStore)如果你还在维护 Vue 2 老项目那一般还是继续用 Vuex 4 比较稳妥。如果是新项目别犹豫上 Pinia 就对了。面试的时候如果被问到两者区别重点说清“Pinia 更简洁、去掉了 mutations、完全支持 Composition API、类型推断更好”这几个点就足够了。2.3 组件化设计从目录结构到可复用组件的思考组件化是 Vue 开发的核心思想之一。简单来说就是把页面拆成一个一个独立、可复用的组件维护和复用都方便。我比较推荐的组件目录结构是下面这样的src/ components/ # 公共组件 BaseButton.vue BaseTable.vue views/ # 页面级组件 Home/index.vue User/Detail.vue composables/ # 组合式函数 useTable.js usePermission.js api/ # 接口请求模块 router/ # 路由配置 stores/ # 状态管理 utils/ # 工具函数在 Vue 3 里我更推荐用组合式函数Composable Function来抽取可复用的逻辑。比如一个表格页的加载、筛选、翻页逻辑在多个页面都用到了那我就提炼成一个useTable函数每个页面只需要一行调用就获得整套表格处理逻辑减少重复代码。组件通信方式也需要清楚父传子用props子传父用emit跨层级或兄弟组件共享状态用 Pinia 或 provide/inject。不要一上来就在所有场景都用 Pinia简单场景用 props 和 emit 就够了状态管理工具虽然强大但过度依赖反而会让数据流变得不好追踪。3. 前后端分离场景下的实践接口请求、鉴权与特殊功能实现现代网站开发基本都是前后端分离前端代码通过 HTTP 接口与后端交互。这一章聊几个实际项目中绕不开的硬核场景。3.1 封装请求与 Token 处理机制前端请求后端接口一般需要统一封装一个 HTTP 请求实例大家最常用的就是 axios。封装的核心是统一设置 baseURL、超时时间、请求拦截器和响应拦截器。请求拦截器里做的最多的事情就是附加 Token。比如从 Pinia 或本地存储中取出 token然后设置到请求头的Authorization字段import axios from axios import { useUserStore } from /stores/user const request axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL, timeout: 15000 }) request.interceptors.request.use(config { const userStore useUserStore() if (userStore.token) { config.headers.Authorization Bearer ${userStore.token} } return config })响应拦截器里主要处理两种情况业务错误码提示和 401 未登录。服务端一般会约定一个返回格式比如{ code: 200, data: ..., message: ok }。当 code 不是 200 时用 UI 组件库的 message 提示错误信息当 code 是 401 时一般意味着 token 失效或过期此时应该清除本地登录态并跳转登录页这在后台管理系统里非常重要——你不希望用户 token 过期后还在页面里“看起来已登录”直到点某个按钮才突然跳登录页体验很差。我在实际开发中还习惯把接口地址按模块抽到不同的文件里比如user.js、order.js每个模块导出一个方法页面里直接引入调用。这样当接口地址变更时只需要在一个文件里改不用全局搜罗字符串。3.2 多表格数据合并导出一个 Excel 文件后台管理系统中“导出 Excel”是高频需求尤其是多个表格数据合并到一个文件里的情况。这里分享一个我验证过很稳的方案用xlsx库自己拼数据而不是依赖后端生成文件。思路很简单先分别获取各类数据然后构建工作簿对象每张表用一个工作表Sheet承载最后写入成一个文件import * as XLSX from xlsx // 假设有多组表格数据 const tables [ { name: 用户列表, rows: userRows }, { name: 订单列表, rows: orderRows } ] const wb XLSX.utils.book_new() tables.forEach(({ name, rows }) { const ws XLSX.utils.json_to_sheet(rows) XLSX.utils.book_append_sheet(wb, ws, name) }) XLSX.writeFile(wb, 数据导出_${Date.now()}.xlsx)如果你对表头有特殊要求比如合并单元格、设置列宽也可以在ws上手动调整。这个方案的好处是纯前端处理逻辑直观不受后端限制。需要注意的一点是数据量大的时候直接在前端一次性导出可能会导致页面卡顿或内存溢出。几千行问题不大如果几万行甚至更多建议做分页拉取或分批追加行数据也可以先只导出当前筛选后的数据在导出按钮上给出友好限制说明。3.3 特殊业务场景视频 m3u8 播放、地图与扫码项目里前端的特殊需求通常很杂我挑几个大家搜索量比较大的分享下经验。m3u8 视频播放。m3u8 是 HLS 视频流格式浏览器原生video并不直接支持播放需要依赖 hls.js 库来转码播放。用 Vue 封装时通常在onMounted里初始化Hls实例并做浏览器兼容处理Safari 原生支持 HLS可以直接指定 srcimport Hls from hls.js const videoRef ref(null) const playM3u8 (url) { if (Hls.isSupported()) { const hls new Hls() hls.loadSource(url) hls.attachMedia(videoRef.value) } else if (videoRef.value.canPlayType(application/vnd.apple.mpegurl)) { videoRef.value.src url } }注意跨域问题m3u8 及其分段 ts 文件所在的服务端一般要允许跨域请求否则会报网络错误。腾讯地图接入 Vue。如果做的是本地生活或物流相关项目地图是刚需。腾讯地图在 Vue 项目里的接入方式就是加载其 JavaScript SDK然后在地图容器 DOM 挂载后初始化const map new TMap.Map(document.getElementById(map), { center: new TMap.LatLng(39.908860, 116.397390), zoom: 15 })无论是腾讯地图还是百度地图都建议封装成独立的组件把初始化逻辑收敛进去。组件卸载时记得销毁地图实例避免内存泄漏。扫码功能。HTML5 调用摄像头扫码在 Vue 里推荐使用html5-qrcode库。它支持摄像头实时扫码和图片识别用法也很简单在onMounted里启动扫描器并等待二维码识别结果回调即可。需要注意 HTTPS 环境或者 localhost 才能调起摄像头权限生产环境如果用 HTTP 访问摄像头权限会被浏览器直接拦截。4. 打包部署阶段布局异常、路由模式与常用调试技巧开发环境一切正常一打包上线就出问题这是新手和中级开发最容易头疼的阶段。这里分享一些我的排查方法和经验。4.1 打包后布局异常的原因与排查思路Vue 项目npm run build之后页面样式错乱、图片不显示、接口 404 是高频问题。绝大多数情况下原因出在静态资源路径上。Vite 默认打包出来的静态资源路径是绝对路径/assets/xxx如果你的网站部署在域名根路径下比如https://example.com/那没问题但如果你部署在子路径下比如https://example.com/admin/那么所有资源路径都会 404页面自然就“裸奔”了。解决办法是在vite.config.js里设置 baseexport default defineConfig({ base: process.env.NODE_ENV production ? /admin/ : / })如果你用了 Vue Router还有一点容易被忽略Router 的createWebHistory模式在部署到 Nginx 等服务器时需要额外配置 try_files 回退到index.html否则用户直接访问https://example.com/admin/dashboard这个地址时服务器找不到对应文件会返回 404。不想折腾服务器配置的话可以用createWebHashHistoryURL 里会多个#刷新和回退都不会出问题不过 URL 不太美观看自己取舍。4.2 Vite 构建配置优化与前端性能提升打包部署不止是解决“能打开”的问题还要考虑构建速度和用户加载体验。我在项目中做过的几项优化可以参考代码分割策略。默认情况下npm run build会把所有依赖打进一个很大的 JS 文件首屏加载自然慢。可以在vite.config.js里配置manualChunks把 vue、vue-router、pinia、组件库等基础库分离出来build: { rollupOptions: { output: { manualChunks: { vue-vendor: [vue, vue-router, pinia], antd-vendor: [ant-design-vue] } } } }图片和静态资源压缩。Vite 对小于某个阈值的图片会自动转 base64减少请求数但大图片还是建议用 CDN 或压缩工具处理。组件库按需引入。我用 Ant Design Vue 或 Element Plus 时会使用它们提供的按需导入插件避免把整个组件库打进包里。这样首屏体积能缩小不少。4.3 Vue Devtools 与前端调试实战调试是开发效率和问题排查的最重要一环。Vue 开发的标配工具就是 Vue Devtools它可以直观地查看组件树、路由、状态管理和事件触发。无论你用 Chrome 还是 Edge直接在扩展商店搜索“Vue Devtools”下载安装即可它会自动识别页面中的 Vue 实例。Vue 3 项目要下载对应版本Vue 3 版本是 Vue Devtools 6 以上图标会有个 Vite 标识Vue 2 项目用旧版本即可。安装后打开项目页面开发者工具里会出现 Vue 面板可以查看组件的 props、data、computed 值和事件列表排查问题非常方便。如果页面中某段逻辑不生效最有效的思路是分步打console.log或断点定位。我在排查过程中发现很多问题的根源其实是数据没更新或数据格式不对而不是 Vue 的响应式没生效因此先确保拿到的数据符合预期再检查视图逻辑能省下大量时间。5. 面试高频考点与技能沉淀从会用到用得明白学了 Vue 之后大家基本都会去看看面试题、整理自己的知识体系。这里结合我自己面试别人和准备面试的经历分享一些真正重要的知识点。5.1 高频面试题与背后的原理思考v-if 和 v-show 的区别。面试必问题。v-if 是“真正的”条件渲染不满足条件时元素不会出现在 DOM 上v-show 只是切换 CSS 的display属性元素始终在 DOM 中。所以频繁切换的场景用 v-show 更好初始渲染条件基本不变、切换频率低的场景用 v-if 能够减少初始 DOM 体积。这个问题的背后考察的是对浏览器渲染机制和 Vue 编译原理的理解。Vue 生命周期。Vue 3 的选项式 API 中生命周期包括beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeUnmount、unmounted。组合式 API 中前面提到的时间点包括onBeforeMount、onMounted、onBeforeUnmount等。面试中如果问“在 created 和 mounted 中请求接口有什么区别”本质上是看你明不明白这两个阶段 DOM 是否已经渲染完成。created 时 data 已经初始化但 DOM 还没挂载mounted 时才能操作 DOM 或初始化依赖 DOM 的库。computed 和 watch 的区别。computed 是计算属性依赖响应式数据变化而自动重新计算有缓存机制watch 是侦听器监听某个数据变化然后执行一段逻辑不做缓存。能用 computed 的地方就不用 watch一方面性能好另一方面代码也更简洁。比如根据名字和年龄拼接一个展示文本若用 watch 需要额外定义一个变量并在回调里赋值而 computed 一行搞定这就是很好的对比。Vue 2 和 Vue 3 的核心区别。面试官问这个主要想看你对技术的关注度。核心区别包括响应式原理从 Object.defineProperty 变成 Proxy解决了对象新增属性、数组索引修改无法侦测的问题组合式 API 让逻辑复用更高效Vite 带来了完全不同的开发和构建体验TypeScript 支持更好。如果只让我说一点我会说“组合式 API 和 Proxy 响应式”是 Vue 3 相比 Vue 2 最本质的晋级。Angular 和 Vue 的区别。这个也经常被问到。简单概括Angular 是完整框架内置依赖注入、RxJS、模块系统适合大型复杂企业应用但也因此学习曲线陡峭Vue 是渐进式框架核心只关心视图层上手简单生态灵活可以从一个小页面扩展到大规模应用。选型上没有绝对的好坏团队技术储备和维护成本才是最重要的考量。5.2 从阅读开源项目到构建自己的 Vue 技能树光看文档写 demo 进步很有限我自己提高最快的一段时间是反复阅读成熟开源项目的源码和结构。比如知名的快速开发框架“若依”RuoYi就有 Vue 版把它的前后端项目拉下来在 IDEA 里跑起来认真研究它的权限路由、字典管理、代码生成器这些模块是怎么实现的会非常开阔思路。学习开源项目时我推荐以“带着问题去看”的方式它的路由表是怎么做权限控制的它的请求封装和我的有什么不同它的组件复用方案是什么如果我在这种项目里新增一个模块要怎么按它的规范写把这些问题想明白再对比自己的项目找差距进步非常快。我也建议每个 Vue 学习者都去搭建一个自己的小项目不一定多复杂可以是个人博客、账单管理、工具集。在这个过程里你会自然地遇到路由、状态管理、接口封装、组价复用、打包部署等所有核心问题。这个“自己动手填坑”的过程就是你最好的技能沉淀。在实际开发中我还有一个很深的体会前端技术变化太快但核心思想是相通的。Vue 里的响应式思想Angular 里也有React 里也有组件化思想更不用多说。所以不要只盯着某个框架的 API 死记多看它要解决的问题本身这样学到的才是能长期保值的东西。最后再分享一个小习惯我每次解决一个有意思的 BUG 或实现一个复杂的交互后都会在一个专门的笔记软件里随手记录问题和解决思路过段时间自己回顾一遍。几个月后这份笔记就是你最独特、最具含金量的技术沉淀它比任何一本教程对你自己的帮助都大。