Vue3 项目的基本架构解读

Vue3 项目的基本架构解读 目录一. Vue项目构成及核心文件解读1.1 assest 静态资源文件1.2 utils 工具包1.3 apicontrollerhttp 包1.4 viewpage 包的页面组件1.5 component 公共组件、UI组件1.6 router/index.js——路由配置1.7 store/index.js——状态管理1.8 app.vue1.9 main.js 应用初始化脚本1.10 HTML 应用入口容器文件二. Vue 项目启动文件加载流程及依赖分析在接手一个Vue项目时合理的项目结构可能让我们非常迅速地对一个项目进行大概的了解此外一个和良好的项目结构对于项目后期的维护性和扩展性都是极有帮助的。所以下面我们就通过一个相对完整的 Vue 项目了解下通常都具有哪些模块吧一. Vue项目构成及核心文件解读如上图所示就是一个相对完整的 Vue 项目大致可以分为10部分我都对应做出了标注下面在介绍的时候不会按照图片标注的顺序来进行说明我们先从代码、图片文件介绍再介绍项目结构、配置文件方便同学们更好理解。1.1 assest 静态资源文件作用存放静态资源文件这个应该是最好理解的了里面通常会存放一些项目图片网站图标CSS样式文件字体文件等。如果将文件夹展开通常是下面这样assest 大文件夹包含四个小文件夹每个文件夹下存放对应的资源文件这里就不展开说了很好理解。需要注意的一点是在有一些中型以及大型项目中由于 图片、图标、CSS、字体设置文件由于数量较多会分为四个顶层文件夹来存储就不再全部或部分存储到 assest 文件夹中啦这一点小伙伴们可以注意一下以后遇到了其他的Vue项目发现不一样也不用惊奇这四类文件位置放哪都可以但一般要么放一起要么分四个放要么部分放一起都无所谓的。1.2 utils 工具包作用存放工具类JS文件这个基本上也不用怎么说就是存放一些提前定义好的工具类JS文件JS方法类似于spring boot 项目的 utils 工具包存放各种提前写好的 JavaScript 函数。下面是我已经写好的一些JS文件可以简单看下从名字基本就能看出来是干什么用的Date就是处理时间的math是数学运算的......1.3 apicontrollerhttp 包作用存放与后端交互的请求函数接口规范JS文件向后端发送请求时通常需要设置请求参数、请求方式、请求地址这些通常都会在JS文件中提前定义好。其实之所以叫 apicontrollerhttp 是因为在不同的项目中或因个人习惯的不同这个 request 请求包通常会有不同的命名方式但比较常见的是以上三种如下图所示我简单展示该包下的文件。然后我打开 baseCode.js 文件小伙伴们可以简单看一下不难发现里面都是已经提前定义的接口规范例如方法名称、参数值、参数处理、URL地址、HTTP请求类型等使用 export 导出这样就可以在其它Vue组件中导入使用类似于Java后端的 import 导包这种做法极大的提高了代码的可复用性只定义一个HTTP函数在项目多处都可以导入使用极大程度上做到了开发中常说的低耦合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 是已经提前封装在其他配置文件中了所以显得简洁一些如果没有封装就会变得非常杂乱。$http.post(envConfig.api.ip config.backend.companyInfo, { searchKey: $scope.clientData.insuredName }).then(function(res){ if(res.data.VerifyResult1){ $scope.clientData.smallMicroBusinesses1; if($scope.companyData){ $scope.companyData.smallMicroBusinesses 1; } parentScope.proposal.prpTmainVo.smallMicroBusinesses1; }else { $scope.clientData.smallMicroBusinesses0; if($scope.companyData){ $scope.companyData.smallMicroBusinesses 0; } parentScope.proposal.prpTmainVo.smallMicroBusinesses0; } });当我们使用了 export 导出方法和 import 导入之后代码就可以变成下面这个样子。首先第一步导包将提前定义好的 HTTP 规范函数导进来其次第二步编写HTML这种页面元素绑定 button1 单击函数传递查询参数最后第三步在 vue 的 method 列表中添加 button1 函数在函数中注解调用已经导入进来的方法 queryLike传递查询参数如此再来看在整个文件中几乎看不出来有调用 HTTP 的痕迹全是纯代码逻辑编写代码之间的耦合度也降低了非常的优雅1. 导包部分 import { queryLike } from /api/common/baseCode 2. 页面部分 el-button :disablededitDisableds clickbutton12())/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 viewpage 包的页面组件作用存放向用户实际展示的页面级别Vue组件通常与路由对应简单来讲就是之前的 .HTML 文件有一点不同的是在 vue 项目中通常不会在使用 .html 文件而是使用 .vue 文件而且通常会搭配 elemenUI 组件一同使用这里没什么好解释的。此外我这里将软件包定义为 view 在一些其他项目中因为项目开发习惯的不同有些人会将软件包定义为 page所以同学们下次见到不必大惊小怪二者一般情况下都可以用来存放页面文件。在这个软件包下通常不同的模块还会定义子包比如用户模块的页面文件都放在 user 包下orders 订单模块的页面文件都放在 orders 包下后台管理页面文件都放在 admin 包下1.5 component 公共组件、UI组件作用存放可复用的 Vue 组件组件是 Vue 项目中一个非常重要的组成部分广义上来说任何一个定义的以 .vue 结尾的文件都可以被看作是一个 Vue 组件只需要在文件中使用如下语法就可以把当前 .vue 文件作为一个组件使用导入到其他文件中或被其组件嵌套使用。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 页面通常要写在末尾否则会报错这一点要注意// 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 路由举例有些小伙伴可能见过下面这种写法。{ 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 中配置别名如下所示const path require(path) module.exports { configureWebpack: { name: name, resolve: { alias: { : path.resolve( src), } }, } }如果使用 Vue3 要提前在 vite.config.js 中配置别名如下所示import { defineConfig } from vite import path from path export default defineConfig({ resolve: { alias: { : path.resolve(__dirname, ./src), } } })1.7 store/index.js——状态管理作用集中管理应用状态如用户信息、全局配置提供响应式数据和状态变更方法使用 PiniaVue 3 推荐或 Vuex。示例代码如下// 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.$storeVuex访问状态。1.8 app.vue作用应用顶级组件所有页面的容器app.vue 内部通常定义全局布局如导航栏/页脚全局样式等包含路由视图容器router-viewtemplate div idapp !-- 全局导航栏 -- nav router-link to/Home/router-link | router-link to/aboutAbout/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; } /style1.9 main.js应用初始化脚本作用创建Vue应用实例、集成全局插件路由Router、状态管理Store等、挂载应用到DOM如下实例代码第一步导包。将我们上面定义的路由router、状态管理store、Vue根容器以及一些其他第三方组件如ElementUI等全部导入第二步注入。创建一个 Vue 实例并将导入的包全部注入到 Vue 实例中第三步挂载。将 Vue 实例挂载到 id app DOM节点上(此处的DOM节点通常就是指我们下面 index.html 文件中 id app 的 div 元素块)。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) // 挂载到idapp的DOM节点 app.mount(#app) // 可选全局错误处理 app.config.errorHandler (err) { console.error(全局错误:, err) }1.10 HTML应用入口容器文件作用整个单页面应用(SPA)的HTML基础模板、提供Vue应用挂载的根节点div idapp、引入全局CSS/JS资源如字体、SDK等。是整个Vue文件的基础模板通常含有一个根元素提供上方创建的 vue 应用挂载的根节点div id appVue 应用会挂在到这个元素上。此外还可以引入打包后的JavaScript、CSS文件作为全局的CSS、JS资源(如字体、SDK等)。示例代码如下!DOCTYPE html html langen head meta charsetUTF-8 titleMy Vue App/title !-- 引入全局样式 -- link relstylesheet hrefhttps://cdn.example.com/font.css !-- 引入打包后的JS文件 -- script src./dist/main.js/script /head body !-- Vue应用挂载点 -- div idapp/div !-- 构建工具会自动注入打包后的JS文件 -- script typemodule src/src/main.js/script /body /html二. Vue 项目启动文件加载流程及依赖分析1. index.html 文件被浏览器加载其中包含一个挂载点通常为div id “app”和引入 main.js 的脚本文件2. main.js 文件执行导入 Vue 库、导入 App.vue 作为根组件、导入 router 和 store、创建 Vue 实例并将 router 和 store 注入到 Vue 中再渲染 app.vue。3. app.vue 被渲染在模板中可能含有全局组件(如NavBar 和 router-view)此时router开始根据文件内的URL地址匹配路由4. router 根据路由配置加在对应的 view 页面组件如 Home.vue5. view 组件被渲染在 view 页面组件中极大概率会引用 component 中的子组件在created或mounted钩子中可能通过api调用接口或通过store访问/修改全局状态6. **store** 中的actions可能调用api模块api模块使用utils中的请求工具发送请求7. **components** 组件在views中被使用它们可能使用assets中的资源如图片或utils中的工具函数综合上面的话大致可以总结为下面这张图各个模块可能出现的依赖关系如下表格模块/组件/文件名称依赖模块被依赖模块index.html无main.js 作为入口文件被加载main.jsapp.vue 根组件router 路由系统store 状态管理系统assest 静态资源文件utils 工具包无 (是项目入口不被其他文件依赖)App.vuerouter 使用router-viewconponent 全局布局组件assest 应用级样式main.js 作为根组件被加载router(router/index.js)view/*.vue (路由组成)store 路由守卫使用 Vuexmain.js 初始化app.vue 提供路由视图容器store(store/index.js)api 调用网络请求utils 数据处理工具各种工具类方法main.js 初始化view/*.vue (使用状态)component (使用状态)view(页面组件)conponent (使用子组件)api 请求调用store 使用 Vuexutils 页面工具各种方法assest 静态资源多为图片图标等router 被路由引用component可复用组件assest 组件资源utils 组件工具其它 component (组件之间可以互相嵌套)view 被页面引用App.vue (全局组件)api(请求函数模块)utils工具模块(如请求拦截器、数据处理等)store Vuex action调用view 页面组件调用assest(静态资源模块)无App.vue、view 、componentutils工具包无api、store、view、component依赖循环处理在有些项目中会出现 store 依赖 utils同时 utils 也依赖 store 的情况解决方案1依赖注入在 main.js 文件中初始化工具// main.js import utils from /utils import store from ./store utils.setStore(store) // 注入store依赖2延迟加载在函数内部 require// utils/auth.js export const getToken () { const store require(/store) // 动态引入 return store.state.token }3架构重组将共享功能提取到独立模块