目录
[一. Vue项目构成及核心文件解读](#一. Vue项目构成及核心文件解读)
[1.1 assest 静态资源文件](#1.1 assest 静态资源文件)
[1.2 utils 工具包](#1.2 utils 工具包)
[1.3 api,controller,http 包](#1.3 api,controller,http 包)
[1.4 view,page 包的页面组件](#1.4 view,page 包的页面组件)
[1.5 component 公共组件、UI组件](#1.5 component 公共组件、UI组件)
[1.6 router/index.js------路由配置](#1.6 router/index.js——路由配置)
[1.7 store/index.js------状态管理](#1.7 store/index.js——状态管理)
[1.8 app.vue](#1.8 app.vue)
[1.9 main.js 应用初始化脚本](#1.9 main.js 应用初始化脚本)
[1.10 HTML 应用入口容器文件](#1.10 HTML 应用入口容器文件)
[二. Vue 项目启动文件加载流程及依赖分析](#二. Vue 项目启动文件加载流程及依赖分析)
在接手一个Vue项目时,合理的项目结构可能让我们非常迅速地对一个项目进行了解;此外,一个和良好的项目结构,对于项目后期的维护性和扩展性都是极有帮助的。下面我们就一起来看一个相对完整的Vue项目吧!
一. Vue项目构成及核心文件解读

如上图所示,就是一个相对完整的Vue项目,大致可以分为10部分,我都一一对应做出了标注。
下面在介绍的时候,不会按照图片标注的顺序来进行说明,我们先从代码、图片文件介绍,再介绍项目结构、配置文件,方便同学们更好理解。
1.1 assest 静态资源文件
作用:存放静态资源文件
这个应该是最好理解的了,里面通常会存放一些项目图片,网站图标,CSS样式文件,字体文件等。如果将文件夹展开,通常是下面这样,assest 大文件夹包含四个小文件夹,每个文件夹下存放对应的资源文件,这里就不展开说了,很好理解。
需要注意的一点是,在有一些中型以及大型项目中,由图片、图标、CSS、字体设置文件由于数量较多,会分为四个顶层文件夹来存储 ,就不再全部或部分存储到 assest 文件夹中啦,这一点小伙伴们可以注意一下,以后遇到了其他的Vue项目发现不一样也不用惊奇,这四类文件位置放哪都可以,但一般要么放一起,要么分四个放,要么部分放一起,都无所谓的。

1.2 utils 工具包
作用:存放工具类JS文件
这个基本上也不用怎么说,就是存放一些提前定义好的工具类JS文件,JS方法,类似于spring boot 项目的 utils 工具包,存放各种定义好的Java方法。
下面是我已经写好的一些JS文件,可以简单看下,从名字基本就能看出来是干什么用的,Date就是处理时间的,math是数学运算的......

1.3 api,controller,http 包
作用:存放与后端交互的请求函数 ,接口规范JS文件
向后端发送请求时,通常需要设置请求参数、请求方式、请求地址,这些通常都会在JS文件中提前定义好。其实之所以叫 api,controller,http 是因为在不同的项目中,或因个人习惯的不同,这个 request 请求包通常会有不同的命名方式,但比较常见的是以上三种,
如下图所示,我简单展示该包下的文件。

然后我打开 baseCode.js 文件,小伙伴们可以简单看一下,不难发现,里面都是已经提前定义的接口规范,例如方法名称、参数值、参数处理、URL地址、HTTP请求类型等,然后使用 export 导出,这样就可以在其它Vue组件中导入使用,类似于Java后端的 import 导包,这种做法极大的提高了代码的可复用性,只定义一个HTTP函数,在项目多处都可以导入使用,极大程度上做到了我们开发中常说的低耦合!
javascript
import request from '@/utils/request'
import PromiseCache from '@/utils/PromiseCache'
const queryLikeCache = new PromiseCache()
const queryInCache = new PromiseCache()
export function queryLike(codeType, query, otherConditions) {
const key = JSON.stringify({ codeType, query, otherConditions })
return queryLikeCache.getCached(key, () => {
return request({
url: '/baseCode/queryLike',
method: 'post',
data: { codeType, query, otherConditions }
})
})
}
export function queryIn(codeType, codes) {
const key = JSON.stringify({ codeType, codes })
return queryInCache.getCached(key, () => {
return request({
url: '/baseCode/queryIn',
method: 'post',
data: { codeType, codes }
})
})
}
在以往的 JavaScript 代码中,如下代码所示,通常都是直接在 JavaScript 代码中使用 Ajax、Axios 发送 HTTP 请求,这种做法就显得不够优雅,如下,envConfig.api.ip + config.backend.companyInfo 是已经提前封装在其他配置文件中了,所以显得简洁一些,如果没有封装,就会变得非常杂乱。
javascript
$http.post(envConfig.api.ip + config.backend.companyInfo, {
"searchKey": $scope.clientData.insuredName
}).then(function(res){
if(res.data.VerifyResult=='1'){
$scope.clientData.smallMicroBusinesses='1';
if($scope.companyData){
$scope.companyData.smallMicroBusinesses = '1';
}
parentScope.proposal.prpTmainVo.smallMicroBusinesses='1';
}else {
$scope.clientData.smallMicroBusinesses='0';
if($scope.companyData){
$scope.companyData.smallMicroBusinesses = '0';
}
parentScope.proposal.prpTmainVo.smallMicroBusinesses='0';
}
});
当我们使用了 export 导出方法和 import 导入之后,代码就可以变成下面这个样子。
首先第一步:导包,将提前定义好的 HTTP 规范函数导进来;
其次第二步:编写HTML这种页面元素,绑定 button1 单击函数,传递查询参数;
最后第三步:在 vue 的 method 列表中添加 button1 函数,在函数中注解调用已经导入进来的方法 queryLike,传递查询参数;
如此再来看,在整个文件中,几乎看不出来有调用 HTTP 的痕迹,全是纯代码逻辑编写,代码之间的耦合度也降低了,非常的优雅!
javascript
1. 导包部分
import { queryLike } from '@/api/common/baseCode'
2. 页面部分
<el-button :disabled="editDisableds" @click="button12()">)</el-button>
3. vue中的 method 方法
methods: {
button1() {
queryLike('feeMethod', this.intermediaryjg.feeMethod, {}).then((response) => {
for (const item of response.data) {
console.log(item.cname)
this.intermediaryjg.formula = this.intermediaryjg.formula + '' + item.cname
}
})
},
}
1.4 view,page 包的页面组件
作用:存放向用户实际展示的页面级别Vue组件(通常与路由对应)
简单来讲,就是之前的 .HTML 文件,有一点不同的是,在 vue 项目中,通常不会在使用 .html 文件,而是使用 .vue 文件,而且通常会搭配 elemenUI 组件一同使用,这里没什么好解释的。
此外,我这里将软件包定义为 view ,在一些其他项目中,因为项目开发习惯的不同,有些人会将软件包定义为 page,所以同学们下次见到不必大惊小怪,二者一般情况下都可以用来存放页面文件。
在这个软件包下,通常不同的模块还会定义子包,比如用户模块的页面文件都放在 user 包下;orders 订单模块的页面文件都放在 orders 包下;后台管理页面文件都放在 admin 包下;

1.5 component 公共组件、UI组件
作用:存放可复用的 Vue 组件
组件是 Vue 项目中一个非常重要的组成部分,广义上来说,任何一个定义的以 .vue 结尾的文件,都可以被看作是一个 Vue 组件,只需要在文件中使用如下语法,就可以把当前 .vue 文件作为一个组件使用导入到其他文件中或被其组件嵌套使用。
javascript
export default {
// 1. 组件标识:当前要导出的组件名称,自定义名称,便于调试和递归
name: 'UserCard',
// 2. 数据管理:内部用来定义双向绑定的数据
data() {
return {
isExpanded: false
}
},
// 3. 方法:就是自定义的方法函数,都写在 methods 里面
methods: {
toggleExpand() {
this.isExpanded = !this.isExpanded
}
},
// 4. 组件注册:组件之间可以相互调用,import 导入后就在这里面声明
components: {
UserAvatar,
UserInfo
}
// 5. 属性接收 (Props),可以用于验证、校验数据是否输入正确
props: {
user: {
type: Object,
required: true
}
},
}
在 Vue 项目中,组件也是有多种多样的,我就列举了两类比较常见的,一个是公共组件,一个是UI组件。
其实二者区别不大,指示作用不太相同,公共组件类似于Java后端代码的 common 公共包,里面定义一些比较通用的组件;UI组件也是一样,就是提前定义好的一些界面样式,有时候很多网页结构都是非常相似的,这个时候我们就可以将网页结构整体提出来,然后在每一个 .vue 文件中进行导入直接复用,提高了代码的质量,降低了冗余度。
如下图,就是组件包展开的样子,一般可以定义两个子包,layout 通常存放公共组件;ui 就用来存放UI组件。

1.6 router/index.js------路由配置
作用:存放路由配置(路径与组件的映射),管理页面跳转和路由守卫。
该文件夹下通常只含有一个核心文件 index.js ,index.js 文件中会定义所有的页面组件和路径的映射关系。
注意!!!这里所指的页面组件就是直接展示给用户浏览的页面,基本上就是 view、page包下的页面组件,而公共组件包是不映射的,具体是否映射见下表格。
|----------------------------------------------------|-----------------------------------------------|
| 需要路由定义的组件 | 不需要路由定义的组件 |
| (1) 通过URL直接访问的页面; (2) 需要深度链接的页面; (3) 需要浏览器导航功能的页面; | (1)纯UI组件:按钮/卡片; (2)业务子组件:表单/列表; (3)弹窗/抽屉等临时组建 |
举个栗子更好理解:如下图所示,我的 view 页面定义了几个页面组件,admin 管理员界面、OrderCenter 订单中心页面、Login 登陆页面等都是需要在浏览器直接展示的;

如果映射关系对应的 index.js 文件中,代码如下:path 就是浏览器要展示URL路径地址,然后对应 component 中所写的页面组件,这样一来就可以通过此路由文件来控制展示给用户的界面了。此外,404Not Found 页面通常要写在末尾,否则会报错,这一点要注意!
javascript
// 1. 导入需要用的组件
import VueRouter from 'vue-router';
// 2. 定义路由映射关系
// path:自定义的,就是浏览器上显示的URL路径;
// name:自定义名称,通常可以命名为文件名称;
// component: 编写组件所在的文件路径
const routes = [
{
path:'/admin/admin1',
name:'admin1',
component:()=>import('../view/admin/admin1.vue')
},
{
path:'/orders/OrderCenter',
name:'OrderCenter',
component:()=>import('../view/orders/OrderCenter.vue')
},
{
path:'/user/Login',
name:'Login',
component:()=>import('../view/user/Login.vue')
},
{
path:'/user/UserCenter',
name:'UserCenter',
component:()=>import('../view/user/UserCenter.vue')
},
{
path:'/Home',
name:'Home',
component:()=>import('../view/Home.vue')
},
{
path:'/404',
name:'404',
component:()=>import('../view/404.vue')
},
]
// 3. 创建路由实例
const router = new VueRouter({
mode:'history',
routes
})
// 4. 导出当前路由组件,供其它组件使用
export default router;
另外一个小芝士,component 组建的路径定义中,我使用了 ".." ,表示当前文件夹的上一层,就拿 admin.vue 路由举例,有些小伙伴可能见过下面这种写法。
javascript
{
path:'/admin/admin1',
name:'admin1',
component:()=>import('@/view/admin/admin1.vue')
},
其实在这里 "@" 和 ".." 表述的意思相近,".." 表示当前文件夹的上一层,就是 view 文件的上一层 src,是相对路径。
而"@"是 webpack/Vite 的路径别名,默认指向项目根目录下的 src 目录,与文件位置无关,是绝对路径。
二者的区别如下表格
|---------------|------------------------------|-----------------------------|
| 特性 | .. (相对路径) | @ (绝对路径) |
| 解析基础 | 基于当前文件位置 | 基于工程根目录 |
| 是否受文件位置影响 | 是 (文件移动会导致路径失效) | 否 (全局有效) |
| 推荐度 | ★★☆☆☆ | ★★★★★ |
| 配置位置 | 无需配置 | 需在 webpack/vite 中配置别名 |
| 典型路径 | ../view/admin/admin1.vue | @/view/admin/admin1.vue |
小编这里图省事,使用了 ".." ,但小编个人更推荐 "@",只是使用"@"需要注意一点,要提前在
如果使用 Vue2 ,要提前在 vue.config.js 中配置@别名,如下所示
javascript
const path = require('path')
module.exports = {
configureWebpack: {
name: name,
resolve: {
alias: {
'@': path.resolve( 'src'),
}
},
}
}
如果使用 Vue3 ,要提前在 vite.config.js 中配置@别名,如下所示
javascript
import { defineConfig } from 'vite'
import path from 'path'
export default defineConfig({
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
}
}
})
1.7 store/index.js------状态管理
作用:集中管理应用状态(如用户信息、全局配置),提供响应式数据和状态变更方法
使用 Pinia(Vue 3 推荐)或 Vuex。
示例代码如下:
javascript
// store/index.js (Pinia 示例)
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
actions: {
increment() {
this.count++
}
}
})
在经过 export 导出之后, 当前组件会被 main.js 文件注册并导入到 Vue 应用中。
其他组件通过 useStore()
(Pinia)或 this.$store
(Vuex)访问状态。
1.8 app.vue
作用:应用顶级组件,所有页面的容器;
app.vue 内部通常定义全局布局(如导航栏/页脚),全局样式等,包含路由视图容器 <router-view>
javascript
<template>
<div id="app">
<!-- 全局导航栏 -->
<nav>
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</nav>
<!-- 路由页面渲染区 -->
<router-view/>
<!-- 全局页脚 -->
<footer>© 2025 ZhangSir</footer>
</div>
</template>
<script>
// 导出当前 app 根容器,然后在 main.js 文件中导入使用
export default {
name: 'App',
// 可在此添加全局逻辑(如用户登录状态检查)
}
</script>
<style scoped>
/* 全局样式 */
nav { padding: 20px; }
footer { margin-top: 50px; }
</style>
1.9 main.js 应用初始化脚本
作用:创建Vue应用实例、集成全局插件(路由Router、状态管理Store等)、挂载应用到DOM
如下实例代码,
第一步:导包。将我们上面定义的路由router、状态管理store、Vue根容器以及一些其他第三方组件如ElementUI等全部导入;
**第二步:注入。**创建一个 Vue 实例,并将导入的包全部注入到 Vue 实例中;
**第三步:挂载。**将 Vue 实例挂载到 id = "app" DOM节点上(此处的DOM节点通常就是指我们下面 index.html 文件中 id = "app" 的 div 元素块)。
javascript
import { createApp } from 'vue'
import App from './App.vue'
import router from './router' // 导入路由配置
import store from './store' // 导入状态仓库
const app = createApp(App)
// 注入路由系统
app.use(router)
// 注入状态管理
app.use(store)
// 挂载到id="app"的DOM节点
app.mount('#app')
// 可选:全局错误处理
app.config.errorHandler = (err) => {
console.error('全局错误:', err)
}
1.10 HTML 应用入口容器文件
作用:整个单页面应用(SPA)的HTML基础模板、提供Vue应用挂载的根节点(<div id="app">
)、引入全局CSS/JS资源(如字体、SDK等)。
是整个Vue文件的基础模板,通常含有一个根元素,提供上方创建的 vue 应用挂载的根节点(<div id = "app">),Vue 应用会挂在到这个元素上。此外,还可以引入打包后的JavaScript、CSS文件作为全局的CSS、JS资源(如字体、SDK等)。
示例代码如下
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>My Vue App</title>
<!-- 引入全局样式 -->
<link rel="stylesheet" href="https://cdn.example.com/font.css">
<!-- 引入打包后的JS文件 -->
<script src="./dist/main.js"></script>
</head>
<body>
<!-- Vue应用挂载点 -->
<div id="app"></div>
<!-- 构建工具会自动注入打包后的JS文件 -->
<script type="module" src="/src/main.js"></script>
</body>
</html>
二. Vue 项目启动文件加载流程及依赖分析
-
index.html 文件被浏览器加载,其中包含一个挂载点,通常为<div id = "app">和引入 main.js 的脚本文件;
-
main.js 文件执行:
导入 Vue 库、导入 App.vue 作为根组件、导入 router 和 store、创建 Vue 实例并将 router 和 store 注入到 Vue 中,再渲染 app.vue。
- app.vue 被渲染
在模板中可能含有全局组件(如<NavBar> 和 <router-view>);此时,router开始根据文件内的URL地址匹配路由;
-
router 根据路由配置,加在对应的 view 页面组件,如 Home.vue;
-
view 组件被渲染
在 view 页面组件中,极大概率会引用 component 中的子组件;
在`created`或`mounted`钩子中,可能通过`api`调用接口,或通过`store`访问/修改全局状态;
-
**store** 中的actions可能调用`api`模块,`api`模块使用`utils`中的请求工具发送请求;
-
**components** 组件在`views`中被使用,它们可能使用`assets`中的资源(如图片)或`utils`中的工具函数;
综合上面的话,大致可以总结为下面这张图

各个模块可能出现的依赖关系如下表格
|-------------------------|--------------------------------------------------------------------------------------|---------------------------------------------------|
| 模块/组件/文件名称 | 依赖模块 | 被依赖模块 |
| index.html | 无 | main.js (作为入口文件被加载) |
| main.js | app.vue (根组件) router (路由系统) store (状态管理系统) assest (静态资源文件) utils (工具包) | 无 (是项目入口,不被其他文件依赖) |
| App.vue | router (使用<router-view>) conponent (全局布局组件) assest (应用级样式) | main.js (作为根组件被加载) |
| router(router/index.js) | view/*.vue (路由组成) store (路由守卫使用 Vuex) | main.js (初始化) app.vue (提供路由视图容器) |
| store(store/index.js) | api (调用网络请求) utils (数据处理工具,各种工具类方法) | main.js (初始化) view/*.vue (使用状态) component (使用状态) |
| view(页面组件) | conponent (使用子组件) api (请求调用) store (使用 Vuex) utils (页面工具,各种方法) assest (静态资源,多为图片图标等) | router (被路由引用) |
| component(可复用组件) | assest (组件资源) utils (组件工具) 其它 component (组件之间可以互相嵌套) | view (被页面引用) App.vue (全局组件) |
| api(请求函数模块) | utils工具模块(如请求拦截器、数据处理等) | store (Vuex action调用) view (页面组件调用) |
| assest(静态资源模块) | 无 | App.vue、view 、component |
| utils(工具包) | 无 | api、store、view、component |
依赖循环处理,在有些项目中,会出现 store 依赖 utils,同时 utils 也依赖 store 的情况,
解决方案:
1:依赖注入,在 main.js 文件中初始化工具
javascript
// main.js
import utils from '@/utils'
import store from './store'
utils.setStore(store) // 注入store依赖
2:延迟加载,在函数内部 require
javascript
// utils/auth.js
export const getToken = () => {
const store = require('@/store') // 动态引入
return store.state.token
}
3:架构重组,将共享功能提取到独立模块