649 lines
18 KiB
Markdown
649 lines
18 KiB
Markdown
|
|
# 前端项目架构设计
|
|||
|
|
|
|||
|
|
::: tip 🎯 核心问题
|
|||
|
|
**文件越放越乱,代码越写越难找,如何设计一个清晰、可维护的前端项目结构?** 这就像问:你是把所有衣服都扔进一个箱子,还是按季节、类型、颜色分类整理?好的项目架构能让团队协作更高效,让代码维护更轻松。
|
|||
|
|
:::
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 1. 为什么要关注项目架构?
|
|||
|
|
|
|||
|
|
### 1.1 从小项目到大项目的演变
|
|||
|
|
|
|||
|
|
很多初学者刚开始写前端时,项目结构非常简单:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
my-project/
|
|||
|
|
├── index.html
|
|||
|
|
├── style.css
|
|||
|
|
└── app.js
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
三个文件搞定一切,简单直接。但随着项目增长,问题开始出现:
|
|||
|
|
|
|||
|
|
- **页面多了**:`page1.html`, `page2.html`... 文件散落在根目录
|
|||
|
|
- **组件多了**:按钮、弹窗、表单各自为政,复用困难
|
|||
|
|
- **工具函数多了**:到处复制粘贴,改一个地方要改十处
|
|||
|
|
- **样式冲突了**:全局 CSS 互相覆盖,调试困难
|
|||
|
|
|
|||
|
|
**问题的本质**:没有"章法",文件随意存放,就像把春夏秋冬的衣服都扔进一个箱子。
|
|||
|
|
|
|||
|
|
### 1.2 好的架构像整理好的衣柜
|
|||
|
|
|
|||
|
|
想象一个整理好的衣柜:
|
|||
|
|
|
|||
|
|
| 区域 | 存放物品 | 特点 |
|
|||
|
|
|------|----------|------|
|
|||
|
|
| **挂衣区** | 外套、衬衫 | 常穿,方便取用 |
|
|||
|
|
| **抽屉区** | 内衣、袜子 | 分类摆放,整齐 |
|
|||
|
|
| **隔板区** | 毛衣、裤子 | 叠放,节省空间 |
|
|||
|
|
| **顶层区** | 换季衣物 | 不常用,收纳起来 |
|
|||
|
|
|
|||
|
|
**好的项目架构**就是把代码也这样组织:每一类文件有自己的"位置",团队成员都知道该去哪找、该往哪放。
|
|||
|
|
|
|||
|
|
::: tip 💡 通俗比喻:餐厅后厨的组织
|
|||
|
|
把前端项目想象成一家餐厅的后厨:
|
|||
|
|
|
|||
|
|
- **`src/pages/`(页面区)** = 出餐口:每个订单对应一个成品菜
|
|||
|
|
- **`src/components/`(组件区)** = 备料台:切好的蔬菜、调好的酱料,随时可用
|
|||
|
|
- **`src/utils/`(工具区)** = 工具柜:刀、勺、温度计等通用工具
|
|||
|
|
- **`src/assets/`(食材区)** = 冷藏库:图片、字体、样式等原材料
|
|||
|
|
- **`src/services/`(服务层)** = 传菜窗口:与外部(服务员/后端)交互
|
|||
|
|
|
|||
|
|
**关键点**:每个区域职责明确,不会混乱。你不会在冷藏库里切菜,也不会把刀具扔进汤锅。
|
|||
|
|
:::
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 2. 经典目录结构解析
|
|||
|
|
|
|||
|
|
### 2.1 标准目录结构(以 Vue/React 为例)
|
|||
|
|
|
|||
|
|
一个中大型前端项目的典型结构如下:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
my-frontend-project/
|
|||
|
|
├── public/ # 静态资源(不经过构建)
|
|||
|
|
│ ├── favicon.ico
|
|||
|
|
│ ├── index.html
|
|||
|
|
│ └── robots.txt
|
|||
|
|
├── src/
|
|||
|
|
│ ├── assets/ # 项目资源(会被构建工具处理)
|
|||
|
|
│ │ ├── images/
|
|||
|
|
│ │ ├── fonts/
|
|||
|
|
│ │ └── styles/
|
|||
|
|
│ │ ├── variables.scss # 变量定义
|
|||
|
|
│ │ ├── mixins.scss # 混入样式
|
|||
|
|
│ │ └── global.css # 全局样式
|
|||
|
|
│ ├── components/ # 通用组件
|
|||
|
|
│ │ ├── common/ # 全局通用组件
|
|||
|
|
│ │ │ ├── Button/
|
|||
|
|
│ │ │ │ ├── index.vue
|
|||
|
|
│ │ │ │ ├── Button.scss
|
|||
|
|
│ │ │ │ └── Button.test.js
|
|||
|
|
│ │ │ ├── Modal/
|
|||
|
|
│ │ │ └── Loading/
|
|||
|
|
│ │ └── business/ # 业务组件
|
|||
|
|
│ │ ├── UserCard/
|
|||
|
|
│ │ └── ProductList/
|
|||
|
|
│ ├── views/ 或 pages/ # 页面组件
|
|||
|
|
│ │ ├── Home/
|
|||
|
|
│ │ ├── About/
|
|||
|
|
│ │ └── User/
|
|||
|
|
│ │ ├── Profile/
|
|||
|
|
│ │ └── Settings/
|
|||
|
|
│ ├── router/ 或 navigation/ # 路由配置
|
|||
|
|
│ │ └── index.js
|
|||
|
|
│ ├── stores/ 或 state/ # 状态管理
|
|||
|
|
│ │ ├── user.js
|
|||
|
|
│ │ └── app.js
|
|||
|
|
│ ├── services/ 或 api/ # API 服务
|
|||
|
|
│ │ ├── user.js
|
|||
|
|
│ │ └── product.js
|
|||
|
|
│ ├── utils/ 或 helpers/ # 工具函数
|
|||
|
|
│ │ ├── request.js # 请求封装
|
|||
|
|
│ │ ├── storage.js # 本地存储
|
|||
|
|
│ │ └── format.js # 格式化工具
|
|||
|
|
│ ├── hooks/ 或 composables/ # 组合式函数
|
|||
|
|
│ │ ├── useAuth.js
|
|||
|
|
│ │ └── useLoading.js
|
|||
|
|
│ ├── directives/ # 自定义指令
|
|||
|
|
│ ├── plugins/ # 插件配置
|
|||
|
|
│ ├── constants/ # 常量定义
|
|||
|
|
│ ├── types/ 或 @types/ # TypeScript 类型
|
|||
|
|
│ └── App.vue 或 App.jsx # 根组件
|
|||
|
|
│ └── main.js 或 main.ts # 入口文件
|
|||
|
|
├── tests/ # 测试文件
|
|||
|
|
│ ├── unit/
|
|||
|
|
│ └── e2e/
|
|||
|
|
├── .env # 环境变量
|
|||
|
|
├── .env.development
|
|||
|
|
├── .env.production
|
|||
|
|
├── vite.config.js # 构建配置
|
|||
|
|
├── package.json
|
|||
|
|
└── README.md
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
::: tip 📊 从图解中你能看到什么?
|
|||
|
|
**分层逻辑**:
|
|||
|
|
|
|||
|
|
- **`public/` vs `src/assets/`**:前者直接复制到输出目录,后者会被构建工具处理(压缩、转译、添加哈希值)
|
|||
|
|
- **`components/` vs `views/`**:组件是"零件",页面是"成品"。一个页面由多个组件组装而成
|
|||
|
|
- **`services/` 独立出来**:把 API 调用集中管理,方便统一处理错误、加载状态、请求拦截
|
|||
|
|
|
|||
|
|
**依赖方向**:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
views/pages → components → utils/hooks
|
|||
|
|
↓
|
|||
|
|
services → stores
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
上层可以调用下层,但下层不应该依赖上层。
|
|||
|
|
:::
|
|||
|
|
|
|||
|
|
### 2.2 按功能组织 vs 按类型组织
|
|||
|
|
|
|||
|
|
项目结构有两种主流的组织方式:
|
|||
|
|
|
|||
|
|
#### 方式一:按类型组织(Type-based)
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
src/
|
|||
|
|
├── components/
|
|||
|
|
│ ├── Button.vue
|
|||
|
|
│ ├── Modal.vue
|
|||
|
|
│ └── Card.vue
|
|||
|
|
├── views/
|
|||
|
|
│ ├── Home.vue
|
|||
|
|
│ ├── User.vue
|
|||
|
|
│ └── Product.vue
|
|||
|
|
├── stores/
|
|||
|
|
│ ├── user.js
|
|||
|
|
│ └── product.js
|
|||
|
|
└── services/
|
|||
|
|
├── user.js
|
|||
|
|
└── product.js
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**优点**:
|
|||
|
|
- 结构清晰,同类文件在一起
|
|||
|
|
- 适合小型项目,一目了然
|
|||
|
|
|
|||
|
|
**缺点**:
|
|||
|
|
- 修改一个功能要跨多个目录
|
|||
|
|
- 大型项目中文件过多,难以定位
|
|||
|
|
|
|||
|
|
#### 方式二:按功能组织(Feature-based)
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
src/
|
|||
|
|
├── features/
|
|||
|
|
│ ├── auth/
|
|||
|
|
│ │ ├── components/
|
|||
|
|
│ │ │ ├── LoginForm.vue
|
|||
|
|
│ │ │ └── RegisterForm.vue
|
|||
|
|
│ │ ├── stores/
|
|||
|
|
│ │ │ └── authStore.js
|
|||
|
|
│ │ ├── services/
|
|||
|
|
│ │ │ └── authApi.js
|
|||
|
|
│ │ ├── hooks/
|
|||
|
|
│ │ │ └── useAuth.js
|
|||
|
|
│ │ └── index.js # 统一导出
|
|||
|
|
│ ├── user/
|
|||
|
|
│ │ ├── components/
|
|||
|
|
│ │ ├── stores/
|
|||
|
|
│ │ └── services/
|
|||
|
|
│ └── product/
|
|||
|
|
│ ├── components/
|
|||
|
|
│ ├── stores/
|
|||
|
|
│ └── services/
|
|||
|
|
├── shared/ # 共享资源
|
|||
|
|
│ ├── components/
|
|||
|
|
│ ├── utils/
|
|||
|
|
│ └── styles/
|
|||
|
|
└── App.vue
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**优点**:
|
|||
|
|
- 高内聚,修改一个功能在一个目录完成
|
|||
|
|
- 便于团队协作,不同人负责不同 feature
|
|||
|
|
- 易于删除或重构,不会散落各处
|
|||
|
|
|
|||
|
|
**缺点**:
|
|||
|
|
- 初期设计需要考虑 feature 划分
|
|||
|
|
- 共享组件需要额外考虑
|
|||
|
|
|
|||
|
|
::: tip 💡 如何选择?
|
|||
|
|
| 项目规模 | 推荐方式 | 原因 |
|
|||
|
|
|----------|----------|------|
|
|||
|
|
| 小型项目(< 10 个页面) | 按类型组织 | 简单直接,快速上手 |
|
|||
|
|
| 中大型项目(> 20 个页面) | 按功能组织 | 便于维护,团队协作 |
|
|||
|
|
| 微前端/大型应用 | 按功能 + 模块拆分 | 独立部署,团队自治 |
|
|||
|
|
|
|||
|
|
**实际建议**:很多项目采用"混合模式"——整体按功能组织,内部按类型细分。
|
|||
|
|
:::
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 3. 各目录的职责与最佳实践
|
|||
|
|
|
|||
|
|
### 3.1 `components/` 组件目录
|
|||
|
|
|
|||
|
|
组件是前端项目的核心,良好的组件设计能大幅提升开发效率。
|
|||
|
|
|
|||
|
|
#### 组件分类
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
components/
|
|||
|
|
├── common/ # 通用组件(跨项目可复用)
|
|||
|
|
│ ├── Button/
|
|||
|
|
│ ├── Input/
|
|||
|
|
│ ├── Modal/
|
|||
|
|
│ └── Loading/
|
|||
|
|
├── business/ # 业务组件(项目特定)
|
|||
|
|
│ ├── UserCard/
|
|||
|
|
│ ├── ProductItem/
|
|||
|
|
│ └── OrderTable/
|
|||
|
|
└── layout/ # 布局组件
|
|||
|
|
├── Header/
|
|||
|
|
├── Sidebar/
|
|||
|
|
└── Footer/
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 单文件组件结构
|
|||
|
|
|
|||
|
|
每个组件建议包含以下文件:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Button/
|
|||
|
|
├── index.vue # 主组件(或 .tsx/.jsx)
|
|||
|
|
├── Button.scss # 样式(可选 CSS Modules)
|
|||
|
|
├── Button.test.js # 单元测试
|
|||
|
|
├── Button.stories.js # Storybook 文档(可选)
|
|||
|
|
├── types.ts # 类型定义(TS 项目)
|
|||
|
|
└── index.ts # 统一导出
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
::: details 📝 组件代码示例
|
|||
|
|
```vue
|
|||
|
|
<!-- Button/index.vue -->
|
|||
|
|
<template>
|
|||
|
|
<button
|
|||
|
|
:class="['btn', `btn--${type}`, { 'btn--disabled': disabled }]"
|
|||
|
|
:disabled="disabled"
|
|||
|
|
@click="handleClick"
|
|||
|
|
>
|
|||
|
|
<Loading v-if="loading" size="small" />
|
|||
|
|
<slot />
|
|||
|
|
</button>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script setup>
|
|||
|
|
import { Loading } from '../Loading'
|
|||
|
|
|
|||
|
|
defineProps({
|
|||
|
|
type: { type: String, default: 'primary' },
|
|||
|
|
disabled: Boolean,
|
|||
|
|
loading: Boolean
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
const emit = defineEmits(['click'])
|
|||
|
|
const handleClick = () => emit('click')
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<style scoped lang="scss">
|
|||
|
|
.btn {
|
|||
|
|
padding: 8px 16px;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
cursor: pointer;
|
|||
|
|
|
|||
|
|
&--primary {
|
|||
|
|
background: var(--primary-color);
|
|||
|
|
color: white;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
&--disabled {
|
|||
|
|
opacity: 0.6;
|
|||
|
|
cursor: not-allowed;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
</style>
|
|||
|
|
```
|
|||
|
|
:::
|
|||
|
|
|
|||
|
|
### 3.2 `views/` 或 `pages/` 页面目录
|
|||
|
|
|
|||
|
|
页面是用户看到的"成品",通常对应路由。
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
views/
|
|||
|
|
├── Home/ # 首页
|
|||
|
|
│ ├── index.vue
|
|||
|
|
│ ├── components/ # 页面私有组件
|
|||
|
|
│ │ ├── HeroSection.vue
|
|||
|
|
│ │ └── FeatureList.vue
|
|||
|
|
│ └── hooks/ # 页面私有 hooks
|
|||
|
|
│ └── useHomeData.js
|
|||
|
|
├── User/
|
|||
|
|
│ ├── Profile/
|
|||
|
|
│ ├── Settings/
|
|||
|
|
│ └── OrderHistory/
|
|||
|
|
└── Product/
|
|||
|
|
├── List/
|
|||
|
|
└── Detail/
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**最佳实践**:
|
|||
|
|
- 页面组件保持"薄",逻辑下沉到 hooks 或 services
|
|||
|
|
- 页面私有组件放在页面目录下,避免污染全局
|
|||
|
|
- 复杂页面可以进一步拆分子目录
|
|||
|
|
|
|||
|
|
### 3.3 `services/` 或 `api/` 服务层
|
|||
|
|
|
|||
|
|
集中管理所有 API 调用,统一处理请求/响应拦截。
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
services/
|
|||
|
|
├── request.js # 请求实例配置(axios/fetch 封装)
|
|||
|
|
├── user.js # 用户相关 API
|
|||
|
|
├── product.js # 商品相关 API
|
|||
|
|
├── order.js # 订单相关 API
|
|||
|
|
└── index.js # 统一导出
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
::: details 📝 服务层代码示例
|
|||
|
|
```javascript
|
|||
|
|
// services/request.js
|
|||
|
|
import axios from 'axios'
|
|||
|
|
import { useAuthStore } from '@/stores/auth'
|
|||
|
|
|
|||
|
|
const request = axios.create({
|
|||
|
|
baseURL: import.meta.env.VITE_API_BASE_URL,
|
|||
|
|
timeout: 10000
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 请求拦截器
|
|||
|
|
request.interceptors.request.use(
|
|||
|
|
(config) => {
|
|||
|
|
const authStore = useAuthStore()
|
|||
|
|
if (authStore.token) {
|
|||
|
|
config.headers.Authorization = `Bearer ${authStore.token}`
|
|||
|
|
}
|
|||
|
|
return config
|
|||
|
|
}
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// 响应拦截器
|
|||
|
|
request.interceptors.response.use(
|
|||
|
|
(response) => response.data,
|
|||
|
|
(error) => {
|
|||
|
|
if (error.response?.status === 401) {
|
|||
|
|
// 统一处理登录过期
|
|||
|
|
window.location.href = '/login'
|
|||
|
|
}
|
|||
|
|
return Promise.reject(error)
|
|||
|
|
}
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
export default request
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
// services/user.js
|
|||
|
|
import request from './request'
|
|||
|
|
|
|||
|
|
export const userApi = {
|
|||
|
|
login: (data) => request.post('/auth/login', data),
|
|||
|
|
register: (data) => request.post('/auth/register', data),
|
|||
|
|
getProfile: () => request.get('/user/profile'),
|
|||
|
|
updateProfile: (data) => request.put('/user/profile', data)
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
:::
|
|||
|
|
|
|||
|
|
### 3.4 `stores/` 状态管理
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
stores/
|
|||
|
|
├── index.js # store 入口
|
|||
|
|
├── auth.js # 认证状态
|
|||
|
|
├── user.js # 用户信息
|
|||
|
|
├── app.js # 应用级状态(主题、语言等)
|
|||
|
|
└── cart.js # 购物车状态
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**建议**:
|
|||
|
|
- 按功能拆分 store,避免单个文件过大
|
|||
|
|
- 区分全局状态和局部状态,不要什么都放全局
|
|||
|
|
- 使用组合式 API(Pinia/Vuex 4+)更灵活
|
|||
|
|
|
|||
|
|
### 3.5 `utils/` 工具函数
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
utils/
|
|||
|
|
├── format.js # 格式化(日期、金额等)
|
|||
|
|
├── storage.js # 本地存储封装
|
|||
|
|
├── validate.js # 表单验证
|
|||
|
|
├── dom.js # DOM 操作
|
|||
|
|
├── date.js # 日期处理
|
|||
|
|
└── index.js # 统一导出
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**原则**:
|
|||
|
|
- 纯函数优先,便于测试
|
|||
|
|
- 单一职责,一个函数只做一件事
|
|||
|
|
- 添加 JSDoc 注释,说明参数和返回值
|
|||
|
|
|
|||
|
|
::: details 📝 工具函数示例
|
|||
|
|
```javascript
|
|||
|
|
// utils/storage.js
|
|||
|
|
const STORAGE_PREFIX = 'myapp_'
|
|||
|
|
|
|||
|
|
export const storage = {
|
|||
|
|
get(key) {
|
|||
|
|
const value = localStorage.getItem(STORAGE_PREFIX + key)
|
|||
|
|
try {
|
|||
|
|
return JSON.parse(value)
|
|||
|
|
} catch {
|
|||
|
|
return value
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
set(key, value) {
|
|||
|
|
localStorage.setItem(
|
|||
|
|
STORAGE_PREFIX + key,
|
|||
|
|
typeof value === 'string' ? value : JSON.stringify(value)
|
|||
|
|
)
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
remove(key) {
|
|||
|
|
localStorage.removeItem(STORAGE_PREFIX + key)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
:::
|
|||
|
|
|
|||
|
|
### 3.6 `hooks/` 或 `composables/` 组合式函数
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
hooks/
|
|||
|
|
├── useAuth.js # 认证逻辑
|
|||
|
|
├── useLoading.js # 加载状态
|
|||
|
|
├── usePagination.js # 分页逻辑
|
|||
|
|
├── useForm.js # 表单处理
|
|||
|
|
└── useWebsocket.js # WebSocket
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
::: details 📝 Hook 示例
|
|||
|
|
```javascript
|
|||
|
|
// hooks/useLoading.js
|
|||
|
|
import { ref } from 'vue'
|
|||
|
|
|
|||
|
|
export function useLoading() {
|
|||
|
|
const loading = ref(false)
|
|||
|
|
|
|||
|
|
const withLoading = async (fn) => {
|
|||
|
|
loading.value = true
|
|||
|
|
try {
|
|||
|
|
return await fn()
|
|||
|
|
} finally {
|
|||
|
|
loading.value = false
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return { loading, withLoading }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 使用
|
|||
|
|
const { loading, withLoading } = useLoading()
|
|||
|
|
const fetchData = () => withLoading(async () => {
|
|||
|
|
const data = await api.getData()
|
|||
|
|
list.value = data
|
|||
|
|
})
|
|||
|
|
```
|
|||
|
|
:::
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 4. 知名开源项目的架构参考
|
|||
|
|
|
|||
|
|
### 4.1 Vue 3 官方仓库
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
vue/
|
|||
|
|
├── packages/
|
|||
|
|
│ ├── vue/ # 核心包
|
|||
|
|
│ ├── reactivity/ # 响应式系统
|
|||
|
|
│ ├── runtime-core/ # 运行时核心
|
|||
|
|
│ ├── runtime-dom/ # DOM 运行时
|
|||
|
|
│ ├── compiler-sfc/ # 单文件组件编译器
|
|||
|
|
│ └── shared/ # 共享工具
|
|||
|
|
├── scripts/ # 构建脚本
|
|||
|
|
└── tsconfig.json
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**特点**:
|
|||
|
|
- Monorepo 结构,多个包统一管理
|
|||
|
|
- 按功能拆分 package,职责清晰
|
|||
|
|
- 共享工具提取到 shared 包
|
|||
|
|
|
|||
|
|
### 4.2 React 官方仓库
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
react/
|
|||
|
|
├── packages/
|
|||
|
|
│ ├── react/ # React 核心
|
|||
|
|
│ ├── react-dom/ # DOM 渲染器
|
|||
|
|
│ ├── react-reconciler/ # 协调器
|
|||
|
|
│ ├── scheduler/ # 调度器
|
|||
|
|
│ └── shared/ # 共享代码
|
|||
|
|
├── fixtures/ # 测试用例
|
|||
|
|
└── scripts/
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**特点**:
|
|||
|
|
- 核心与渲染器分离(react vs react-dom)
|
|||
|
|
- reconciler 独立,支持多平台
|
|||
|
|
- scheduler 单独抽离,可独立使用
|
|||
|
|
|
|||
|
|
### 4.3 Ant Design Vue
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
ant-design-vue/
|
|||
|
|
├── components/ # 组件目录
|
|||
|
|
│ ├── button/
|
|||
|
|
│ ├── modal/
|
|||
|
|
│ └── ...
|
|||
|
|
├── docs/ # 文档
|
|||
|
|
├── site/ # 官网
|
|||
|
|
├── tests/ # 测试
|
|||
|
|
└── typings/ # 类型定义
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**特点**:
|
|||
|
|
- 组件与文档分离
|
|||
|
|
- 每个组件独立目录,包含 demo、test、style
|
|||
|
|
- 统一的类型定义
|
|||
|
|
|
|||
|
|
### 4.4 Next.js(全栈框架)
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
my-nextjs-app/
|
|||
|
|
├── app/ # App Router(新版)
|
|||
|
|
│ ├── page.js # 页面
|
|||
|
|
│ ├── layout.js # 布局
|
|||
|
|
│ ├── loading.js # 加载状态
|
|||
|
|
│ └── api/ # API 路由
|
|||
|
|
├── components/ # 组件
|
|||
|
|
├── lib/ # 工具函数
|
|||
|
|
├── public/ # 静态资源
|
|||
|
|
└── styles/ # 全局样式
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**特点**:
|
|||
|
|
- 约定式路由,文件即路由
|
|||
|
|
- 内置 loading、error、layout 等约定文件
|
|||
|
|
- API 路由与页面共存
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 5. 架构设计原则与检查清单
|
|||
|
|
|
|||
|
|
### 5.1 核心原则
|
|||
|
|
|
|||
|
|
| 原则 | 说明 | 实践建议 |
|
|||
|
|
|------|------|----------|
|
|||
|
|
| **单一职责** | 一个模块只做一件事 | 组件、函数保持简洁 |
|
|||
|
|
| **高内聚低耦合** | 相关代码放在一起,减少依赖 | 按功能组织目录 |
|
|||
|
|
| **可预测性** | 代码行为符合直觉 | 命名清晰,结构一致 |
|
|||
|
|
| **可测试性** | 便于编写单元测试 | 纯函数、依赖注入 |
|
|||
|
|
| **可扩展性** | 新功能容易添加 | 预留扩展点,避免硬编码 |
|
|||
|
|
|
|||
|
|
### 5.2 检查清单
|
|||
|
|
|
|||
|
|
**目录结构**:
|
|||
|
|
- [ ] 是否有清晰的目录划分?
|
|||
|
|
- [ ] 新成员能否快速找到文件位置?
|
|||
|
|
- [ ] 是否避免了过深的嵌套(建议不超过 4 层)?
|
|||
|
|
|
|||
|
|
**组件设计**:
|
|||
|
|
- [ ] 组件是否单一职责?
|
|||
|
|
- [ ] Props 是否清晰、可预测?
|
|||
|
|
- [ ] 是否提取了可复用的逻辑到 hooks?
|
|||
|
|
|
|||
|
|
**代码组织**:
|
|||
|
|
- [ ] 是否避免了循环依赖?
|
|||
|
|
- [ ] 工具函数是否纯函数优先?
|
|||
|
|
- [ ] 常量、配置是否集中管理?
|
|||
|
|
|
|||
|
|
**团队协作**:
|
|||
|
|
- [ ] 是否有编码规范文档?
|
|||
|
|
- [ ] 是否有文件命名约定?
|
|||
|
|
- [ ] 代码审查是否关注架构问题?
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 6. 总结
|
|||
|
|
|
|||
|
|
::: tip 💡 核心思想
|
|||
|
|
好的前端项目架构不是一成不变的,而是随着项目发展不断演进的。关键是建立清晰的**组织原则**和**命名约定**,让团队成员达成共识。
|
|||
|
|
|
|||
|
|
**记住这几点**:
|
|||
|
|
1. **先简单后复杂**:小项目不要过度设计
|
|||
|
|
2. **按功能组织**:中大型项目推荐 Feature-based
|
|||
|
|
3. **统一约定**:命名、结构、代码风格保持一致
|
|||
|
|
4. **持续重构**:定期审视架构,及时调整
|
|||
|
|
|
|||
|
|
**最终目标**:让代码像整理好的衣柜一样,想找什么立刻能找到,新成员也能快速上手。
|
|||
|
|
:::
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 参考资源
|
|||
|
|
|
|||
|
|
- [Vue 风格指南](https://vuejs.org/style-guide/)
|
|||
|
|
- [React 项目结构建议](https://react.dev/learn/thinking-in-react)
|
|||
|
|
- [Bulletproof React - 架构指南](https://github.com/alan2207/bulletproof-react)
|
|||
|
|
- [Feature Sliced Design](https://feature-sliced.design/)
|