介绍
在启动 Vue 项目时,思考项目结构至关重要。主要考虑因素是预期项目的规模。在本篇博文中,我将探讨适用于不同规模 Vue 项目的各种结构。这个考虑与康威定律相吻合:
"设计系统的组织受限于产生这些组织沟通结构的设计。" - 梅尔·康威
基本上,康威定律暗示了您的 Vue 应用程序的架构将固有地反映出您的组织架构,从而影响您应该如何规划项目的结构。
一些常规规则
在我们开始介绍不同的项目结构之前,我想强调一些通用的规则,这些规则适用于每种结构,大部分来自于官方的 Vue 风格指南。
基础组件命名
为您的 UI 组件使用前缀。
不好的
components/
|- MyButton.vue
|- VueTable.vue
|- Icon.vue
好的
components/
|- BaseButton.vue
|- BaseTable.vue
|- BaseIcon.vue
紧密耦合的组件名称
将紧密耦合的组件名称放在一起。
不好的
components/
|- TodoList.vue
|- TodoItem.vue
|- TodoButton.vue
好的
components/
|- TodoList.vue
|- TodoListItem.vue
|- TodoListItemButton.vue
组件名称中单词的顺序
组件名称应该以最高级别(通常是最通用的)的单词开头,并以描述性的修改词结尾。
不好的
components/
|- ClearSearchButton.vue
|- ExcludeFromSearchInput.vue
|- LaunchOnStartupCheckbox.vue
|- RunSearchButton.vue
|- SearchInput.vue
|- TermsCheckbox.vue
好的
components/
|- SearchButtonClear.vue
|- SearchButtonRun.vue
|- SearchInputQuery.vue
|- SearchInputExcludeGlob.vue
|- SettingsCheckboxTerms.vue
|- SettingsCheckboxLaunchOnStartup.vue
测试
决定如何组织您的测试以及将它们放置在何处可能是另一个博文的主题。在本文中,我们将探讨将测试放置在单独的文件夹中,其中每个测试文件反映源代码。或者,您可以将测试文件放置在它们所测试的文件旁边。这两种方法都是有效的。
方法 1:单独的测试文件夹
/vue-project
|-- /src
| |-- /components
| | |-- MyComponent.vue
| |-- /views
| | |-- HomeView.vue
|-- /tests
| |-- /components
| | |-- MyComponent.spec.js
| |-- /views
| | |-- HomeView.spec.js
|-- package.json
|-- ...
方法 2:内联测试文件
/vue-project
|-- /src
| |-- /components
| | |-- MyComponent.vue
| | |-- MyComponent.spec.js
| |-- /views
| | |-- HomeView.vue
| | |-- HomeView.spec.js
|-- package.json
|-- ...
扁平式方法
在启动小规模 Vue 项目(如概念验证)时,您可能更喜欢简单直接的文件夹结构以避免复杂性:
/src
|-- /components
| |-- BaseButton.vue
| |-- BaseCard.vue
| |-- PokemonList.vue
| |-- PokemonCard.vue
|-- /composables
| |-- usePokemon.js
|-- /utils
| |-- validators.js
|-- /layout
| |-- DefaultLayout.vue
| |-- AdminLayout.vue
|-- /plugins
| |-- translate.js
|-- /views
| |-- Home.vue
| |-- PokemonDetail.vue
|-- /router
| |-- index.js
|-- /store
| |-- index.js
|-- /assets
| |-- /images
| |-- /styles
|-- /tests
| |-- ...
|-- App.vue
|-- main.js
原子设计
对于较大的 Vue 应用程序,采用原子设计方法可能是有利的。这种方法将组件组织成从简单到复杂的层次结构:
• 原子(Atoms):基本元素(例如按钮、图标)
• 分子(Molecules):由原子组成的组合体(例如搜索栏)
• 有机体(Organisms):复杂组件(例如导航栏)
• 模板(Templates):显示组件结构的布局
• 页面(Pages):具有真实数据的实际 UI 屏幕
这种方法确保了可扩展性和可维护性,并且能够在简单和复杂组件之间平滑过渡。
/src
|-- /components
| |-- /atoms
| | |-- AtomButton.vue
| | |-- AtomIcon
.vue
| |-- /molecules
| | |-- MoleculeSearchInput.vue
| | |-- MoleculePokemonThumbnail.vue
| |-- /organisms
| | |-- OrganismPokemonCard.vue
| | |-- OrganismHeader.vue
| |-- /templates
| | |-- TemplatePokemonList.vue
| | |-- TemplatePokemonDetail.vue
|-- /pages
| |-- PageHome.vue
| |-- PagePokemonDetail.vue
|-- /composables
| |-- usePokemon.js
|-- /utils
| |-- validators.js
|-- /layout
| |-- LayoutDefault.vue
| |-- LayoutAdmin.vue
|-- /plugins
| |-- translate.js
|-- /router
| |-- index.js
|-- /store
| |-- index.js
|-- /assets
| |-- /images
| |-- /styles
|-- /tests
| |-- ...
|-- App.vue
|-- main.js
模块
随着项目规模的扩大,考虑采用模块化的单块架构。这种结构封装了每个功能或领域,增强了可维护性,并为可能的演变向微服务方向做好了准备:
/src
|-- /core
| |-- /components
| | |-- BaseButton.vue
| | |-- BaseIcon.vue
| |-- /models
| |-- /store
| |-- /services
| |-- /views
| | |-- DefaultLayout.vue
| | |-- AdminLayout.vue
| |-- /utils
| | |-- validators.js
|-- /modules
| |-- /pokemon
| | |-- /components
| | | |-- PokemonThumbnail.vue
| | | |-- PokemonCard.vue
| | | |-- PokemonListTemplate.vue
| | | |-- PokemonDetailTemplate.vue
| | |-- /models
| | |-- /store
| | | |-- pokemonStore.js
| | |-- /services
| | |-- /views
| | | |-- PokemonDetailPage.vue
| | |-- /tests
| | | |-- pokemonTests.spec.js
| |-- /search
| | |-- /components
| | | |-- SearchInput.vue
| | |-- /models
| | |-- /store
| | | |-- searchStore.js
| | |-- /services
| | |-- /views
| | |-- /tests
| | | |-- searchTests.spec.js
|-- /assets
| |-- /images
| |-- /styles
|-- /scss
|-- App.vue
|-- main.ts
|-- router.ts
|-- store.ts
|-- /tests
| |-- ...
|-- /plugins
| |-- translate.js
功能分割设计
功能分割设计是一种组织大型和长期项目以便更易于管理和扩展的方法。此方法将应用程序分成不同的层,每个层具有特定的角色:
• 应用程序(App):全局设置、样式和提供者。
• 页面(Pages):使用实体、功能和小部件构建完整页面。
• 小部件(Widgets):将实体和功能组合成一致的 UI 块,如 IssueList 或 UserProfile。
• 功能(Features):处理添加价值的用户交互,例如发送评论、添加到购物车或搜索用户。
• 实体(Entities):表示核心业务模型,如用户、产品和订单。
• 共享(Shared):提供与特定业务逻辑无关的可重用实用程序和组件,如 UIKit、库和 API。
/src
|-- /app
| |-- App.vue
| |-- main.js
| |-- app.scss
|-- /processes
|-- /pages
| |-- Home.vue
| |-- PokemonDetailPage.vue
|-- /widgets
| |-- UserProfile.vue
| |-- PokemonStatsWidget.vue
|-- /features
| |-- pokemon
| | |-- CatchPokemon.vue
| | |-- PokemonList.vue
| |-- user
| | |-- Login.vue
| | |-- Register.vue
|-- /entities
| |-- user
| | |-- userService.js
| | |-- userModel.js
| |-- pokemon
| | |-- pokemonService.js
| | |-- pokemonModel.js
|-- /shared
| |-- ui
| | |-- BaseButton.vue
| | |-- BaseInput.vue
| | |-- Loader.vue
| |-- lib
| | |-- api.js
| | |-- helpers.js
|-- /assets
| |-- /images
| |-- /styles
|-- /router
| |-- index.js
|-- /store
| |-- index.js
|-- /tests
| |-- featureTests.spec.js
这种设置非常适合大型项目,因为它使得项目更容易扩展和保持整洁。要了解有关这些层如何工作的更多详细信息,请查看官方的功能分割设计文档。
图片
微前端
微前端将微服务的思想应用于 Web 应用程序的前端部分。这意味着不同的团队可以独立处理 Web 应用程序的不同部分,而不会相互干扰。每个部分,或“微前端”,都可以独立运行,并可以单独更新。这是一个 SPA 的基本概述。请注意,本文不会深入介绍微前端的工作原理。
• 应用程序 Shell:这是控制主要布局和站点路由的主要控制器。它将所有微前端连接在一起。
• 分解的 UI:每个微前端都专注于应用程序的特定部分。它们可以使用不同的技术进行开发,并可以分别更新。
图片
主要优点是微前端让团队可以在不等待其他团队的情况下更新应用程序的各个部分,这可以加快开发速度。然而,这种设置可能会使应用程序更复杂,难以管理和保持一致。
有用的资源:
• 微前端 - 将微服务思想扩展到前端开发
• 马丁·福勒关于微前端
这种策略非常适合具有多个开发团队的大型、复杂项目。每个团队都可以专注于特定的业务需求,而不会影响其他团队的工作,可能使用最适合其部分的技术。
结论
图片
希望现在清楚了,您应该选择一个反映您组织规模和复杂性的结构。此外,更先进的结构将值得一篇独立的博文;我只是想为您提供一个良好的概述。一般来说,您的团队越大、越复杂,或者拥有更多的团队,您就越应该朝着更好地分隔这些概念的结构努力。基本上,您团队的结构将指导您确定最适合您需求的项目结构。
方法 |
描述 |
优点 |
缺点 |
扁平式方法 |
简单的结构,适合小项目或概念验证。 |
- 易于实施 - 最小设置 |
- 不可扩展 - 随着项目增长而混乱 |
原子设计 |
基于组件复杂性的分层结构。 |
- 可扩展 - 有组织 - 可重用组件 |
- 管理层面的开销 - 复杂的设置 |
模块 |
封装功能的模块化结构。 |
- 可扩展 - 封装特性 |
- 可能存在重复 - 可能变得复杂 |
功能分割设计 |
将项目组织成功能层和切片。 |
- 高内聚 - 明确的功能分离 |
- 初始复杂性 - 需要彻底规划 |
微前端 |
应用程序的每个部分都可以单独部署。 |
- 独立部署 - 可扩展 |
- 复杂性高 - 需要团队之间的协调 |