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:
sanbuphy
2026-02-25 12:22:49 +08:00
parent f44c842fe7
commit df51f84ab5
36 changed files with 8535 additions and 393 deletions
@@ -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,避免单个文件过大
- 区分全局状态和局部状态,不要什么都放全局
- 使用组合式 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/)
@@ -434,69 +434,365 @@ Flexbox 是现代 CSS 最常用的布局方式。它让元素自动排列对齐
| `flex-wrap` | 是否换行 | `nowrap``wrap` |
| `gap` | 元素间距 | `10px``1rem` |
### 3.7 SCSSCSS 的"升级版"
### 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 🎯 新手困惑