一、写在前面
很多人学 Vue3,前期都是在一个很小的范围里练习:
-
写一个
App.vue -
写几个小组件
-
练练
ref -
练练
props -
做个 TodoList
这时候你对 Vue3 的理解,更多还是停留在:
一个组件怎么写。
但只要你开始接触稍微正式一点的项目,很快就会发现,真正的 Vue3 项目通常不是只有一两个文件,而是会出现一整套目录结构。
比如你会看到:
-
components -
views -
assets -
router -
stores -
api -
utils -
composables
这时候很多新手会本能地发懵:
-
为什么要分这么多目录?
-
这些目录是不是都必须有?
-
什么文件该放哪里?
-
为什么我明明知道单个组件怎么写,一进项目还是看不懂?
其实这很正常。
因为从这里开始,你面对的问题已经不再只是"语法",而是:
一个 Vue 项目该怎么组织。
而项目结构组织能力,恰恰是从"会写练习"走向"会做项目"的关键一步。
二、为什么 Vue 项目需要目录结构?
先说一个根本问题:
项目目录不是为了显得专业,而是为了让代码在变多之后仍然可控。
你写一个很小的练习时,可能一个 App.vue 就够了。
因为功能少、文件少、逻辑简单,放一起也还能接受。
但如果项目开始变大,比如有:
-
首页
-
登录页
-
详情页
-
用户中心
-
导航组件
-
表单组件
-
请求接口
-
状态管理
-
工具函数
这时候如果你还把所有内容都堆在一起,马上就会出问题:
-
文件难找
-
功能边界混乱
-
修改容易误伤
-
后期维护非常痛苦
所以项目结构存在的核心原因只有一个:
把不同职责的代码放到更合适的位置。
也就是说,项目结构本质上是在解决"代码分类"和"职责分层"的问题。
三、新手最容易误解的地方:目录不是越多越高级
这一点很重要。
很多新手第一次看到完整项目,会以为:
-
目录越多越规范
-
文件夹越细越专业
其实不是。
好的项目结构不是"复杂",而是"清楚"。
所以你一定要先建立这个认知:
项目结构的目标不是堆目录,而是让人容易找东西、容易理解、容易维护。
这意味着两件事:
1. 小项目不需要强行套大项目目录
2. 大项目也不能什么都乱堆在一个地方
也就是说,项目结构要和项目规模匹配。
四、先建立一个整体视角:一个 Vue3 项目通常由哪些层次组成?
你可以把一个比较常见的 Vue3 项目,粗略拆成下面几层:
1. 启动层
负责把项目跑起来
例如:
-
main.ts -
App.vue
2. 页面层
负责完整页面视图
例如:
views
3. 组件层
负责可复用的局部模块
例如:
components
4. 路由层
负责页面跳转关系
例如:
router
5. 状态层
负责共享数据管理
例如:
stores
6. 资源层
负责图片、样式等静态资源
例如:
-
assets -
public
7. 工具层
负责接口、工具函数、组合逻辑
例如:
-
api -
utils -
composables
这个分层一旦先有了整体感觉,后面你再看具体文件夹,就不会那么乱。
五、main.ts:项目启动入口
这个你前面其实已经接触过了。
最典型的写法通常是:
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
它的核心职责就是:
启动整个 Vue 应用。
在真实项目里,main.ts 往往还会顺便做一些全局接入工作,比如:
-
注册路由
-
注册 Pinia
-
引入全局样式
-
挂载全局插件
例如:
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import { createPinia } from 'pinia'
const app = createApp(App)
app.use(router)
app.use(createPinia())
app.mount('#app')
所以你可以把 main.ts 理解成:
项目的总启动开关。
六、App.vue:最外层根组件
App.vue 通常是整个项目最外层的根组件。
你可以理解成:
-
main.ts负责把应用启动起来 -
App.vue负责决定应用最外层长什么样
在简单项目里,App.vue 可能直接写页面内容。
但在更正式的项目里,它往往会更像一个"外壳"。
例如:
<template>
<router-view />
</template>
也就是说:
-
它不一定自己承担太多业务页面内容
-
而是作为页面切换和整体承接入口
所以你可以记成:
App.vue是项目最外层的组件容器。
七、components:可复用组件目录
这是 Vue 项目里最核心、最常见的目录之一。
它通常用来放:
可以在多个地方复用的局部组件。
比如:
-
按钮组件
-
用户卡片组件
-
搜索框组件
-
表格组件
-
弹窗组件
-
分页组件
-
侧边栏组件
-
顶部导航组件
这些组件的共同特点是:
-
它们不是一个完整页面
-
而是页面中的局部模块
-
通常具备复用价值
1. components 和前面讲的"组件化开发"是什么关系?
完全一致。
你前面学到:
-
页面可以拆成很多局部模块
-
这些模块可以封装成组件
那么这些组件文件,通常就放在 components 目录里。
例如:
src/
components/
SearchBar.vue
UserCard.vue
BaseButton.vue
2. components 里适合放什么?
你可以用一个非常实用的判断法:
如果它更像"页面零件",而不是"完整页面",就优先考虑放在
components。
例如:
-
搜索栏
-
用户资料卡片
-
商品项
-
评论列表项
-
通用弹窗
-
表格头
-
表单输入组件
这些都很典型。
八、views:页面级组件目录
这也是很多新手最容易和 components 混淆的地方。
先直接给结论:
views通常放"页面级组件",也就是和路由对应的页面。
例如:
-
首页
HomeView.vue -
登录页
LoginView.vue -
详情页
DetailView.vue -
用户中心
ProfileView.vue
这些组件的特点是:
-
它们通常对应一个完整页面
-
会被路由切换加载
-
内部可能还会再使用很多
components里的小组件
1. views 和 components 的最核心区别
这个一定要讲透。
views
更像"整页"
components
更像"页中的零件"
你可以把关系理解成:
一个
view页面,通常会由多个components组件组成。
比如一个首页 HomeView.vue 里,可能会使用:
-
Banner.vue -
SearchBar.vue -
ProductCard.vue -
FooterBar.vue
所以它们不是同一层的角色。
2. 一个很直观的类比
你可以把:
-
views看成"整栋房间" -
components看成"房间里的家具和功能模块"
这样就很好理解。
九、为什么小案例里经常没有 views,但正式项目里很常见?
因为小案例通常没有真正的多页面概念。
比如你练一个计数器、待办列表、学生管理小页,
可能一个 App.vue 就够了,根本不需要路由切换。
但只要项目变成:
-
首页
-
登录页
-
列表页
-
详情页
这种多页面应用,就会很自然地需要一个 views 目录来集中放页面组件。
所以:
views往往是随着"路由页面"一起出现的。
十、assets:项目内静态资源目录
assets 通常用来放:
-
图片
-
图标
-
字体
-
局部样式文件
-
一些需要被构建工具处理的静态资源
比如:
src/
assets/
logo.png
banner.jpg
main.css
你可以把它理解成:
和项目源码关系比较紧密、会参与打包处理的资源文件目录。
1. 常见会放哪些东西?
例如:
-
页面中引用的图片
-
组件内部用到的图标
-
全局样式文件
-
CSS/SCSS 文件
-
字体文件
这在 Vue 项目里非常常见。
十一、public 和 assets 有什么区别?
这是新手高频问题。
两者都能放静态资源,但它们的定位不完全一样。
assets
更偏向:
作为源码的一部分,被项目构建过程管理。
通常适合放:
-
组件中引用的图片
-
项目内部样式资源
-
跟源码关系密切的静态文件
public
更偏向:
原样保留、不经过源码模块系统管理的公共资源。
通常适合放:
-
网站 favicon
-
不需要 import 的静态文件
-
一些直接通过路径访问的公共资源
直观理解
你前期可以先简单记成:
-
组件里常引用的资源 ,多半放
assets -
公共直接访问的资源 ,可以放
public
对新手来说,先建立这个层级感就够了。
十二、router:路由配置目录
只要你的项目涉及多页面切换,几乎就会出现 router。
它的核心职责是:
定义页面和路径之间的对应关系。
比如:
-
/对应首页 -
/login对应登录页 -
/profile对应个人中心页
典型写法会像这样:
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
import LoginView from '../views/LoginView.vue'
const router = createRouter({
history: createWebHistory(),
routes: [
{ path: '/', component: HomeView },
{ path: '/login', component: LoginView }
]
})
export default router
所以你可以把 router 目录理解成:
整个项目的页面导航地图。
1. 为什么它很重要?
因为只要项目不是单页面小练习,而是真正的"多个页面切换",
就一定需要一个集中管理路由关系的地方。
而 router 的出现,会让你开始真正进入"应用级项目"的感觉。
十三、stores:全局状态管理目录
这个目录通常和 Pinia 配合出现。
它的核心职责是:
存放多个页面或多个组件之间需要共享的状态。
例如:
-
当前登录用户信息
-
购物车数据
-
主题状态
-
权限信息
-
全局计数或缓存数据
如果这些数据只是某个组件自己用,那放组件内部就行。
但如果很多地方都要用,就适合放到 stores 里统一管理。
1. 为什么不直接组件里各写各的?
因为一旦数据需要跨组件、跨页面共享,
如果你还靠层层传 props,或者每个组件自己维护一份,就会非常混乱。
所以状态管理目录的本质意义在于:
集中管理"大家都要用"的共享状态。
2. stores 通常长什么样?
例如:
src/
stores/
user.ts
counter.ts
每个文件通常负责一类共享状态。
比如:
-
user.ts管用户登录信息 -
cart.ts管购物车 -
theme.ts管主题模式
这会让全局状态职责清晰很多。
十四、api:接口请求目录
不是所有教程都一定会先建这个目录,
但在稍微正式一点的项目里,它非常常见。
它通常用来放:
和后端接口交互相关的请求函数。
例如:
-
登录接口
-
获取用户信息接口
-
获取商品列表接口
-
删除学生接口
你可以把它理解成:
把所有"请求后端"的代码集中放在一起。
这样做的好处是:
-
页面组件更干净
-
接口逻辑统一管理
-
后期维护和修改更方便
一个简单例子
import request from './request'
export const getUserInfo = () => {
return request.get('/user/info')
}
然后页面中再去调用它。
这比直接在页面里到处写请求代码要清晰很多。
十五、utils:工具函数目录
这个目录通常放:
和业务页面无强绑定、可复用的通用工具函数。
比如:
-
日期格式化
-
金额格式化
-
防抖节流
-
字符串处理
-
数组去重
-
本地存储封装
例如:
export const formatDate = (date) => {
// 日期格式化逻辑
}
它的核心特征是:
-
不是页面本身
-
不是组件本身
-
不是状态本身
-
而是很多地方都可能会用到的小工具
所以把它们集中放在 utils 会很自然。
十六、composables:组合式逻辑复用目录
这个目录是 Vue3 组合式 API 时代很常见的一个目录。
它通常用来放:
可复用的组合逻辑函数。
比如你把一组逻辑封装成:
-
获取鼠标位置
-
处理分页逻辑
-
表单校验逻辑
-
搜索逻辑
-
倒计时逻辑
这些逻辑本质上不是完整组件,
但它们又是可以被多个组件共用的。
这时候就很适合放在 composables 里。
例如:
export function useCounter() {
const count = ref(0)
const add = () => count.value++
return { count, add }
}
这就是典型的组合式逻辑封装。
你前期不一定马上大量使用它,
但知道这个目录的存在非常有帮助。
十七、一个比较常见的 Vue3 项目目录长什么样?
你可以先参考这种理解结构:
src/
assets/
components/
views/
router/
stores/
api/
utils/
composables/
App.vue
main.ts
这里每个目录的角色大致就是:
-
assets:资源 -
components:局部可复用组件 -
views:页面 -
router:路由 -
stores:全局状态 -
api:接口 -
utils:工具函数 -
composables:组合式逻辑复用 -
App.vue:根组件 -
main.ts:启动入口
你只要先把这个"角色地图"记住,很多项目就能看懂个大概。
十八、小项目和正式项目目录为什么差别这么大?
这是新手特别容易困惑的一点。
原因其实很简单:
项目规模不同,结构需求就不同。
小项目可能只有:
src/
App.vue
main.ts
components/
因为功能简单,不需要更多分层。
而正式项目可能需要:
-
多页面
-
路由
-
状态共享
-
请求接口
-
工具封装
-
资源分类
那目录自然就会丰富起来。
所以不要误以为:
-
小项目目录少就是不规范
-
大项目目录多就是高级
真正合理的标准是:
目录结构是否和项目复杂度匹配。
十九、新手最常见的项目结构误区
这一部分很重要。
1. 什么都往 components 里塞
这是最高频问题之一。
很多新手一开始学组件化后,会把:
-
页面
-
弹窗
-
表格
-
路由页面
-
大块布局
全都塞进 components
结果后面越来越难区分:
-
哪个是局部模块
-
哪个是整页
-
哪个才是路由页面
所以一定要逐渐学会区分:
-
components更偏零件 -
views更偏整页
2. 什么都放在 App.vue
这在初学阶段非常常见,但不能长期这样。
一旦你发现 App.vue 里开始同时承担:
-
页面内容
-
列表逻辑
-
路由逻辑
-
组件组合
-
表单逻辑
那就说明应该考虑拆分和分层了。
3. 盲目照搬大项目目录
有些人做一个很小的练习,也硬建:
-
api -
stores -
utils -
composables -
hooks -
layouts
结果项目本身没多大,目录却特别复杂。
这不是规范,而是过度工程化。
4. 文件命名和目录职责混乱
比如:
-
页面组件叫得像通用组件
-
通用组件又放到页面目录
-
工具函数随手放某个组件里
这样会让项目边界越来越糊。
所以从一开始就要有意识地让:
-
目录职责清楚
-
文件命名有语义
二十、你现在最需要建立的项目结构思维是什么?
我建议你先建立下面这几个意识。
1. 项目结构本质上是代码分类
不是形式主义。
2. 页面和组件不是一回事
views 和 components 要逐渐分开理解。
3. 路由、状态、接口、工具,最好各有位置
这样项目长大后不会乱。
4. 目录不是越多越好,而是越清楚越好
始终围绕"方便维护"去设计。
只要这几个意识起来了,你后面看任何 Vue3 项目都会更顺。
二十一、这一篇学完后,你应该达到什么程度?
如果你把这篇真正理解了,至少应该做到:
-
知道
main.ts是项目启动入口 -
知道
App.vue是最外层根组件 -
知道
components放局部复用组件 -
知道
views放页面级组件 -
知道
assets和public的基本区别 -
知道
router负责页面路径关系 -
知道
stores负责共享状态 -
知道
api、utils、composables分别大致做什么 -
知道项目目录不是越复杂越好,而要和项目规模匹配
只要这些点清楚了,你对 Vue3 的理解就会从"单个组件怎么写",进一步升级到:
一个完整项目大概是怎么组织起来的。
二十二、总结
这一篇文章,我们把 Vue3 项目结构中最核心的一批目录系统梳理了一遍。
最重要的主线可以浓缩成下面几句话:
-
main.ts:启动项目 -
App.vue:最外层根组件 -
components:局部可复用组件 -
views:页面级组件 -
assets/public:静态资源 -
router:页面路径管理 -
stores:共享状态管理 -
api:接口请求封装 -
utils:工具函数 -
composables:组合式逻辑复用
如果说前面的文章让你逐渐学会了:
-
写页面
-
写组件
-
做组件通信
-
会基础调试
那么这一篇的作用,就是让你开始真正具备:
看懂一个 Vue3 项目骨架的能力。
这一步很关键,因为接下来我们就要正式进入"应用级开发"里非常核心的一层了:路由与全局状态管理。