大型应用程序最关键和最具挑战性的方面之一是良好且合理的文件结构。在考虑使用微前端将代码库分解为多个应用程序之前,可以遵循一些步骤来改进项目级别的架构,并使转换更容易(如果您考虑过这条路径)。
本文将介绍如何通过设置功能之间的界限并最大限度地减少代码耦合和副作用,使代码库更易于理解。
Vue 默认项目结构
当我们使用 Vue Cli 生成项目的时候,组件结构是扁平的,不遵循任何层次结构。
- assets 目录, 用于存放项目中的静态资源,如 图片,音频, 字体, css 样式 等。
- components 目录, 用于存放一些可复用的组件,整体比较扁平化的。
- router 目录, 用于存放路由配置文件
- views 目录, 用于存放页面 Vue 文件
- main.ts 文件, 文件作为应用程序的入口点,支持 Vue 初始化和插件或附加库的配置。
- App.vue 文件, 代表应用程序的根组件,充当其他组件的容器并充当主模板。
对于一个小型项目来说,这样的文件结构组织看起来没太大问题。 但是,对于一个大型项目来说,这种架构很快就会失控。需要某种模块化才能轻松定位给定文件,设置功能之间的边界,并避免组件的紧密耦合。
按功能模块拆分应用
任何大型应用程序都分为多个独立的功能。识别它们并不总是那么容易和直接,但经过一段时间的经验积累后会变得更好。让我们尝试将一个流行的应用程序分成几个部分作为练习。
微博的个人主页上有很多的功能内容。
时间线是页面的核心,周围有许多功能,如导航、微博创建部分、热搜、感兴趣的人等。
将构成这些功能的所有组件放在同一个文件夹中是不可维护的,并且即使使用 IDE 的快速查找选项,找到其中一个组件也将非常困难。
更好的项目组织结构
根据我数十年的工作经验来看, 更好更全面的文件组织结构应该像下边这张图一样。
- assets 目录
- components 目录 - 整个应用程序中使用的所有共享组件。
- config 目录 - 一些通用的环境配置文件
- features 目录 - 包含所有应用程序功能。我们希望将大部分应用程序代码保留在此处。稍后会详细介绍这一点。
- layout 目录 - 页面的不同布局。
- lib 目录 - 我们的应用程序中使用的不同第三方库的配置。
- pages 目录 - 应用程序的不同页面
- router 目录 - 路由配置
- services 目录 - 跨功能共享的一些服务封装。
- stores 目录 - 一些共享的功能数据流管理
- test 目录 - 单元测试
- utils 目录 - 自己写的一些工具方法
- types 目录 - TS 的类型声明
几点注意事项
- 默认情况下,Pages 文件夹将用于存放实际 Router 配置中的具体展示页面。将所有页面放在一处非常有帮助,但其中的逻辑应保持在最低耦合度。
- 为了更好维护性和可扩展性,我们的目标是将大部分应用程序代码保留在 features 文件夹中。每个功能文件夹都应包含给定功能的独立解耦的业务实现代码。
- 在理想情况下,我们其实不需要跨feture 去共享组件, store, services 等这些公用逻辑。 但,实际业务开发中 需求会千奇百怪各种变化, 所以不可避免我们需要一些全局共享的store 、组件、 service 等。但,当我们需要放入公共复用目录的时候,需要好好思考一下,这么做是否有必要。
功能模块组织
正如我们之前提到的,我们的大部分业务代码应该位于多个子目录的 features 文件夹内。
所以 features 目录下,需要合理的规划组织,以便于提升我们的可维护性。
- API - 当前功能模块,所有的网络请求数据前置处理,都在这块实现。
- components - 组件层。 当前feature 拆分出来的业务组件。
- store - 数据管理。 当前feature 使用的store
- types - 类型声明。
- test - 单元测试。
- index.ts - 这是该功能的入口文件。它的行为就像该功能的公共 API,并且它应该只导出应该对应用程序的其他部分公开的内容。
上边提到的Index.ts ,就像是该功能模块的索引文件一样。 作为该功能模块被外部模块使用的唯一引用入口。
代码示例:
# Bad
import { UserProfile } from '@/features/profile/components/UserProfile.vue'
# Good ✅ ✅ ✅
import { UserProfile } from '@/features/profile'
我们可以通过配置ESlint 的规则,来强制在编码过程中使用我们的推荐写法。 ESlint 配置如下:
rules: {
...
'no-restricted-imports': [
'error',
{
patterns: ['@/features/*/*'],
},
],
'import/no-cycle': 'error',
...
}
总结
面向功能的架构是构建复杂项目的有效且经过考验的方式。
它允许我们将代码解耦到单独的模块中,并随着项目代码变得更加复杂而扩展我们的项目。
你们的项目结构是不是也是类似的呢? 你有使用不同的模式吗? 欢迎讨论