前端项目架构设计与选型
从分层边界、PC/移动端差异到测试发布闭环,建立可解释、可演进的前端架构决策方法。
前端项目架构设计与选型
整理来源:
第五篇 - 面试利器之前端技术架构设计实操.pdf的分层图与 PC/移动端模板清单,以及第四篇 - 面试高频踩坑解析.pdf的工程化追问。这里把依赖清单改写为约束、边界和验证方法,过滤机构、课程、报名信息和无法复核的效果数字。
1. 架构题先回答约束
架构不是把库名排成一列,而是让变化被限制在合适的边界内。开始设计前先确认:
- 运行环境:纯浏览器、SSR、桌面壳还是移动 WebView;是否需要离线和旧浏览器兼容。
- 产品形态:管理端、内容站、交互应用或组件库;页面数量、权限模型和 SEO 要求。
- 数据边界:服务端状态、客户端 UI 状态、缓存、表单草稿和敏感凭据分别由谁拥有。
- 团队与交付:包/目录边界、多人并行方式、测试门禁、发布渠道和回滚入口。
- 可观测性:要验证的用户指标、错误上下文、版本标识和降级路径。
没有这些约束时,所谓“最佳技术栈”只是偏好。面试中应先给一个可运行的最小方案,再说在约束变化时如何替换。
2. 用依赖方向表达分层
资料中的展示层、业务逻辑层和数据访问层可以落成下面的依赖方向:
页面/组件 -> ViewModel/DTO 映射 -> 用例与领域规则
-> 仓储/数据访问接口
-> HTTP、Storage、数据库或原生能力
分层的重点是依赖方向,而不是层数:
- 展示层负责路由入口、可访问性、用户交互和 ViewModel,不直接拼接数据库字段。
- 应用/领域层负责用例、状态转移、校验和错误语义,不依赖具体 UI 库。
- 数据访问层负责 API 客户端、缓存、序列化和重试策略,通过接口向上提供能力。
- 基础设施层负责浏览器 API、日志、配置、监控和第三方适配;不能把密钥下发到浏览器。
DTO、BO、VO、PO 等名词只有在边界真的不同的时候才有价值。若只是复制对象改名,会增加维护成本;若外部 API 经常变化,单独的映射层可以隔离后端契约和页面模型。
3. PC 与移动端模板如何取舍
常见 Vue 模板会组合 Router、文件系统路由、布局插件、Pinia、TypeScript、Markdown/代码高亮、国际化、head 管理和 Vitest;移动端可能再加入触摸友好的组件库。它们是候选能力,不是必须成套安装的清单:
| 关注点 | PC 管理端 | 移动端 Web | 选型证据 |
|---|---|---|---|
| 布局 | 多栏、表格、键盘操作和可调整面板 | 单列、手势、窄屏和系统回弹 | 关键页面交互原型与可访问性测试 |
| 路由/布局 | 文件系统路由可减少重复声明,但仍需权限守卫 | 需要处理返回栈、深链和网络恢复 | 路由状态图、刷新/返回测试 |
| 状态 | 筛选条件、批量操作和 URL 同步较多 | 草稿、滚动位置和离线恢复更重要 | 状态所有权表与持久化策略 |
| UI 组件 | 桌面密度、焦点和拖拽 | 触摸命中区、键盘替代和安全区 | 真实设备交互测试 |
| 内容/SEO | Markdown 组件和文档头可能是核心能力 | 关注首屏、带宽和渐进增强 | SSR/性能预算与降级实验 |
| 测试 | 单元、组件交互、浏览器 E2E | 加入触摸、旋转、低带宽和页面恢复 | 稳定的 CI 场景矩阵 |
例如,文件系统路由只解决路由发现和组织,不自动解决权限;Pinia 只解决一类客户端状态,不代替服务端缓存一致性;Markdown 渲染器必须配合 HTML 消毒和内容来源约束。
3.1 工具职责与验证边界
把模板中的工具按“谁拥有哪类变化”拆开,面试时才能从库名讲到实际架构:
| 工具/能力 | 应负责什么 | 不应替代什么 | 最小验证 |
|---|---|---|---|
| Router + 文件系统路由/布局插件 | 路由发现、布局组合、参数解析和导航状态 | 服务端授权、业务状态和数据缓存 | 深链、刷新、前进后退、未授权跳转 |
| Pinia 或局部 store | 客户端 UI、跨页面偏好和少量会话派生状态 | 服务端查询缓存、事务和最终权限判断 | 订阅粒度、刷新恢复、登出清理、SSR 隔离 |
| Markdown + 代码高亮 | 受信内容的结构化渲染和阅读体验 | HTML 安全、任意组件执行和用户输入消毒 | 恶意标签/链接、代码块、目录锚点和 SSR 输出 |
| i18n + head 管理 | locale 选择、文案回退、标题和 meta 同步 | URL 权限、数据翻译和 SEO 的全部保证 | 切换语言、刷新深链、缺失 key、服务端/客户端一致性 |
| VueUse/平台适配器 | 封装重复的浏览器、交互和生命周期逻辑 | 隐藏副作用、权限或资源释放责任 | SSR/浏览器分支、卸载清理、异常和降级 |
| 移动端组件库 | 触摸命中区、手势、安全区和移动控件语义 | 桌面布局、业务状态和跨端数据协议 | 真机触摸、旋转、低带宽、键盘替代和可访问性 |
| Vitest | 纯函数、状态转移、适配器和组件局部行为 | 真实浏览器导航、网络链路和发布产物 | 边界输入、异步取消、稳定 fixture 和覆盖风险 |
| Cypress 等浏览器 E2E | 真实路由、权限、关键用户流程和回归 | 单元级所有分支、性能基准和生产监控 | 隔离数据、网络 stub、可访问定位器、截图/trace |
这张表也说明了为什么不能把“安装了测试框架”写成“有测试保障”:每一层都要对应风险、夹具和失败后的处理方式。PC 与移动端可以共享领域用例和设计令牌,但应分别验证交互和渲染边界,避免复制一套页面后再用 CSS 补救所有差异。
4. 技术选型记录(ADR)
每个会影响公共边界的选择都应留下短记录:
背景:多个页面需要共享筛选条件,并支持刷新后恢复。
约束:筛选可分享、服务端可复现,不能把令牌放进 URL。
选项:仅组件状态、Pinia、URL + 页面状态的组合。
决定:可分享的筛选进入 URL,瞬时 UI 状态留在页面;请求缓存由请求层负责。
验证:深链、刷新、前进后退、并发请求和权限变化测试。
回滚:保留旧参数解析一段兼容期,失败时回退到默认筛选。
选型评审至少比较兼容性、包体/启动成本、调试能力、团队熟悉度、迁移成本和失败时的降级方式。不要用未经复核的“快多少”“节省多少”替代基准;没有测量时写成待验证假设。
5. 状态、路由和数据的边界
建议先画状态所有权,而不是先决定状态库:
| 状态 | 事实来源 | 生命周期 | 常见风险 |
|---|---|---|---|
| URL 查询参数 | 地址栏/路由 | 可分享、可回退 | 把敏感信息写入历史记录 |
| 服务端数据 | API/缓存层 | 可失效、可重取 | 竞态响应覆盖新请求 |
| 页面 UI 状态 | 组件或局部 store | 页面级 | 全局 store 过度耦合 |
| 表单草稿 | 表单模型/Storage | 受控持久化 | 版本迁移和隐私泄露 |
| 会话凭据 | 受控认证边界 | 会话级 | XSS、日志和跨端同步泄露 |
请求层应定义取消、超时、重试、去重和错误码;页面只消费语义化结果。路由守卫负责导航前置检查,服务端仍必须做最终授权,不能把前端隐藏菜单当成安全边界。
6. 从代码到发布的质量闭环
一套可交付的架构至少包含:
- 类型检查和 Lint:配置、生成目录和运行环境边界可审查。
- 单元/组件测试:验证状态转移、错误和键盘/触摸交互。
- 浏览器 E2E 与 SSR/水合检查:验证真实路由、权限和首屏标记。
- 可复现构建:锁定依赖和 Node/包管理器版本,产物由当前提交生成。
- 发布治理:版本标识、灰度或分批策略、错误监控、回滚入口和兼容窗口。
组件库还要检查 exports、类型声明、CSS 副作用和消费者安装;详见前端组件库工程化。监控 SDK、缓存和灰度策略可结合前端稳定性与跨端交互与发布治理。
7. 架构评审清单
- 每个跨层调用是否有明确的接口和错误语义?
- 展示层是否能在不改领域规则的情况下替换 UI 框架?
- 服务端数据、UI 状态和凭据是否由不同边界管理?
- 第三方库是否有替换成本、版本策略和公开入口?
- 关键路径是否有最小实验、测试、监控字段和回滚方案?
- PC/移动端的差异是否来自真实交互约束,而不是复制两份代码?
- 新增一层是否真正隔离了变化;若没有,是否应删掉这层?