Files
test-repo/docs/zh-cn/appendix/3-browser-and-frontend/frontend-project-architecture.md
T
sanbuphy df51f84ab5 docs: 重构 README 附录展示 & 新增多个附录交互组件
README 更新:
- 移除顶部 header.png 横幅图片
- 新增「附录知识库」板块,以 3×3 网格展示 9 大知识领域精选内容
- 附录链接指向部署版网站 (datawhalechina.github.io)
- 阶段表格新增「附录」行,突出 80+ 交互式专题
- 章节标题「新手入门 & PM」简化为「零基础入门」
- News 新增 2026-02-25 附录知识库更新条目

新增交互组件:
- 异步任务队列 (async-task-queues) 演示组件
- 文件存储 (file-storage) 演示组件
- 项目架构 (project-architecture) 演示组件
- 限流与背压 (rate-limiting) 演示组件
- 搜索引擎 (search-engines) 演示组件
- 计算机基础: AppLaunch/BiosUefi/OSBoot 等启动流程演示组件

新增附录文档:
- 前端项目架构 (frontend-project-architecture.md)
- 后端项目架构 (backend-project-architecture.md)

内容优化:
- 算法思维、数据结构、编程语言、调试艺术等多篇附录内容更新
- HTML/CSS 布局、请求旅程等前后端文档完善
- 附录索引页 (index.md) 同步更新
2026-02-25 12:22:49 +08:00

649 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 前端项目架构设计
::: 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,避免单个文件过大
- 区分全局状态和局部状态,不要什么都放全局
- 使用组合式 APIPinia/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/)