打开终端准备跑第一个项目的时候,光标一闪一闪——我带新人时见过太多这样的瞬间。Vue3教程刷了不少,组件、响应式、生命周期听起来都懂,一合上电脑,手却不知道往哪放。这篇笔记就是解决“手不知道往哪放”的:从装环境讲到项目结构,再讲到vite.config.js、package.json这些关键配置,最后落到跑通项目之后常见的坑。适合刚学完基础语法、准备真正动手搭Vue3项目的人,也适合那些已经能跑demo,但项目里每个文件、每个配置都靠复制粘贴、出了问题完全抓瞎的同学。
先把话说在前面:零基础学Vue3,最卡人的往往不是语法,而是“工程化”这三个字。你打开任何一个真实项目,看到的是一堆目录、配置文件、依赖声明,这些不是用来吓唬新手的,它们每一行都有具体职责。这篇文章会把它们拆开、揉碎,按我平时带人的顺序讲一遍,保证你照着做完,心里是透亮的。
1. 先搞清楚:Vue3这套笔记要带零基础跨过哪道坎
1.1 教程里的“代码片段”和真实项目的差距
绝大多数新手学Vue3,接触的都是单文件Demo:一个App.vue,里面写几行ref、computed,浏览器一刷新,效果出来了,觉得自己会了。但真实项目根本不是这样。真实项目是一个“工程”,里面包含几十个文件、几百个依赖包、复杂的构建流程、环境变量管理、路由跳转、状态持久化……这些组合在一起,才叫一个“前端项目”。
我见过不少同学,语法都学过,但拿到一个真实仓库后连npm install都不敢跑,怕把电脑搞坏。这种畏惧的来源,就是对工程结构的不理解。所以这篇Vue3笔记,第一步不是教你怎么写组件,而是带你把“项目”这个东西看透:它怎么来的、每个目录和文件是干嘛的、配置改了会有什么后果。
1.2 为什么Vite环境下的Vue3和传统网页开发不一样
以前写网页,你直接建一个HTML文件,引入Vue的CDN链接,写几行代码就能跑。那叫“运行时加载”,浏览器直接解析。现在的Vue3开发,普遍采用Vite作为构建工具,它干的事情远比“加载一个JS文件”复杂:Vite会把项目里所有模块统一管理、做依赖预构建、处理模块热更新、把.vue单文件组件编译成浏览器能识别的代码。
打个比方:传统方式像你从超市买一袋速冻饺子回家煮,Vite则像开了一家中央厨房——原料(源码)先进厨房,经过切配、调味、装盘(编译、打包),最后端到你面前。中央厨房的好处是上菜快、品质稳定,坏处是流程多了,哪里接不上,菜就端不出来了。所以我们创建Vue3项目,本质上是在搭一套“中央厨房”的流水线,搭完才能安心写业务。
2. 环境准备:装Node、选包管理器、配编辑器
2.1 先确认你的Node.js版本是否足够新
Vite和Vue3对Node版本有硬性要求。你可以在终端里执行node -v看看当前版本。按目前的主流版本,建议Node.js版本不低于18,能用20的LTS版本就更好。如果版本太旧,Vite可能直接报错,或者创建项目时静默失败,新手很容易被这种“环境问题”劝退。
安装Node.js千万记得:去官方网站下载安装包,一路下一步即可,不要用某些教程里给的乱七八糟的“一键脚本”。装完之后,在终端分别执行:
node -v npm -v两条命令都能输出版本号,说明Node和npm已经就位。这里有个容易被忽略的点:改完Node版本之后要新开一个终端窗口,否则可能还是读的旧环境变量,报各种莫名其妙的错。
2.2 npm和pnpm到底选哪个
包管理器是前端项目的“快递员”,负责把项目依赖的第三方库下载到本地。新手最常用的就是npm,它是Node.js自带的,不用额外安装。另一款pnpm近年很流行,特点是磁盘占用小、安装速度快,很多企业项目已经在用。
我的建议非常直接:如果你是零基础,第一个项目先用npm,少引入一个变量。等你把npm的install、run dev、run build这些命令跑熟了,再尝试pnpm也不迟。日常操作无非这么几条:
npm install # 安装依赖 npm run dev # 启动开发服务器 npm run build # 打包生产代码 npm run preview # 本地预览打包结果你不需要知道里面复杂的依赖解析算法,只要记住:项目根目录出现了package.json,就说明这是一个由npm管理依赖的工程;node_modules文件夹里是装好的所有第三方库,这个文件夹一般很大,千万不要手动去改里面的内容。
2.3 编辑器里的几个必需插件
写Vue3强烈推荐VS Code,它本身只是个编辑器,装上合适的插件才好用。我最常配置的有三件套:
- Vue语言支持插件:让
.vue文件获得语法高亮和代码提示,没有它你会觉得Vue文件像普通的txt文档。 - ESLint扩展:在编码时即时提示不规范写法,省去自己一遍遍检查的功夫。
- Prettier扩展:统一代码格式,保存时自动整理缩进、引号,避免团队协作时格式混乱。
装完插件,如果用最新的Vue语言支持插件,它会提示你禁用旧的Vetur插件,这个提示可以直接接受。很多新手装了旧插件不兼容,半天写不出代码提示,问题往往就出在这里。
3. 用create-vue建项目:交互式选项逐个翻译成大白话
3.1 create-vue在向你提问,它在问什么
创建Vue3项目的官方推荐方式是执行这条命令:
npm create vue@latest执行之后,你会看到一串交互式提问。很多零基础的同学就是在这里被问懵的,什么TypeScript、JSX、Vitest,听都没听过,生怕选错就把项目搞废了。其实没那么严重,这些选项翻译成人话就是:
| 提问内容 | 大白话解释 | 零基础建议 |
|---|---|---|
| 项目名称 | 给你的项目文件夹取名 | 用英文小写,不要带空格 |
| 添加TypeScript | 给JS加上类型检查,写代码时少出低级错误 | 可以先选No,理解后再加 |
| 添加JSX | 在Vue里用类似React的写法 | 默认No即可,Vue用模板更舒服 |
| 添加Vue Router | 是否引入路由,实现页面跳转和URL变化 | 选Yes,真实项目几乎必用 |
| 添加Pinia | 引入状态管理库,跨页面共享数据 | 选Yes,后面单独讲 |
| 添加Vitest | 配置单元测试环境 | 先选No,功能开发期用不上 |
| 端到端测试工具 | 自动化模拟用户操作的测试 | 先选No |
| 使用ESLint和Prettier | 代码规范化和格式化 | 选Yes,帮你养成好习惯 |
选完这些,脚手架会自动生成一套完整的工程模板,随后你只需要按提示执行两步:
cd 你的项目名 npm install然后再执行npm run dev,等到终端出现Local: http://localhost:5173/,在浏览器打开这个地址,你就能看到Vue3项目的默认首页了。
3.2 每个选项勾选之后会多出什么文件
如果你理解了这个,以后看任何项目都能快速分辨它的技术栈。选TypeScript,项目里会出现src/main.ts、env.d.ts;选Vue Router,会生成src/router/index.ts和src/views目录;选Pinia,会出现src/stores目录;选Vitest,会多出测试相关配置。这和点菜一样,你勾什么,厨房就给你备什么材料。
我个人的体会是:新手第一次建项目,宁可选项少一点,也不要贪多。默认模板已经足够完整,复杂的特性后面用到再补,比一上来面对一堆看不懂的文件要好得多。
4. 项目结构拆解:每个目录和文件的真实职责
4.1 根目录下的文件各自是干什么的
项目建好后,你会看到一堆文件和目录,第一反应可能是乱。我按“能用眼睛看到”的顺序给你理顺。最顶层的这些,大多是配置文件,它们决定了项目“怎么被构建、怎么被解析、有哪些规则”。
index.html:整个项目的HTML入口。注意Vue3项目的index.html在最外层,不在public里。打开它,你会发现body里只有一个<div id="app"></div>,这个div就是将来整个Vue应用挂载的地方。package.json:项目的“体检报告”,记录项目名称、脚本命令、所有依赖包及其版本号。vite.config.js:Vite的配置文件,开发服务器端口、代理、路径别名等都在这里改。.env系列文件:存放环境变量,比如开发环境和生产环境不同的接口地址。.gitignore:告诉Git哪些文件不要提交到仓库,比如node_modules和一些本地配置文件。README.md:项目说明文档,写给其他开发者看的。
这些文件里,你最需要学会改的是vite.config.js和.env系列,后面第5章详细讲。剩下的大多保持默认即可。
4.2 src目录是开发的主战场
src目录存放源码,将来你大部分时间都在这里写代码。脚手架默认创建的包括:
main.js:项目入口文件,在这里创建Vue应用实例,并“装配”上路由、状态管理等插件。你可以把它理解成总开关,所有插件、全局样式、全局组件都要在这里注册。App.vue:根组件,是整棵组件树的起点。它通常只放一个<router-view />,剩下的页面都由路由来决定显示哪个组件。components:存放可复用的公共组件,比如一个按钮、一个弹窗、一个表格。views:存放页面级组件,每个文件通常对应一个路由地址,比如HomeView.vue对应首页。router:路由配置文件,URL和页面组件的对应关系都写在这里。stores:Pinia状态管理文件,跨页面共享的数据集中管理。assets:存放静态资源,比如图片、全局CSS文件。
另外两个目录是社区项目里很常见、但脚手架不会自动生成的,建议你也自己建出来:api目录用来统一封装所有后端请求接口,utils目录用来放工具函数,比如日期格式化、防抖节流等。这样做的核心好处是:页面组件里不出现大段重复的代码,维护起来清爽。
4.3 public和assets到底怎么分
很多新手会把图片、字体乱放,这会导致打包后资源路径出问题。记住一个简单规则:public目录里的文件会原封不动地被复制到打包结果根部,引用时用绝对路径/images/logo.png;assets目录里的资源会被打包工具处理,引用时通常用相对路径或别名路径。
打个比方,public适合放那种“不经过处理的原始文件”,比如favicon.ico;assets适合放“会和代码一起被打包、压缩、加哈希指纹”的图片和样式。项目开发中,绝大多数图片都应该放assets,因为打包后浏览器缓存可以区分版本。
5. 完整配置逐行讲清楚:vite.config.js、package.json、index.html、.env
5.1 vite.config.js:别只留着默认值
脚手架生成的vite.config.js往往只有几行,但真实项目基本都会在这里做三件事:配置路径别名、开发服务器端口、打包出口。一份很常见的配置大概长这样:
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)) } }, server: { port: 3000, open: true, proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } }, build: { outDir: 'dist', sourcemap: false } })逐项说下:
resolve.alias:把@符号映射到src目录。这样你在写import xxx from '@/components/Button.vue'时,不需要写一长串相对路径../../components/Button.vue。这是团队项目里约定俗成的写法,强烈建议加上。server.port:开发服务器的端口号。默认5173,如果你同时跑多个项目,就靠改端口区分。加了open: true,每次启动都会自动打开浏览器,省一下手。server.proxy:代理配置。开发阶段浏览器直接请求后端接口会有跨域问题,Vite会帮你把请求转发到目标地址。这里把所有/api开头的请求转发到http://localhost:8080,后端接口就畅通无阻了。build.outDir:打包产物的输出目录,默认dist,一般来说不需要改,但要清楚它存在。
改完vite.config.js,记得重启npm run dev,这个文件的大部分改动不会热更新生效。
5.2 package.json:scripts和依赖版本看懂了吗
package.json里最容易看懂的就是scripts部分。脚手架默认生成:
"scripts": { "dev": "vite", "build": "vite build", "preview": "vite preview" }它们分别对应npm run dev(开发模式)、npm run build(生产打包)、npm run preview(本地预览打包产物)。npm run build完成之后,会在项目根目录生成dist文件夹,这就是将来可以部署到服务器的成品代码。
依赖部分分成两块:
dependencies:生产环境也要用的库,比如vue、vue-router、pinia、axios。devDependencies:只在开发时用的工具,比如vite、eslint、prettier。
区分它们的意义在于:如果你打包时分析依赖大小,重点看dependencies;如果某个开发工具装错了位置,部署时可能会白白增加体积。对新手来说更实用的一个点是:不要随便删除package.json里的依赖,也不要手动修改版本号后不执行npm install。依赖版本一变,代码可能报错,排查起来非常头疼。
5.3 index.html和.env:两个容易被忽略的“总开关”
打开最外层的index.html,你会看到:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <link rel="icon" href="/favicon.ico" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Vue3 Project</title> </head> <body> <div id="app"></div> <script type="module" src="/src/main.js"></script> </body> </html>关键在最后一行:src/main.js通过模块方式被引入。Vite启动后会先加载main.js,然后main.js创建Vue应用并挂载到#app上。如果你打开浏览器看到白屏,第一步就检查这个div和script是否还在、路径是否正确。
然后是.env文件。Vue3 + Vite环境下,环境变量必须带VITE_前缀才能暴露给前端代码。举个例子,新建.env文件:
VITE_APP_TITLE=我的Vue3项目 VITE_API_BASE_URL=/api在代码里这样读取:
console.log(import.meta.env.VITE_APP_TITLE)同时还可以创建.env.development和.env.production,分别存放开发、生产环境的不同配置,比如接口地址。这个能力用得好,你就不用在代码里到处写死接口地址了。
6. 技术栈选型:Router、Pinia、请求层、UI库怎么组合
6.1 路由模块:views目录和router的联动
Vue Router是Vue3官方推荐的路由库,核心概念是“URL和组件一一对应”。脚手架选了Router之后,src/router/index.js一般是这样的:
import { createRouter, createWebHistory } from 'vue-router' import HomeView from '@/views/HomeView.vue' const router = createRouter({ history: createWebHistory(), routes: [ { path: '/', name: 'home', component: HomeView } ] }) export default router新手最容易理解的用法是:在views目录里新建一个页面组件,然后在routes数组里加一条记录。路由里的component字段可以用两种写法,一种是直接导入组件(静态加载),一种是箭头函数动态导入:
{ path: '/about', name: 'about', component: () => import('@/views/AboutView.vue') }第二种写法会把对应页面拆成单独文件,访问时才加载,首屏体验更好。我在项目里默认就用动态导入,所有页面都这样写。然后在App.vue里放<router-view />作为页面出口即可。
6.2 状态管理:Pinia比Vuex好在哪
Pinia是Vue3时代的状态管理库,名字听着陌生,作用其实就是“跨组件共享数据”。比如用户登录信息、购物车数据,你把它们放到Pinia的store里,任何组件都能读取和修改。
定义一个store很简单,在src/stores下新建文件:
import { defineStore } from 'pinia' import { ref } from 'vue' export const useUserStore = defineStore('user', () => { const name = ref('') const token = ref('') function setLoginInfo(userName, userToken) { name.value = userName token.value = userToken } return { name, token, setLoginInfo } })在任意组件里使用它:
import { useUserStore } from '@/stores/user' const userStore = useUserStore() userStore.setLoginInfo('张三', 'xxxx-token')相比Vuex,Pinia最大的优点是“去掉了繁琐的概念”,不需要mutations、getters的分类约束,写起来更像普通JavaScript。零基础学这个很舒服,理解为“一个全局可访问的对象”就够了。别忘了在main.js里用app.use(pinia)把Pinia安装进去。
6.3 请求层与UI库:直接进入业务开发的关键拼图
真实项目几乎都要和后端打交道,目前主流方案是Axios。零基础记住一个原则:不要在每个页面里直接写axios.get,而是封装一个统一的请求层。新建src/api/request.js,放一份常规配置:
import axios from 'axios' const request = axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL || '/api', timeout: 10000 }) // 请求拦截器:给每个请求带上token 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 => { console.error(error.response?.data?.message || '请求失败') return Promise.reject(error) } ) export default request这样做的意义在于,以后如果后端要求改请求头或者统一处理登录过期,你只需要改这一个文件,不用满项目找axios调用点。UI库这块,Vue3生态下Element Plus是最普及的组件库,表单、表格、弹窗都能直接拿来用。配合unplugin-vue-components插件实现按需导入,打包体积也不会失控,算是上手成本最低的一套组合。
7. 零基础跑通后最常见的坑(按我遇到的频率排)
7.1 第一坑:npm run dev之后端口被占用
这个报错信息特别明显,终端会提示端口被占用或EADDRINUSE。第一次遇到别慌,不是项目坏了,是端口冲突。解决方式很简单,改vite.config.js里的server.port,比如从5173改成3000,重启即可。
7.2 第二坑:@别名在代码里不生效
脚手架默认不配置别名,直接写@/components/...有时会报错找不到模块,因为Node无法解析@。如果你看到这类报错,检查两点:第一,vite.config.js里resolve.alias是否配置了@指向src;第二,项目根目录是否有jsconfig.json或者tsconfig.json把@/*映射到src/*,否则编辑器的跳转提示也会失灵。很多教程只给了Vite配置,忘了提编辑器配置,所以这里特别提醒。
7.3 第三到第六坑:打包路径、依赖版本、改了不生效、首屏404
- 打包后样式或图片404:如果项目部署在服务器子目录,而不是根目录,需要把
vite.config.js里的base改成相对路径'./'或具体子目录。这个问题开发时完全发现不了,只有部署上线才会暴露,建议项目开始阶段就按需设好。 - 依赖版本混杂导致报错:今天装一个插件,明天装一个插件,版本全是最新的,结果相互冲突。我的习惯是把
package-lock.json当作版本锁定的依据,装库时尽量参考项目现有Vue版本,不盲目追求最新。 - 改了代码页面不更新:Vite的热更新一般很及时,但不排除某些深层文件改动后失效。最常见的原因是编辑器的插件冲突或缓存卡住,先重启
npm run dev,还不行就在开发工具里硬刷新(Ctrl+Shift+R)。 - 部署后首屏404:使用路由的history模式部署到Nginx后,直接访问某个子路径会404。这是因为服务器没有把未知路径重定向到
index.html,需要配置服务器的try_files规则。新手先记住结论,将来部署时再具体研究。
如果让我给零基础的同学一句最实在的提醒,那就是:第一次搭项目,宁可慢一点,把每个选项、每个配置文件都查清楚再回车。你现在省下的十分钟,未来会在排查问题的时候以几十倍的效率还给你。Vue3项目本质上是“一套约定大于配置的工程体系”,理解它的结构和配置,比背多少个API都重要。把这套笔记看完、跟着建一个项目,你就不再是只会写demo的选手了。