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) 同步更新
This commit is contained in:
@@ -0,0 +1,648 @@
|
||||
# 前端项目架构设计
|
||||
|
||||
::: 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/)
|
||||
@@ -434,69 +434,365 @@ Flexbox 是现代 CSS 最常用的布局方式。它让元素自动排列对齐
|
||||
| `flex-wrap` | 是否换行 | `nowrap`、`wrap` |
|
||||
| `gap` | 元素间距 | `10px`、`1rem` |
|
||||
|
||||
### 3.7 SCSS:CSS 的"升级版"
|
||||
### 3.7 CSS 预处理器:SCSS/SASS 与 LESS
|
||||
|
||||
::: tip 🎯 真实场景
|
||||
|
||||
你写了一个项目,CSS 文件有 2000 行。后来要改主题色,你发现:
|
||||
|
||||
- 主色调 `#3b82f6` 出现了 50 次
|
||||
- 改一个颜色要全局搜索替换
|
||||
- 还要担心漏改了某个地方
|
||||
- 改一个颜色要全局搜索替换,还要担心漏改
|
||||
- 选择器写成 `.nav .nav-list .nav-item .nav-link` 又长又难维护
|
||||
|
||||
**SCSS 解决的问题**:变量、嵌套、混入、模块化
|
||||
**CSS 预处理器**就是来解决这些问题的。它让 CSS 也能"编程":有变量、有嵌套、能复用代码。
|
||||
:::
|
||||
|
||||
**SCSS 示例**:
|
||||
#### 3.7.1 什么是 CSS 预处理器?
|
||||
|
||||
```scss
|
||||
// 1. 变量:定义主题色
|
||||
$primary-color: #3b82f6;
|
||||
**用人话解释**:预处理器是一种"更聪明的 CSS"。你用更强大的语法写样式,然后它帮你**编译**成普通 CSS,浏览器就能正常识别了。
|
||||
|
||||
// 2. 嵌套:父子关系一目了然
|
||||
.card {
|
||||
background: white;
|
||||
|
||||
h2 {
|
||||
color: $primary-color;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
}
|
||||
```
|
||||
**为什么要用?**
|
||||
|
||||
**编译后变成普通 CSS**:
|
||||
| 痛点 | 原生 CSS | 预处理器 |
|
||||
|------|----------|----------|
|
||||
| 颜色重复出现 | 到处复制粘贴 | 定义变量,一处修改全局生效 |
|
||||
| 选择器层级太深 | 写成一长串 | 嵌套语法,层级一目了然 |
|
||||
| 相同样式重复写 | 复制粘贴 | 混入(Mixin),像函数一样复用 |
|
||||
|
||||
#### 3.7.2 三大预处理器对比
|
||||
|
||||
| 特性 | 原生 CSS | **SCSS/SASS** | **LESS** |
|
||||
|------|----------|---------------|----------|
|
||||
| **变量写法** | `--primary` | `$primary` | `@primary` |
|
||||
| **嵌套语法** | ❌ 不支持 | ✅ 支持 | ✅ 支持 |
|
||||
| **混入(复用代码)** | ❌ 不支持 | ✅ `@mixin` | ✅ `.mixin()` |
|
||||
| **学习难度** | 简单 | 中等 | 中等 |
|
||||
| **流行程度** | - | ⭐⭐⭐ 最流行 | ⭐⭐ 较流行 |
|
||||
|
||||
**简单记忆**:
|
||||
- **SCSS**:用 `$` 符号,Bootstrap 5 在用,生态最好
|
||||
- **LESS**:用 `@` 符号,和 CSS 的 `@media` 写法一致,容易上手
|
||||
|
||||
#### 3.7.3 核心功能对比示例
|
||||
|
||||
##### 1. 变量:一处修改,全局生效
|
||||
|
||||
**场景**:主题色 `#3b82f6` 在 20 个地方用到,要改成红色。
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="原生 CSS">
|
||||
|
||||
```css
|
||||
.card {
|
||||
background: white;
|
||||
}
|
||||
.card h2 {
|
||||
color: #3b82f6;
|
||||
}
|
||||
.card:hover {
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
/* 要改 20 处,容易漏 */
|
||||
.button { background: #3b82f6; }
|
||||
.link { color: #3b82f6; }
|
||||
.border { border-color: #3b82f6; }
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="SCSS">
|
||||
|
||||
```scss
|
||||
$primary: #3b82f6;
|
||||
|
||||
.button { background: $primary; }
|
||||
.link { color: $primary; }
|
||||
.border { border-color: $primary; }
|
||||
/* 改 $primary 一处即可 */
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="LESS">
|
||||
|
||||
```less
|
||||
@primary: #3b82f6;
|
||||
|
||||
.button { background: @primary; }
|
||||
.link { color: @primary; }
|
||||
.border { border-color: @primary; }
|
||||
/* 改 @primary 一处即可 */
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
##### 2. 嵌套:层级关系一目了然
|
||||
|
||||
**场景**:导航栏里有多层结构。
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="原生 CSS">
|
||||
|
||||
```css
|
||||
/* 写成一长串,难看出层级关系 */
|
||||
.navbar .nav-list .nav-item .nav-link { }
|
||||
.navbar .nav-list .nav-item .nav-link:hover { }
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="SCSS">
|
||||
|
||||
```scss
|
||||
.navbar {
|
||||
.nav-list {
|
||||
.nav-item {
|
||||
.nav-link {
|
||||
&:hover { } /* & 表示父选择器 */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**SCSS vs Less vs 原生 CSS**:
|
||||
</TabItem>
|
||||
<TabItem label="LESS">
|
||||
|
||||
| 特性 | 原生 CSS | SCSS | Less |
|
||||
|------|----------|------|------|
|
||||
| 变量 | ✅ `--var` | ✅ `$var` | ✅ `@var` |
|
||||
| 嵌套 | ❌ | ✅ | ✅ |
|
||||
| 混入 | ❌ | ✅ `@mixin` | ✅ `.mixin()` |
|
||||
| 学习曲线 | 简单 | 中等 | 中等 |
|
||||
```less
|
||||
.navbar {
|
||||
.nav-list {
|
||||
.nav-item {
|
||||
.nav-link {
|
||||
&:hover { }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
##### 3. 混入(Mixin):复用代码片段
|
||||
|
||||
**场景**:多个按钮都需要"居中显示"的样式。
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="原生 CSS">
|
||||
|
||||
```css
|
||||
/* 复制粘贴 3 次 */
|
||||
.btn-primary {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.btn-secondary {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="SCSS">
|
||||
|
||||
```scss
|
||||
@mixin center {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.btn-primary { @include center; }
|
||||
.btn-secondary { @include center; }
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="LESS">
|
||||
|
||||
```less
|
||||
.center() {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.btn-primary { .center(); }
|
||||
.btn-secondary { .center(); }
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
#### 3.7.4 如何选择?
|
||||
|
||||
| 情况 | 推荐选择 |
|
||||
|------|----------|
|
||||
| 刚开始学,项目小 | **原生 CSS**(先打好基础) |
|
||||
| 项目用 Bootstrap 5 | **SCSS**(Bootstrap 源码是 SCSS) |
|
||||
| 团队熟悉 `@` 符号 | **LESS**(和 CSS 的 `@media` 写法一致) |
|
||||
| 需要复杂逻辑(循环、条件) | **SCSS**(功能更强大) |
|
||||
|
||||
#### 3.7.5 在项目中使用
|
||||
|
||||
**Vite 项目(最简单)**:
|
||||
|
||||
```bash
|
||||
# 安装 sass
|
||||
npm install -D sass
|
||||
|
||||
# 直接使用 .scss 或 .less 文件
|
||||
```
|
||||
|
||||
::: tip 💡 新手建议
|
||||
|
||||
1. **先学好原生 CSS**:预处理器只是"语法糖",本质还是 CSS
|
||||
2. **项目大了再用 SCSS**:小项目直接写 CSS 更简单
|
||||
3. **现代 CSS 已经支持变量**:`--primary-color: #3b82f6;` 原生就能用
|
||||
1. **先学好原生 CSS**:预处理器只是"语法糖",不懂 CSS 基础会越用越乱
|
||||
2. **小项目不用强上**:CSS 不到 200 行,直接写 CSS 更简单
|
||||
3. **从 SCSS 开始**:语法和 CSS 几乎一样,只是多了 `$` 变量
|
||||
4. **不要嵌套太深**:超过 3 层会让代码难维护
|
||||
:::
|
||||
|
||||
#### 3.7.6 不同技术栈的文件组织对比
|
||||
|
||||
**同样的项目,用不同技术栈,文件结构有什么不同?**
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="原生 HTML + CSS">
|
||||
|
||||
```
|
||||
my-website/
|
||||
├── index.html # 页面结构
|
||||
├── about.html
|
||||
├── css/
|
||||
│ ├── reset.css # 重置样式
|
||||
│ ├── layout.css # 布局样式
|
||||
│ ├── components.css # 组件样式
|
||||
│ └── style.css # 主样式(可能上千行)
|
||||
├── js/
|
||||
│ └── main.js
|
||||
└── images/
|
||||
└── logo.png
|
||||
```
|
||||
|
||||
**特点**:
|
||||
- CSS 集中在一个或几个文件
|
||||
- 改样式要来回切换 HTML 和 CSS 文件
|
||||
- 样式容易互相冲突
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Vue + 原生 CSS">
|
||||
|
||||
```
|
||||
src/
|
||||
├── components/ # 组件文件夹
|
||||
│ ├── Button/
|
||||
│ │ ├── Button.vue # 模板 + 样式 + 逻辑
|
||||
│ │ └── Button.test.js
|
||||
│ ├── Header/
|
||||
│ │ └── Header.vue
|
||||
│ └── Footer/
|
||||
│ └── Footer.vue
|
||||
├── views/ # 页面文件夹
|
||||
│ ├── Home.vue
|
||||
│ └── About.vue
|
||||
├── App.vue # 根组件
|
||||
└── main.js # 入口文件
|
||||
```
|
||||
|
||||
**Button.vue 内部结构**:
|
||||
```vue
|
||||
<template>
|
||||
<button class="btn">点击</button>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default { name: 'Button' }
|
||||
</script>
|
||||
|
||||
<style scoped> <!-- scoped 样式只影响当前组件 -->
|
||||
.btn { background: #3b82f6; }
|
||||
</style>
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Vue + SCSS">
|
||||
|
||||
```
|
||||
src/
|
||||
├── assets/
|
||||
│ └── styles/
|
||||
│ ├── _variables.scss # 变量:颜色、间距等
|
||||
│ ├── _mixins.scss # 混入:复用代码块
|
||||
│ ├── _functions.scss # 函数:颜色计算等
|
||||
│ └── global.scss # 全局样式入口
|
||||
├── components/
|
||||
│ ├── Button/
|
||||
│ │ └── Button.vue # 组件内用 @import 引入变量
|
||||
│ └── Card/
|
||||
│ └── Card.vue
|
||||
├── views/
|
||||
│ ├── Home.vue
|
||||
│ └── About.vue
|
||||
├── App.vue
|
||||
└── main.js
|
||||
```
|
||||
|
||||
**_variables.scss**:
|
||||
```scss
|
||||
$primary: #3b82f6;
|
||||
$secondary: #64748b;
|
||||
$spacing-sm: 8px;
|
||||
$spacing-md: 16px;
|
||||
```
|
||||
|
||||
**Button.vue**:
|
||||
```vue
|
||||
<style scoped lang="scss">
|
||||
@import '@/assets/styles/variables';
|
||||
|
||||
.btn {
|
||||
background: $primary; // 使用变量
|
||||
padding: $spacing-md;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Vue + Tailwind CSS">
|
||||
|
||||
```
|
||||
src/
|
||||
├── components/
|
||||
│ ├── Button.vue # 不需要 style 块
|
||||
│ ├── Card.vue
|
||||
│ └── Header.vue
|
||||
├── views/
|
||||
│ ├── Home.vue
|
||||
│ └── About.vue
|
||||
├── App.vue
|
||||
└── main.js
|
||||
|
||||
# 配置文件(根目录)
|
||||
tailwind.config.js # 主题配置
|
||||
tailwind.css # 基础样式入口
|
||||
```
|
||||
|
||||
**Button.vue**(没有 style 块):
|
||||
```vue
|
||||
<template>
|
||||
<button class="bg-blue-500 hover:bg-blue-600 px-4 py-2 rounded">
|
||||
点击
|
||||
</button>
|
||||
</template>
|
||||
```
|
||||
|
||||
**特点**:
|
||||
- 没有单独的样式文件
|
||||
- 类名就是样式(`bg-blue-500` = 蓝色背景)
|
||||
- 配置集中在 `tailwind.config.js`
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
**核心区别总结**:
|
||||
|
||||
| 技术栈 | 样式文件位置 | 主题管理 | 代码复用 |
|
||||
|--------|-------------|----------|----------|
|
||||
| 原生 HTML+CSS | 集中式 `css/` 文件夹 | 搜索替换 | 复制粘贴 |
|
||||
| Vue + CSS | 分散在 `.vue` 组件内 | 搜索替换 | 复制粘贴 |
|
||||
| Vue + SCSS | 组件内 + `styles/` 公共文件 | 变量统一管理 | 混入复用 |
|
||||
| Vue + Tailwind | 无(类名里) | `tailwind.config.js` | 类名组合 |
|
||||
|
||||
### 3.8 如何记住这么多 CSS 属性?
|
||||
|
||||
::: tip 🎯 新手困惑
|
||||
|
||||
Reference in New Issue
Block a user