前言
前段时间决定系统学习 Vue 3,正好从最基础的项目搭建开始,一步步把整个项目的架构搞清楚。网上很多教程要么直接贴一堆命令,要么直接跳到组件写法,我总觉得缺了点“为什么这么做”的感觉
这次我把自己从零开始搭建一个 Vue 3 + Vite 项目的所有笔记和代码整理了一下,写成这篇文章。目标很简单:让跟你我一样的新手,看完就能明白一个现代 Vue 项目到底长什么样、每一层是干嘛的,以及它是怎么实现前后端分离的。
咱们不讲太高深的原理,就从实际代码和文件结构入手,由浅入深,一步步拆解。
一、用 Vite 快速创建一个 Vue 项目
现在 Vue 官方推荐的构建工具不再是 Vue CLI,而是 Vite。
Vite 是谁开发的?就是咱们熟悉的 Vue 作者尤雨溪(Evan You)亲自操刀的下一代构建工具。它的最大亮点是开发时启动速度极快 + 热更新(HMR)几乎秒刷新。
为什么快?因为它利用了浏览器原生支持的 ES Modules(ESM),开发模式下不需要把所有代码打包成一个大 bundle,浏览器想用哪个模块就直接去请求哪个模块,省掉了大量打包时间。
创建项目命令超级简单:
npm create vite@latest
按提示选择:
- 项目名:比如 all-vue
- 框架:Vue
- 变体:JavaScript(或者 TypeScript,随你)
创建完后:
cd all-vue
npm install
npm run dev
打开浏览器访问 http://localhost:5173,你会看到一个默认的 Vite + Vue 欢迎页。
这一步我们就得到了一个开箱即用、架构清晰的标准 Vue 项目模板。
二、整体架构一览:看这张图就懂了
我自己画了一张简易架构图,帮助大家快速建立全局观:
整个项目就像搭积木一样,一层一层往上叠。
三、项目文件结构详解
创建好的项目大概长这样:
all-vue/
├── index.html # 整个应用的入口 HTML
├── package.json
├── vite.config.js
├── src/
│ ├── main.js # 应用入口文件
│ ├── App.vue # 根组件
│ ├── style.css # 全局样式
│ ├── components/ # 可复用组件目录
│ ├── views/ # 页面级组件目录(我们后面会加)
│ └── router/ # 路由配置(我们后面会加)
└── public/
└── vite.svg # 静态资源
我们一个个文件来看。
1. index.html —— 真正的“首页”
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>all-vue</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
重点看两行:
- < div id="app">:这就是 Vue 应用将来挂载的根节点。
- < cript type="module" src="/src/main.js"></ script>:关键!type="module" 说明我们用的是原生 ES Modules,这也是 Vite 能这么快的核心原因。Vite 在开发时直接把这个 script 当作入口启动服务器。
2. src/main.js —— 应用启动入口
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router' // 后面我们加的路由
createApp(App)
.use(router) // 启用路由插件
.mount('#app') // 挂载到 index.html 的 #app 上
这就是整个 Vue 应用的启动流程:
- 用 createApp 创建一个应用实例(基于根组件 App.vue)
- 安装需要的插件(比如 router)
- 把应用挂载到 DOM 的 #app 节点上
从这一刻起,页面就完全交给 Vue 管理了,我们再也不用手动操作 DOM(告别原生 DOM 编程)。
3. App.vue —— 根组件
<script setup>
// 这里可以写组件逻辑
</script>
<template>
<header>
<nav>
<ul>
<li><router-link to="/">Home</router-link></li>
<li><router-link to="/about">About</router-link></li>
</ul>
</nav>
</header>
<main>
<!-- 这里会渲染匹配到的页面组件 -->
<router-view></router-view>
</main>
<footer></footer>
</template>
<style scoped>
/* 组件局部样式 */
</style>
App.vue 是所有组件的“老大”。我们通常在这里放:
- 全局布局(header、footer、导航)
- :路由占位符,哪个页面匹配当前路径,就把哪个页面组件渲染在这里
四、如何实现多页面?—— 引入 vue-router
单页应用(SPA)的核心就是路由。Vue 官方推荐的路由库是 vue-router。
安装:
npm install vue-router@4
新建目录结构:
src/
├── views/ # 专门放页面级组件
│ ├── Home.vue
│ └── About.vue
└── router/
└── index.js # 路由配置文件
1.Home.vue 和 About.vue(页面组件)
<!-- Home.vue -->
<template>
<div>Home 页面内容</div>
</template>
<script>
export default {}
</script>
<style lang="scss" scoped>
</style>
About.vue 同理,只是内容换成 About。
2.router/index.js —— 路由配置核心
import { createRouter, createWebHashHistory } from 'vue-router'
import Home from '../views/Home.vue'
import About from '../views/About.vue'
const routes = [
{
path: '/', //跳转路径
name: 'Home', // 命名
component: Home // 跳转的组件
},
{
path: '/about',
name: 'About',
component: About
}
]
const router = createRouter({
history: createWebHashHistory(), // hash 模式,带 #,简单好用
routes
})
export default router
大括号里的两个东西:createRouter 和 createWebHashHistory
vue-router 这个包里导出了很多东西,我们只需要其中两个:
-
createRouter 字面意思就是“创建一个路由器”。 它是 vue-router 的核心函数,用来创建路由实例(也就是整个项目的路由管理系统)。 后面我们会调用它:
const router = createRouter({ ...配置 })得到一个 router 对象,然后在 main.js 里通过 app.use(router) 把路由功能挂到 Vue 应用上。
-
createWebHashHistory 这个是路由模式的工厂函数,决定你的 URL 长什么样子以及浏览器历史记录怎么管理。 它创建的是 Hash 模式 的历史管理器。
Hash 模式的特点:
-
URL 中会带一个 #,例如:http://localhost:5173/#/about
-
后面的部分叫 hash,浏览器不会把 hash 部分发送到服务器。
-
优点:兼容性最好,部署最简单,直接扔到任何静态服务器(甚至 GitHub Pages)都能正常路由切换,不需要服务器做特殊配置。
-
缺点:URL 看起来有点“老土”,带个 # 不够美观(不过对新手学习来说完全够用)。
vue-router 还支持其他模式,比如:
- createWebHistory() → History 模式(干净的 URL,如 /about),但生产部署时需要服务器配合(否则刷新会 404)。
- createMemoryHistory() → 内存模式,主要用于 SSR(服务器端渲染)或测试。
我们这里选 createWebHashHistory 是因为它最简单、最稳。
-
在main.js中,导入并使用:
import router from './router'
createApp(App).use(router).mount('#app')
现在访问 / 看到 Home,访问 /#/about 看到 About,页面切换完全不刷新,这就是单页应用(SPA)!
/:
/#/about:
五、package.json 和 package-lock.json:前端项目的“清单”和“锁单”
在每一个用 npm(或 yarn、pnpm)管理的现代前端项目里,你都会看到这两个文件:package.json 和 package-lock.json。它们长得很像,但作用完全不一样。我们用项目里的内容来通俗讲解一下。
1. package.json —— 项目清单(人为维护的“购物清单”)
这是项目最核心的配置文件,由我们自己(或创建工具)手动维护。
你的 package.json 内容:
{
"name": "all-vue",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"vue": "^3.5.24",
"vue-router": "^4.6.4"
},
"devDependencies": {
"@vitejs/plugin-vue": "^6.0.1",
"vite": "^7.2.4"
}
}
它主要记录了以下几类信息:
-
项目基本信息:名字(name)、版本(version)、是否私有(private)
-
运行脚本(scripts) : 你平时敲的命令其实都是这里定义的:
- npm run dev → 执行 vite(启动开发服务器)
- npm run build → 执行 vite build(打包生产代码)
- npm run preview → 执行 vite preview(本地预览打包后的项目)
-
依赖包(dependencies) : 项目运行时真正需要的包(上线后用户也会用到):
- vue: Vue 3 核心框架
- vue-router: 路由库
-
开发依赖(devDependencies) : 只在开发时需要的包(打包后不会带到生产环境):
- vite: 构建工具本身
- @vitejs/plugin-vue: Vite 的 Vue 插件,让 Vite 能正确处理 .vue 文件
-
type: "module" :告诉 Node.js 这个项目使用 ES Module 语法(import/export),而不是老的 CommonJS(require/module.exports)
总结:
package.json 就像你的“购物清单”,写清楚了“我这个项目要用哪些包,大概版本要多少”。
注意里面的版本号前面有 ^(脱字符号),表示允许小版本和补丁版本自动更新,比如 ^3.5.24 可以接受 3.5.x 到 3.6.0 以下的版本,但不会跳到 4.x。
2. package-lock.json —— 依赖锁文件(自动生成的“收据”)
这个文件非常长(你提供的就已经被截断了,实际可能有几万行),它是由 npm 自动生成和维护的,我们一般不手动修改它。
它的作用是:精确记录当前项目里每一个依赖包的具体版本、下载地址、完整依赖树以及完整性校验值。
在我的文件中可以看到:
- vue 实际安装的是 3.5.25(比 package.json 里写的 ^3.5.24 高了一点)
- vite 实际安装的是 7.3.0(比 ^7.2.4 高)
- 还有一大堆底层依赖(如 @babel/parser、esbuild 各种平台的二进制包等)的精确版本和下载源(这里用了 npmmirror.com 镜像)
为什么需要这个锁文件?
想象一下:
- 你今天 npm install,装好了 vue@3.5.25 和 vite@7.3.0,项目能正常跑。
- 过三个月,你或你的同事在新电脑上重新 npm install,如果没有 package-lock.json,npm 会去下载当时最新的符合 ^3.5.24 的版本,可能已经是 3.6.x 或更高,导致项目行为不一致,甚至出错。
- 有了 package-lock.json,npm 会严格按照里面记录的版本(比如必须是 3.5.25)去安装,保证每一次安装的结果都完全一样。
这就是“锁版本”的意思,确保团队成员、本地开发、CI/CD 服务器、生产部署的环境完全一致。
两者最主要的区别总结
| 项目 | package.json | package-lock.json |
|---|---|---|
| 谁维护 | 人为维护(我们自己改) | npm 自动生成和更新(我们不手动改) |
| 内容 | 粗略版本范围(带 ^ 或 ~) | 精确到具体小版本(如 3.5.25) |
| 大小 | 很小(几十行) | 很大(几千甚至上万行) |
| 作用 | 声明项目需要哪些依赖 | 锁定所有依赖的精确版本和依赖树,确保一致性 |
| 是否提交到 Git | 必须提交 | 必须提交(非常重要!) |
| 运行 npm install 时 | 根据这里去解析版本范围,然后下载最新符合的版本 | 优先读取这里,直接安装锁定的精确版本 |
| 可以手动修改吗 | 可以,也应该改(加新包、改版本) | 不建议手动改(容易出问题) |
实际开发中的建议
-
永远把这两个文件都提交到 Git 仓库(尤其是 package-lock.json)。
-
当你想添加新依赖时:
- 运行 npm install vue-router --save(会自动加到 dependencies)
- 或 npm install vite-plugin-inspect --save-dev(加到 devDependencies)
- npm 会自动更新 package.json 和 package-lock.json
-
看到别人项目里有 package-lock.json,就放心克隆下来 npm install,环境一定和别人一模一样。
简单记一句话:
- package.json 是你告诉 npm “我要买这些东西,大概这个牌子就行”。
- package-lock.json 是 npm 给你开的“详细收据”,写清楚了“我到底买了哪个具体版本,从哪儿买的,下次还买一样的”。
理解了这两个文件的区别,你就真正掌握了现代前端项目依赖管理的第一步!
六、前后端分离到底是怎么实现的?
很多人新手时最困惑的一点:Vue 项目怎么跟后端配合?为什么叫前后端分离?
简单来说:
- 传统后端渲染:用户访问一个 URL → 后端(Java、PHP 等)直接返回一个完整的 HTML 页面(里面已经把数据渲染好了)。
- 前后端分离(SPA) :用户访问任何一个 URL,都先返回同一个 index.html(里面只有一个空 ),然后由前端 Vue 通过 JavaScript 把页面内容动态渲染出来。
数据从哪里来?前端通过 Ajax / fetch / axios 向后端提供的 API 接口(通常是 RESTful 或 GraphQL)请求 JSON 数据,然后把数据渲染到页面上。
优势:
- 前端路由完全由 vue-router 掌控,用户切换页面体验像原生 App 一样流畅(不白屏)。
- 前后端职责清晰:后端只负责提供数据接口,前端负责页面展示和交互。
- 部署简单:前端项目打包后就是一堆静态文件(html/js/css),扔到 Nginx 或 CDN 上就行,后端接口部署在另一台服务器。
在我们这个项目里:
- 开发阶段:Vite 启动一个本地 dev server(localhost:5173),代理 API 请求到后端(vite.config.js 可以配置 proxy)
- 生产打包:运行 npm run build,会生成 dist 目录,里面全是静态文件,直接部署即可。
七、开发体验加分项:两个实用插件
-
Volar(VS Code 插件)
- Vue 官方出品,取代了旧的 Vetur
- 提供 < script setup> 语法的高亮、自动补全、类型提示
- 安装后你的 Vue 单文件组件写起来会非常舒服
-
Vue Devtools(Chrome/Firefox 插件)
- 可以直接在浏览器开发者工具里看到组件树、Vuex 状态、路由信息
- 调试 Vue 项目必备神器
总结:一个现代 Vue 项目的完整架构思维导图
- 底层基石:Node.js + npm 管理依赖
- 构建工具:Vite 负责开发服务器、热更新、打包
- 核心框架:Vue 3(组件化、响应式)
- 路由层:vue-router 实现单页应用多页面切换
- 状态管理(进阶):Vuex / Pinia(本文暂不展开)
- 开发工具:Volar + Vue Devtools 提升效率
- 部署方式:打包成静态文件,实现彻底前后端分离
掌握了这些,你就已经站在现代前端工程化的起跑线上了。
接下来你可以:
- 在 components 里写各种可复用组件
- 在 views 里添加更多页面
- 引入 Element Plus / Ant Design Vue 等 UI 库
- 学 Pinia 做全局状态管理
- 配置代理跨域,真正连上后端接口