
搞前端这么久我一直觉得“搭一个 Vue 项目”是最容易被低估的一步。很多人以为跑一条npm create vuelatest就完事了结果卡在 Node 版本不对、依赖装不上、路由配好一刷新就 404、打包上线样式全乱这些连环坑里。这篇文章就是一份我实际踩坑踩出来的完整步骤从环境准备到项目创建再到路由、状态管理、代理配置、打包部署全部按真实操作顺序讲适合刚接触 Vue 的入门者也适合已经写过业务代码、但一直没系统梳理过“项目从零到上线”全流程的同学参考。1. 装环境别当玄学Node.js版本和npm镜像一个都不能省很多人拿到教程第一件事就是敲命令敲完发现报错然后就开始怀疑人生。其实 Vue 项目对环境有硬性要求这一步躲不掉也不该躲。1.1 Node.js版本选错Vite直接罢工先搞清楚一个逻辑链Vue 3 官方推荐的构建工具是 Vite而 Vite 对 Node.js 有明确的版本要求。Vite 大版本要求的 Node.js 版本Vite 414.18 / 16Vite 518 / 20Vite 618 / 20 / 22如果你电脑里的 Node 还是 14 或者更老的 12直接跑 Vite 项目大概率会报unsupported engine或者各种莫名其妙的语法错误。我的建议很直接装 LTS 版本目前 Node 20 LTS 或者 22 LTS 都可以。这里有个很常见的低级错误用node -v看到版本号就直接开搞其实系统里可能装了 nvm 但默认切到了别的版本。所以正规操作是先nvm ls看看有没有多版本再nvm use切到目标版本最后才node -v确认。1.2 镜像源不配好npm install能卡到你怀疑人生安装依赖慢不是网速问题是 registry 默认指向国外地址的问题。这一步不需要什么高超技巧但一定要在搭建项目之前配好npm config set registry https://registry.npmmirror.com配完验证一下npm config get registry返回的是国内镜像地址就对了。顺便说一下如果你是公司内网环境可能还要配私有 registry这时候不要强行改全局而是建议在项目根目录放.npmrc文件做局部配置内容大概是registryhttps://你的内网地址。1.3 动手前先做这三步自检我每次在给别人远程排查“为什么 vue 项目跑不起来”的时候第一句话永远都是把这三条命令的执行结果发我。node -v npm -v npm config get registry这三条命令的输出能排查掉大概七成的前置问题。版本不对的直接去官网下载安装包重装之后记得重新打开终端镜像没配的补上镜像配置版本和镜像都没问题但依然报错再考虑是不是权限问题比如 macOS 下 npm 全局报EACCES权限错误那就要检查 npm 的 prefix 目录而不是一上来就sudo chmod -R乱改。还有一点容易被忽略装完新版本 Node 后最好顺手把 npm 更新一下npm install -g npmlatest。因为老版本 npm 在处理某些依赖树时会出现诡异的peerDependencies冲突你排查半天以为是代码问题结果换个 npm 版本就好了。2. 脚手架选型不是跟风Vite与Vue CLI怎么选现在搜 Vue 搭建教程能看到两种完全不同的命令一种是npm create vuelatest另一种是npm install -g vue/cli然后vue create my-project。很多新手被弄糊涂了其实这是一代工具的更替。2.1 create-vue到底好在哪npm create vuelatest是 Vue 官方当前主推的脚手架底层是基于 Vite 的。它跟 Vue CLI 最大的区别在于Vite 开发服务器启动快到离谱因为它按需编译用原生 ES Module 起服务不像 Webpack 那样先把整个项目打包一遍。以前用 Vue CLI 起一个中型项目等编译等几十秒很正常现在 Vite 冷启动基本是一秒级。而且改了代码之后的刷新反馈非常快热更新几乎感觉不到延迟。实际写代码的时候这种体感差异会直接影响开发效率。2.2 老项目的Vue CLI还要不要迁移如果你只是新学 Vue直接学 Vite 路线就行别走弯路了。但如果你手里维护着老项目用的是 Vue CLI 4/5我不建议为了追新随便迁移。迁移本身不是简单的换命令涉及 webpack 配置、第三方库兼容、环境变量处理方式等一堆问题小项目可以折腾大项目一旦上线了稳定压倒一切。那 Vue CLI 还有没有存在价值有。如果你要维护 Vue 2 项目或者项目里有些旧依赖只在 webpack 的配置方式下跑得通Vue CLI 仍然是可用的。只是你需要清楚一个事实Vue CLI 现在处于维护模式官方不再加新功能长期看新项目不会再选它。2.3 Vue 2还是Vue 3新项目别纠结这个问题本来不该出现在“搭建”教程里但实在太常被问到了顺手说清楚新项目无脑选 Vue 3。Vue 2 在 2023 年底就已停止维护官方不再提供安全更新新项目没有任何理由再开 Vue 2 的坑。你要是入职老公司要维护 Vue 2那是另一回事自学或者新写项目直接 Vue 3。有个相关的问题也被问得多layui可以用vue吗。Layui 是 jQuery 时代的东西跟 Vue 的数据驱动、组件化思想属于两个物种强行混着用会让你写出非常别扭的代码。你要是特别喜欢 Layui 的风格可以去看看基于 Vue 的第三方组件库比如 Naive UI、Element Plus、Ant Design Vue这些才是 Vue 生态里的“官方队友”。3. 命令一条条执行从空目录到页面渲染环境就绪、脚手架选型定了之后真正创建项目的过程其实不长但每一步都有值得注意的细节。我这里用 Vite 创建 Vue 3 项目的完整流程举例大家跟着敲就行。3.1 npm create vuelatest的交互选项怎么勾打开终端进入你想要放置项目的目录执行npm create vuelatest这里有个细节如果你的 npm 是旧版本可能需要手动安装 create-vue 包才能识别这个命令新版本 npm 会自动处理。跑起来之后命令行会问你项目名称接着是一串功能选项。我自己的推荐勾法如下项目功能推荐选择说明TypeScript按需想学 TS 就选是纯 JS 项目选否JSX否写 Vue 用模板语法就够JSX 不是必需Router是单页应用基本都会用到路由Pinia是Vue 3 官方推荐的状态管理Vitest否单元测试框架项目要上测试再勾ESLint是代码规范检查强烈建议开Prettier是代码格式化配合 ESLint 省心如果你连Router和Pinia是什么都还不太清楚我建议先勾上因为这两个东西学到项目中期基本是躲不开的而且脚手架集成好的比自己后期手动加要省事官方已经帮你处理了依赖版本和基础配置。3.2 装完依赖先别急着写代码看看目录结构创建完成之后按提示进入项目目录执行npm install npm run devnpm install这个步骤在镜像源配错的时候会非常折磨人所以前面才反复强调 registry。装完依赖不会看到什么成功提示只要不报错就是最大的好消息。接着npm run dev起来之后终端会给出一个本地地址通常是http://localhost:5173浏览器打开就能看到默认页面。这个阶段我建议大家做一件事把项目根目录的文件夹结构完整过一遍。Vite 生成的框架项目结构长这样my-vue-app/ ├── public/ # 静态资源不走构建的 ├── src/ # 核心源码目录 │ ├── assets/ # 图片、样式等被构建的资源 │ ├── components/ # 组件文件夹 │ ├── router/ # 路由配置 │ ├── stores/ # Pinia 状态管理 │ ├── views/ # 页面级组件 │ ├── App.vue # 根组件 │ └── main.js # 入口文件 ├── .env.development # 开发环境变量 ├── .env.production # 生产环境变量 ├── index.html # 页面入口 HTML └── package.json # 项目依赖与脚本很多人拿到这个结构会问为什么只有一个index.html在外面因为 Vite 以index.html作为入口通过浏览器原生 ES Module 加载/src/main.js再由 Vue 把组件挂载到页面上的某个 DOM 节点上。别把index.html当成传统的页面文件它其实是一个“壳”。3.3 用计数器组件验证热更新和组件通信能看到默认页面之后我建议不要立刻开始写业务而是先改几行代码验证整个开发链路是通的。打开src/components下面那个现成的组件把模板里的内容改成最简单的结构script setup import { ref } from vue const count ref(0) /script template button clickcount点我当前数字{{ count }}/button /template保存文件浏览器里的内容会自动刷新这就是 Vite 的热更新。这个过程能验证三件事组件能渲染、响应式数据能用、热更新是通的。这三件事跑通说明你这个项目的基础链路完全没问题后面写复杂业务才有底。4. 路由、鉴权和状态管理工程化项目的三根支柱脚手架把项目建起来只是第一步真正做业务绕不开路由跳转、页面鉴权和数据共享这三个问题。这一章我把最常见的用法和踩坑点一次性说清楚。4.1 路由参数传递的三种姿势和坑vue-router是 Vue 生态里的路由标配。先在src/router/index.js里定义路由表核心配置大概长这样import { createRouter, createWebHistory } from vue-router const routes [ { path: /, name: home, component: () import(/views/HomeView.vue) }, { path: /user/:id, name: user, component: () import(/views/UserView.vue) } ] const router createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes }) export default router这里要注意import.meta.env.BASE_URL这个写法它是 Vite 提供的环境变量读取的是base配置。如果你在部署时把项目放在了子目录这个值不配置对路由和静态资源全都会崩。路由传参有三个常见姿势path方式、query方式和params方式// params 方式适合按照 /user/:id 这种路径设计 router.push({ name: user, params: { id: 123 } }) // query 方式适合列表页的筛选条件 router.push({ path: /list, query: { page: 1, keyword: vue } })有个经典的大坑如果路由表里写的是/user/:id但你用router.push({ path: /user, params: { id: 123 } })页面会直接报错或丢失参数。params必须配合name使用不能配合path。这个细节在面试题里出现的频率很高实际开发中也容易踩记住一句话用name传params用path带query。4.2 路由拦截器与刷新404的纠葛路由拦截器很多人叫“前置守卫”实际上是通过beforeEach挂在路由实例上的钩子函数。最典型的应用场景就是登录鉴权router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.meta.requiresAuth !token) { next({ name: login }) } else { next() } })前后端分离项目里的请求 token 处理通常跟这个守卫配合。前端登录成功后把 token 存到 localStorage请求拦截器里带上Authorization头响应拦截器里遇到 401 就跳回登录页。这个模式比较成熟就不展开贴完整代码了大家知道链路是“路由守卫控制页面访问权限 请求拦截器控制接口访问权限”就够。再说一个跟路由强相关的坑createWebHistory模式HTML5 History 模式下本地开发一切正常但打包部署到 Nginx 后你直接访问http://你的域名/list会看到 404。原因很简单服务器上没有这个物理文件请求被后端处理了。解决方式是让 Nginx 把所有路径都重写到index.html让前端路由接管。这个我在第 6 章部署部分会再单独说。4.3 Pinia接替Vuex新项目的状态管理该选谁状态管理解决的是“多个组件共享数据”的问题。Vue 2 时代大家用 Vuex步骤多且繁琐需要写state、mutations、actions、getters四件套。Vue 3 时代官方主推 Pinia它把很多概念砍掉了。Pinia 的基础用法很简单新建src/stores/counter.jsimport { defineStore } from pinia import { ref } from vue export const useCounterStore defineStore(counter, () { const count ref(0) const increment () { count.value } return { count, increment } })然后在组件里直接用script setup import { useCounterStore } from /stores/counter const store useCounterStore() /script template p{{ store.count }}/p button clickstore.increment1/button /template有没有注意到这个写法比 Vuex 直观太多不需要 commit 去触发 mutation不需要写一堆重复代码。新项目的结论很简单选 Pinia不要犹豫。唯一需要补的认知是 Pinia 的模块化方式——一个 store 就是一个文件你可以创建userStore、orderStore、cartStore等等互不干扰。多说一句面试题里常考的对比Pinia 为什么好一是取消了 mutation 概念开发链路缩短二是天然支持组合式写法三是类型推导比 Vuex 强很多四是支持 store 之间互相调用。5. 开发调试阶段不可省的基础设施项目跑起来之后别急着堆业务代码先把开发调试相关的几个基础设施配好。这些配置看起来不起眼但等到项目大了再回头补成本高得惊人。5.1 环境变量文件怎么分环境管理一个正经项目至少有开发环境、生产环境可能还有测试环境。Vite 项目在根目录用.env文件管理环境变量命名规则是.env.development、.env.production、.env.test。文件内容格式如下# .env.development VITE_API_BASE/api VITE_APP_TITLE开发环境# .env.production VITE_API_BASEhttps://api.example.com VITE_APP_TITLE生产环境变量名以VITE_开头可以通过import.meta.env.VITE_API_BASE在代码中访问。这里有一个很多人都会掉的坑修改.env文件之后必须重启开发服务器才生效热更新不够因为环境变量是在服务启动时读入的。5.2 代理配置前后端分离项目的跨域救星开发环境下前端跑在5173端口后端跑在8080端口直接发请求必然跨域。解决跨域的方式很多但开发阶段最好用的不是 CORS而是 Vite 的代理功能。在vite.config.js里加上export default defineConfig({ server: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true, rewrite: (path) path.replace(/^\/api/, ) } } } })这里配置的意思是所有以/api开头的请求都会被代理到http://localhost:8080changeOrigin让后端看起来请求来自同源rewrite把/api前缀去掉因为很多后端的接口路径并没有/api这段。配好后前端代码里要请求接口就这样写fetch(/api/login)代理层自动帮你转发到http://localhost:8080/login。这个方案能最大程度模拟生产环境请求路径避免在业务代码里写死http://localhost:8080这种硬编码。5.3 浏览器里装上Vue插件排查效率翻倍如果你还没装 Vue DevTools现在是时候装了。它能直接在浏览器里查看组件树、Props、Events、Pinia 状态和路由信息排查问题效率完全不是一个量级。装完插件打开 Vue 项目页面就能看到Vue标签页。我最常用的功能是“组件状态实时查看”——当某个数据没按预期变化时直接看插件里的 Pinia 或组件 state能迅速定位是 setter 没调用、还是数据被别处改掉了。另外还推荐大家在路由出错时先看一眼 “Router” 面板当前 path、matched 路由、query 参数一目了然。说到调试再提一个工具链上的问题如果你用的编辑器是 VS Code搜索查看历史版本的 VolarVue Language Features注意现在的新版本推荐直接装Vue - Official插件旧版 Volar 在部分新项目里会出现智能提示不生效的问题。这个问题在官方文档里明确提示过但很多教程没提到过放到这里算是个补充提醒。6. 打包部署时的细节直接影响线上体验本地开发环境再顺也不能说明项目完成打包部署才是真正检验项目质量的时刻。这一章讲的每个坑我都真实遇到过而且很多都是做完就忘了、隔半年又踩一遍。6.1 打包后布局异常和404问题怎么排查执行npm run build之后项目会生成一个dist目录。我见过太多次“本地好好的一打包就废”的情况排序靠前的两个原因如下第一个资源路径不对。如果你的项目部署在服务器根域名那base配置用默认值即可但如果部署在域名子目录比如http://example.com/my-app/那么index.html里引用的 JS/CSS 路径如果带了绝对路径/assets/xxx.js加载一定会失败表现为页面白屏、布局自然全乱。解决办法是在vite.config.js里配置export default defineConfig({ base: ./ // 使用相对路径 })这样打包出来的资源路径会变成./assets/xxx.js不管放在哪个子目录都能正常加载。第二个代码本身有兼容性问题。如果你的项目用了较新的 JavaScript 语法比如可选链?.、空值合并??打包后目标浏览器版本不够新控制台会报语法错误页面同样白屏。这种情况可以在vite.config.js里配置build.target或者干脆用官方默认的baseline-widely-available作为目标如果业务对老浏览器有要求就要引入 polyfill 了。6.2 Nginx上线配置的几个关键点部署 Vue 项目最常用的方式就是 Nginx 静态文件托管。把dist目录里的内容上传到服务器然后 Nginx 配置大概长这样server { listen 80; server_name example.com; root /var/www/my-app/dist; index index.html; gzip on; gzip_types text/css application/javascript application/json; location / { try_files $uri $uri/ /index.html; } }核心是try_files $uri $uri/ /index.html;这一行它解决了 History 路由模式下的刷新 404 问题当 URL 是/list时服务器找不到对应的物理文件就回退到index.html然后前端路由接管页面渲染。还有一个细节容易被忽略如果前端通过/api请求后端接口生产环境不要在 Nginx 里硬编码后端地址建议用反向代理location /api/ { proxy_pass http://127.0.0.1:8080/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }这样前端代码里的VITE_API_BASE/api在生产环境也适用不需要针对不同环境改代码。整个链路是浏览器 → Nginx 静态文件 → Nginx 反向代理 → 后端服务。6.3 离线环境下安装依赖的三种思路有些内网开发环境是无法直接访问外网的npm install基本是废的。这时候有几种思路可以尝试第一种找一台有外网的电脑在同一个项目目录执行npm install然后把node_modules整个打包传到内网。这个方案最笨但最可靠前提是两台电脑的操作系统一致否则部分原生模块可能出问题。第二种在外网机器上执行npm pack 包名把依赖包下载成.tgz文件传到内网后用npm install 包名.tgz安装。这个方案适合依赖项不多的情况依赖项一多就比较费力了。第三种使用 npm 的缓存离线模式。在外网机器上执行一次完整的npm install之后npm 会把下载的包缓存到本地目录之后在断网状态下执行npm install --offline就能从缓存安装。这个方案要求缓存存在而且不同机器的 npm 版本可能有兼容问题。无论哪种方式都建议在项目里维护一个完整的package-lock.json文件并提交到仓库它能锁定每一层依赖的准确版本避免离线安装时出现依赖版本匹配的问题。7. 最后聊点别的我踩过的坑和现在的习惯写了这么多与其说是教程不如说是我这几年前端项目搭建经历的一次完整复盘。最后分享几个我现在养成的习惯可能对你有帮助。第一新建项目时多花两分钟配置好 ESLint 和 Prettier。很多新手觉得这些工具没用等到项目写了几千行代码、格式满天飞的时候才后悔。ESLint 能在你保存代码时自动帮你把低级 bug 挡在门外Prettier 能让团队代码风格统一。这两个是团队协作的基础设施不是可选项。第二路由跳转永远用name或具名路由不要裸写字符串路径。路径一改动所有相关跳转的地方都要跟着改非常容易遗漏。用name做解耦路径怎么变都不影响已有代码。这虽然是个小习惯但能省下大量改造成本。第三遇到“本地好好的、部署就出问题”的情况先别急着查代码按顺序排查三个点资源路径base、路由模式 History/History 和 Nginx 的try_files、后端接口地址是否可访问。这三步排查完八成的问题都能解决。第四Vue 项目搭好之后多用一阵子再谈扩展。很多同学创建完项目第一件事是找各种组件库往里面塞结果项目还没跑通依赖冲突先来了一堆。先把手上的业务跑顺再按需引入组件库、工具函数库项目会健康得多。说到底搭建 Vue 项目本身不是一个高深的技术活它考察的是对工具链的理解、对环境的掌控力以及遇到问题时排查的思路是否清晰。把这篇文章里的步骤跟着做一遍遇到问题再回来看对应的排查章节基本就能顺利走通“从零到部署上线”的完整流程。后面我大概率还会写一篇关于 Vue 项目里接口请求封装和权限控制的文章到时候再系统聊一聊 token 刷新、接口重试、按钮级权限这些工程化话题。