2、技术架构设计:前端架构模式选择(SPA/SSR/SSG)、目录结构设计、组件树设计、状态管理方案
好,咱们进入第二章。说实话,技术架构设计这步,是很多团队容易「想当然」的地方。我见过不少项目,上来就选 Vue + SPA,结果做到一半发现 SEO 搞不定,或者首屏慢得离谱。嗯,今天咱们就把这事掰扯清楚。
2.1 架构模式选择:SPA、SSR 还是 SSG?
先问个问题:你的用户是谁?你的内容多久变一次?这两个问题想明白了,选型就简单了。
SPA(单页应用),说白了就是前后端分离,所有页面都在浏览器端渲染。我早期做的一个后台管理系统,用的就是 SPA。用户都是内部员工,不需要 SEO,交互又复杂,SPA 再合适不过了。
SSR(服务端渲染),每次请求都在服务端拼好 HTML 再返回。适合对首屏速度要求高、需要 SEO 的场景。比如电商、资讯站。我在项目中遇到过,一个 B2B 网站,客户要求谷歌收录必须前三页,不用 SSR 根本做不到。
SSG(静态站点生成),构建时就把页面生成好,部署到 CDN 上。适合内容不常变的场景,比如文档站、博客、官网。我记得有个客户,他们的官网一年改两次,用 SSG 简直不要太爽,成本低、速度快。
| 特性 | SPA | SSR | SSG |
|---|---|---|---|
| 首屏速度 | 慢(需下载 JS) | 快(服务端直出) | 极快(纯静态) |
| SEO 友好 | 差 | 好 | 好 |
| 动态交互 | 强 | 中 | 弱 |
| 维护成本 | 低 | 高(需 Node 服务) | 低 |
| 适用场景 | 后台、工具类 | 电商、内容平台 | 博客、官网 |
核心建议:别盲目追新。如果你的项目是内部系统,老老实实 SPA。如果是面向 C 端的内容站,优先考虑 SSG,实在不行再上 SSR。你想想看,SSR 要维护 Node 层,出问题排查起来可麻烦了。
避坑指南:我曾经有个项目,选了 Nuxt 的 SSR 模式,结果第三方 SDK 在服务端跑不了,各种 hack。后来发现其实用 SSG + 客户端动态加载就够用了。所以,先想清楚你的「动态」到底有多动态。
2.2 目录结构设计:别让代码变成一锅粥
目录结构这事,看着简单,但真能看出一个团队的工程化水平。我个人习惯按「功能」而不是按「类型」来划分。
什么意思?就是别把所有的组件都扔到 components/ 下,所有的页面都扔到 pages/ 下。那样项目一大了,找文件得翻半天。
我推荐这种结构:
src/
├── modules/ # 业务模块
│ ├── user/ # 用户模块
│ │ ├── components/ # 该模块专用组件
│ │ ├── hooks/ # 该模块逻辑
│ │ ├── pages/ # 该模块页面
│ │ └── store/ # 该模块状态
│ ├── order/ # 订单模块
│ └── product/ # 商品模块
├── shared/ # 共享资源
│ ├── components/ # 通用组件
│ ├── utils/ # 工具函数
│ └── constants/ # 常量
├── layouts/ # 布局组件
├── router/ # 路由配置
└── styles/ # 全局样式
这样设计的好处是:每个模块都是独立的,新人接手只看一个模块就行。而且模块之间耦合度低,以后拆成微前端也方便。
小技巧:我习惯在 modules/ 下每个模块里放一个 index.ts,统一导出该模块的公共内容。这样外部引用时只需要 import { UserCard } from '@/modules/user',清爽得很。
2.3 组件树设计:从原子到页面
组件树设计,说白了就是怎么把页面拆成一个个可复用的积木。我一般用「原子设计」的思路,但会结合实际项目做调整。
- 原子组件:按钮、输入框、图标。这些是最小单元,不包含业务逻辑。
- 分子组件:搜索栏、表格、弹窗。由原子组件组合而成,可能有简单的交互。
- 组织组件:用户列表、订单卡片。包含业务逻辑,但可复用。
- 页面组件:具体的页面,由组织和分子组件拼装而成。
举个例子,一个商品列表页:
<ProductListPage> <!-- 页面组件 -->
├── <SearchBar> <!-- 分子组件 -->
│ ├── <Input /> <!-- 原子组件 -->
│ └── <Button /> <!-- 原子组件 -->
├── <ProductTable> <!-- 组织组件 -->
│ ├── <Table /> <!-- 分子组件 -->
│ └── <Pagination /> <!-- 分子组件 -->
└── <ProductDetailModal> <!-- 组织组件 -->
└── <Modal /> <!-- 分子组件 -->
关键原则:每个组件只做一件事。如果一个组件超过 200 行,或者有超过 3 个 if-else 分支,就该拆了。我在项目中遇到过,一个「多功能」组件维护了半年,最后没人敢动,重构花了三周。嗯,血的教训。
2.4 状态管理方案:别滥用,也别不用
状态管理这块,争议最大。我的观点是:能用 props 传递就别用全局状态,能用局部状态就别用 Redux。
为什么?因为全局状态一多,数据流就乱了。你想想看,一个变量改了,十几个组件重新渲染,性能能不差吗?
我一般这样选:
- 组件内部状态:用
useState或ref。比如表单输入、弹窗开关。 - 组件间共享状态:用
useContext或provide/inject。比如主题、用户信息。 - 全局复杂状态:用 Pinia(Vue)或 Zustand(React)。比如购物车、权限列表。
- 服务端状态:用 React Query 或 SWR。比如从 API 获取的数据。
这里有个常见的误区:很多人把 API 返回的数据也放到全局 store 里。其实没必要,用 React Query 这类工具,自带缓存、重试、过期机制,比你自己手写强多了。
我的习惯:在项目中,我会把 store 按模块拆分。比如 useUserStore、useCartStore。每个 store 只管理自己的状态,互不干扰。这样测试也好写,出问题也好定位。
避坑指南:我曾经接手过一个项目,一个 store 里放了十几个模块的状态,光 actions 就有 50 多个。每次改一个功能,都要担心会不会影响到其他地方。后来花了两个月重构,拆成了 8 个独立的 store。所以,从一开始就做好拆分,别偷懒。
好了,技术架构设计这块,核心就是这些。记住:没有银弹,只有最适合你项目的方案。下一章咱们聊聊构建工具和打包策略,那个坑更多,到时候细说。