最近总有朋友问我同一个问题在VS Code里到底怎么把一个Vue3项目干净利落地建起来说真的这个问题看着基础但你在搜索引擎里翻一圈各种回复版本都不一样有的让你用Vue CLI有的让你用Vite还有的上来就让你装一堆看不懂的依赖。我这些年一直用VS Code做Vue3开发从早期用Vue CLI创建项目到后来全面切到Vite中间踩过的坑说不上多深但确实不少。这篇就专门聊Vue3项目从零创建这件事把环境准备、命令选择、配置要点和常见报错一次性理清楚。适合刚接触Vue3的前端新手也适合原来写Vue2、现在想快速切到Vue3的老开发。1. 环境准备先把地基打好很多人创建项目失败根本原因不是命令不对而是机器上的基础环境有问题。这一步看着不起眼但直接影响后面所有操作。我把环境准备拆成三个关键环节。1.1 Node.js版本检查在VS Code里打开终端快捷键Ctrl 先执行下面两个命令确认版本node -v npm -v经常有人在这里翻车。比如装完Node后忘了重启VS Code终端里node -v怎么敲都提示“不是内部或外部命令”这时候重启一下VS Code基本就好了。版本要求方面Vite 5要求Node 18以上Vite 6/7建议Node 20.19或22.12。如果你还在用Node 16创建项目时会直接报错。我的建议是直接上Node 20 LTS版本稳定兼容性好。如果机器上装了多个Node版本推荐用nvmNode Version Manager管理切换版本方便也避免不同项目之间互相锁版本。注意npm和Node是绑定安装的升级Node版本后最好确认npm也同步更新否则可能出现npm版本过老的警告。1.2 VS Code插件配齐开发省一半心打开VS Code的扩展面板这几个插件是必装的Vue - Official这是Vue3的官方语言服务插件提供.vue单文件组件的语法高亮、类型检查、智能提示也叫Volar。如果你想用Vue3这个插件可以说是刚需。ESLint代码规范检查团队协作时尤其重要。Prettier - Code formatter统一代码格式化。Auto Rename Tag自动同步修改标签对写模板时很实用。这里有一个非常容易踩的坑很多老教程会让你装Vetur那是Vue2时代的插件。在Vue3项目里如果同时装了Vetur和Vue - Official两个语言服务会打架导致.vue文件要么不提示要么疯狂报错。装Vue - Official之前先把Vetur禁用掉。装完插件后建议在VS Code设置里把editor.formatOnSave打开再配合Prettier这样每次保存文件都会自动格式化能省掉大量手动调格式的时间。1.3 工具链选型Vite还是Vue CLI问得最多的就是到底用Vite还是Vue CLI创建项目我的结论很直接新项目无脑选Vite。原因有三点。第一Vite开发模式基于原生ESM冷启动速度极快像Vue CLI这种基于webpack的工具启动一个大型项目可能要等几十秒Vite通常两三秒就起来了。第二Vite的热更新做到了毫秒级改代码保存后页面几乎无感刷新。第三Vite官方已经把create-vue作为Vue3项目的默认创建方式Vue CLI基本进入维护模式新功能不会再往里面加了。用生活化类比说Vite类似于现做现吃的快餐按需编译启动快Vue CLI更像提前备好大锅菜的食堂不管你这顿吃不吃得先把菜全炒好。Vue CLI的webpack方案在构建大项目时功能成熟但开发体验确实差了不止一个档次。2. 从零到一用Vite创建Vue3项目的完整流程环境准备好之后开始动手创建项目。这一节是全文的核心我把每一步都拆开讲连同背后的原理一起说清楚。2.1 命令行创建项目在VS Code终端里执行npm create vuelatest注意不是npm create vite而是npm create vue。这个命令实际上是执行create-vue脚手架它会帮你拉取官方模板并且在交互式命令行里询问你需要的功能包括是否使用TypeScript、是否引入Vue Router、是否使用Pinia、是否添加Vitest、是否需要ESLint等。这里我推荐的选择方式用表格列出来选项项目推荐选择TypeScript是否需要类型系统团队不会TS就选No会就选YesJSX是否用JSX/TSX不写React风格可选NoVue Router是否要路由多页面系统选YesPinia是否要状态管理涉及共享状态选YesVitest是否要单元测试项目要长期迭代选YesESLint/Prettier代码规范推荐Yes如果你想跳过交互式询问直接在命令行把参数带上npm create vuelatest my-vue3-app -- --ts --router --pinia --eslint这样会以my-vue3-app为项目名直接生成带TypeScript、Router、Pinia、ESLint的项目。命令里的--是npm的参数分隔符用来告诉npm后面的内容都传给create-vue不要漏了它。注意项目名不能有大写字母和驼峰命名用短横线分隔的kebab-case风格比如my-vue3-app否则脚手架会直接报错。2.2 目录结构逐层拆解进入项目目录打开VS Code的文件夹结构大致如下my-vue3-app ├─ .vscode/ # VS Code编辑器配置推荐提交到git ├─ node_modules/ # 依赖包npm install后自动生成 ├─ public/ # 不需要打包的静态资源如favicon.ico ├─ src/ # 源码目录 │ ├─ assets/ # 静态资源图片、样式等 │ ├─ components/ # 通用组件 │ ├─ router/ # 路由配置 │ ├─ stores/ # Pinia状态管理 │ ├─ views/ # 页面视图 │ ├─ App.vue # 根组件 │ └─ main.ts # 入口文件 ├─ index.html # 页面模板Vue挂载的起点 ├─ vite.config.ts # Vite配置文件 ├─ tsconfig.json # TypeScript配置 └─ package.json # 依赖与脚本管理很多新手会好奇index.html为什么在项目根目录而不是public里。原因是Vite把index.html当作开发服务器的入口它引用src/main.ts作为模块入口。这个设计和webpack的dist/index.html不太一样Vite的理念是让入口更直观你打开index.html就能看到整个应用从哪里启动。main.ts的内容也值得看一眼import { createApp } from vue import App from ./App.vue createApp(App).mount(#app)Vue3不再像Vue2那样用new Vue()而是通过createApp创建应用实例再挂载到index.html里定义的div idapp上。理解了这个后面看main.ts的演变就不会懵。2.3 启动开发服务器验证创建完成后依次执行cd my-vue3-app npm install npm run devnpm install是安装依赖这一步在项目刚创建后必须做。装完依赖后npm run dev启动开发服务器默认端口是5173。启动成功后终端会显示类似这样的内容VITE v5.x.x ready in 3xxx ms ➜ Local: http://localhost:5173/ ➜ Network: http://192.168.x.x:5173/按住Ctrl键点击Local地址浏览器会打开一个带着Vue图标和示例代码的页面。到这里你的Vue3项目就算创建成功并跑起来了。如果你用的是远程开发环境比如在服务器上用VS Code Remote-SSH或者部署到局域网访问可能需要用npm run dev -- --host来监听所有网络接口否则只有本机可以访问。3. 项目核心配置路径别名、代理与代码规范脚手架生成的项目能跑但真正进入开发还需要把几个基础配置配好。这三个配置是我做任何一个Vue3项目都会优先处理的路径别名、开发代理、代码规范。3.1 配置路径别名项目一复杂组件层级深了import路径就会变成../../../../components/xxx这种相对路径写起来难受改起来更难受。解决的办法是配置别名让指向src目录。打开vite.config.tsimport { 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)) } } })这里用node:url的fileURLToPath把import.meta.url转成文件路径确保跨平台兼容。直接用字符串/src在某些环境也能跑但用fileURLToPath更严谨。配了Vite还不够TypeScript的项目还要同步修改tsconfig.json。在compilerOptions里加{ compilerOptions: { baseUrl: ., paths: { /*: [src/*] } } }如果只改Vite不改TS编辑器依然会对/xxx的导入路径报红。这两个文件必须成对修改缺一个都会遇到问题。3.2 开发代理解决跨域前后端分离的项目开发阶段最大的痛点是跨域。前端跑在5173端口后端接口跑在8080端口浏览器从5173发出的请求会被后端拒绝跨域。解决思路是让Vite开发服务器做代理前端代码请求本机5173的同源地址Vite再把请求转发到后端。vite.config.ts里这样配export default defineConfig({ server: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true, rewrite: (path) path.replace(/^\/api/, ) } } } })这里的关键点是changeOrigin: true。它会把请求头里的Host字段改成目标地址的域名很多后端服务会校验这个字段不设的话即使代理通了后端也可能返回403。rewrite的作用是去掉路径里的/api前缀。如果你的后端接口本身不带/api前缀就需要这个配置如果后端所有接口都带/api那就不用rewrite直接转发就行。这里要根据你后端的实际情况调整。提醒Vite的代理只在开发服务器有效。生产环境部署时跨域问题要在Nginx、网关层或者后端配置CORS来处理不能指望前端的这个配置在生产环境生效。3.3 用ESLint和Prettier统一团队代码风格脚手架会在生成项目时询问是否需要ESLint如果你选了Yes项目里已经带了基础配置。但我建议再加一个Prettier和ESLint配合使用——ESLint管代码规则比如禁止未使用变量、强制单引号Prettier管格式比如缩进、换行、尾逗号。二者有重叠但各司其职。安装Prettiernpm install -D prettier eslint-config-prettier然后创建.prettierrc配置文件{ singleQuote: true, semi: false, trailingComma: none, printWidth: 100, tabWidth: 2 }配好后在VS Code设置里开启editor.formatOnSave并把Prettier设为默认格式化器。这样每次保存文件格式自动调整团队协作时大家交出来的代码风格基本一致code review就能少很多无意义的“格式互怼”。4. 开发中的常见报错与排查实录这部分是我实际开发中踩过、也帮别人排查过的高频问题每一个我都给出来源排查思路和解决方式。4.1 依赖安装或启动时的经典报错报错vite 不是内部或外部命令这个报错十有八九是npm install没成功或者项目依赖压根没装。检查根目录有没有node_modules文件夹没有就先装依赖。还有一种情况是npm版本和package-lock.json里的版本不匹配导致部分依赖被跳过删掉node_modules和package-lock.json后重新npm install基本能解决。报错TypeError: Cannot read properties of null (reading xxx)这个报错通常在组件渲染阶段出现最常见的场景是模板里访问了一个不存在的对象属性比如后端返回的数据里list是null模板里却直接list.length。排查方法是打开浏览器DevTools的控制台看具体是哪一行报错再用v-if或可选链?.规避。报错Failed to resolve import说明Vite找不到你导入的模块。先检查路径有没有写错再看是不是包没安装。如果用路径别名检查一下第3.1节配置是否同时改了vite.config.ts和tsconfig.json不少人是只改了一个文件导致这个问题。4.2 编辑器类型与语法提示异常场景.vue文件完全没有任何高亮和智能提示几乎都是插件问题。确认安装了Vue - OfficialVolar然后在扩展列表里搜Vetur有就禁用并重载窗口。两个插件同时开启的后果就是互相干扰Vue3项目里只保留Vue - Official。场景浏览器里能跑但编辑器和vue-tsc提示类型错误这通常不是真的运行时错误而是类型声明文件缺失。比如导入某个第三方库没有自带类型就提示“找不到模块的声明文件”。解决方式是在根目录的env.d.ts或shims-vue.d.ts里手动声明declare module some-lib { const content: any export default content }如果项目中很多文件报类型错误但浏览器正常还有一个可能是tsconfig.json里的include配置没有覆盖到出错的目录。4.3 热更新失效的排查思路Vite的热更新偶尔也会“抽风”具体表现是改了代码保存后页面没有任何反应。排查顺序我总结成一个固定动作第一看VS Code终端有没有报错信息。如果Vite开发服务器崩溃了直接把终端里最新的报错丢给搜索引擎大概率能定位到问题。第二确认是不是配置文件本身改了。修改vite.config.ts后Vite会自动重启服务器但如果它没有自动重启手动停掉进程CtrlC再npm run dev一次。第三检查是不是文件系统权限问题。项目放在同步盘、或者被某些工具锁定了目录可能导致文件变更事件监听不到。把项目移到本地磁盘非系统盘目录通常能解决。第四在文件里尝试修改.vue组件的script内容如果热更新不生效但修改style生效可能是script setup配置的特殊问题。升级Vite和vitejs/plugin-vue到最新版本是这种问题最直接的解法。5. 从一个空项目到后台管理系统常用能力补齐项目骨架搭好之后接下来要做的是往里面填充实际开发需要的“装备”。我以大多数人都会做的后台管理系统为例把常用的三个能力讲一下。5.1 引入Element Plus组件库Vue3的组件库Element Plus是使用率最高的选择尤其做中后台项目开箱即用。安装npm install element-plusElement Plus支持全量引入和按需自动导入。全量引入在main.ts里import ElementPlus from element-plus import element-plus/dist/index.css app.use(ElementPlus)全量引入的好处是省心缺点是打包体积大。对追求性能的项目推荐按需自动导入配合两个插件npm install -D unplugin-vue-components unplugin-auto-import然后在vite.config.ts里配置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()] }) ] })配置之后模板里直接用el-button、el-table等等组件插件会自动按需引入对应的JS和样式无需手动import样式也不会丢。我实测下来按需引入相比全量引入打包后的体积能减少30%以上。5.2 路由与状态管理脚手架创建项目时如果选了Vue Router和Pinia这步则基本不用重配直接看用法。路由入口在src/router/index.ts添加一个页面路由的常见写法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 } ] }) export default router这里注意选择createWebHistoryHTML5 history模式还是createWebHashHistoryhash模式。开发阶段两者几乎没有区别但生产环境用history模式需要服务器端把所有路由都重定向到index.html否则刷新页面会404。如果你没有服务器配置权限直接用hash模式最简单。Pinia状态管理的使用方式import { defineStore } from pinia export const useUserStore defineStore(user, { state: () ({ token: }), actions: { setToken(token: string) { this.token token } } })在组件里使用import { useUserStore } from /stores/user const userStore useUserStore() userStore.setToken(xxx)Pinia和Vuex最大的区别是去掉了mutations只有state、getters、actions写起来更简洁而且天然支持TypeScript类型推断比Vuex好太多。这也是为什么Vue3官方推荐用Pinia的原因。5.3 Axios请求封装与接口对接后台管理系统几乎离不开请求库目前主流的方案是Axios加拦截器封装。先安装npm install axios在src/utils/request.ts里封装一个统一实例import axios from axios const service axios.create({ baseURL: /api, // 配合第3.2节的代理 timeout: 15000 }) // 请求拦截器统一附加token service.interceptors.request.use( (config) { const token localStorage.getItem(token) if (token) { config.headers.Authorization Bearer ${token} } return config }, (error) Promise.reject(error) ) // 响应拦截器统一处理业务错误 service.interceptors.response.use( (response) { const res response.data if (res.code ! 200) { console.error(res.message || 请求失败) return Promise.reject(new Error(res.message || 请求失败)) } return res }, (error) { // 处理HTTP错误状态码比如401跳登录、500提示服务异常 return Promise.reject(error) } ) export default service然后在具体的API模块里使用import request from /utils/request export function fetchUserList(params: any) { return request.get(/user/list, { params }) } export function updateUser(data: any) { return request.post(/user/update, data) }这样做的好处是所有请求都走同一个实例token注入、错误拦截、loading管理都能在拦截器里统一处理不需要在每个页面里重复写这些逻辑。后面如果接入了新的协议比如从HTTP换成WebSocket只需要修改拦截器或者request封装业务代码完全不用动。我个人在实际开发中的体会是vite.config.ts里的别名和代理建议在一开始建项目就配好别等着写到后面再来补。特别是别人接手时发现所有import路径都是../../想改得花一晚上。另外Element Plus的按需自动导入如果团队里有人不熟悉这个机制偶尔会看到“组件没生效”的奇怪问题其实多半是没配置resolver或者VS Code缓存没刷新。重启一次VS Code按下CtrlShiftP执行“Reload Window”很多“灵异事件”就没了。最后再分享一个让我节省了大量时间的小习惯创建完项目后我会在src/views下先放一个HomeView.vue里面直接写上“基础框架已验证可以开始开发”这样的标记文字然后跑一遍完整的npm run build。如果构建成功说明整个工具链是通的。这个动作每次新建项目时做一次后面开发就算遇到问题也能确定不是基础链路的问题排查范围会小很多。