Files
global-coding-governance/vue-coding-style/references/architecture.md
T
2026-07-25 23:45:09 +08:00

108 lines
6.6 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.
# Vue 架构与职责边界
## 目录
- 结构原则
- 组件边界
- Composable
- Pinia
- Vue Router
- API 与数据边界
- 表单
- SSR 与 Hydration
- 依赖和公共 API
- 官方依据
## 结构原则
- 先遵循现有目录结构;不要在单个需求中引入第二套架构。
- 新建中大型项目或独立业务域时,优先按 feature/domain 聚合页面、组件、composable、store、服务和类型;将真正跨域的稳定能力放入 `shared`
- 避免把 `components/``utils/``types/` 变成无边界仓库。目录名需表达所属业务或技术职责。
- 只允许单向依赖:应用层可依赖共享层,共享层不得反向依赖具体页面或业务域。
- 在浏览器、服务端、存储、网络和第三方 SDK 边界建立适配层,避免组件直接承载协议细节。
## 组件边界
- 让页面组件负责路由级编排,让业务组件负责明确用例,让基础组件负责可复用交互和展示。
- 组件同时承担数据请求、复杂业务计算、多块无关 UI 和跨域副作用时,按职责拆分。
- 不按行数机械拆分;仅在职责、复用、测试隔离或性能边界更清晰时提取。
- 保持公共组件 API 小而稳定。避免大量布尔 props 组合出互斥模式;使用判别联合、variant 或具名 slots。
- 不让父组件通过 DOM 查询或 template ref 修改子组件内部状态。
- 向下传递少量明确数据;深层跨树依赖使用 provide/inject 时定义 typed injection key,并提供缺失依赖处理。
- 设计组件时覆盖默认、loading、empty、error、disabled 和只读等适用状态。
## Composable
- composable 封装可复用的有状态逻辑、生命周期或外部系统连接,不把纯函数包装成 composable。
- 接收明确参数或响应式输入;不要隐式读取任意全局单例,除非该依赖就是契约的一部分。
- 返回稳定、最小的公开 API。将 state 与操作区分清楚,必要时返回 readonly state。
- 由创建资源的一方负责释放资源;在内部注册 cleanup,或向调用方暴露明确 `stop`/`dispose`
- 避免一个 composable 同时处理请求、表单、路由、通知和持久化;按用例编排更小能力。
- 对有宿主组件依赖的 composable 标明生命周期约束;纯响应式 composable 应可直接单测。
## Pinia
- 仅将跨组件、跨页面、需持久化或需 DevTools 跟踪的状态放入 store。
- 每个 store 表达一个稳定业务域,使用唯一 id,并在独立文件定义。
- state 保持最小;派生值使用 getters/computed;状态改变和业务动作通过 actions 或明确方法完成。
- 不在组件外随意解构 store;在组件中用 `storeToRefs` 解构响应式 state/gettersactions 可直接解构。
- setup store 必须返回所有应被 Pinia 管理的 state;不要依赖“私有 state”技巧破坏 SSR、DevTools 或插件行为。
- 不在 store 中保存 Router/Route、DOM 元素或不可序列化第三方实例;这些依赖留在调用层或适配层。
- 持久化仅保存必要字段,并处理版本迁移、过期、隐私与反序列化失败。
- 测试 store 的公开 action、getter 和状态转移,不依赖内部实现顺序。
## Vue Router
- 页面级组件使用动态 import 做路由懒加载;不要把异步组件与路由懒加载混为一谈。
- 优先命名路由,集中定义 path、name 和 meta 类型。
- 将认证、权限和通用重定向放在合适的全局或路由级 guard;不要把所有页面业务塞入全局 guard。
- guard 返回导航结果或抛出可处理错误;避免多次调用导航、隐式 fall-through 和未处理竞态。
- 客户端路由权限只控制体验,不构成安全边界;后端必须独立授权。
- 路由参数和 query 都是不可信字符串输入,使用前解析、校验并处理缺失或重复值。
- 编程式导航需要时等待结果并处理 navigation failure。
## API 与数据边界
- 将 HTTP 客户端配置、认证头、错误归一化和序列化放入 API 基础层。
- 按业务域定义 service/repository 函数,组件不直接拼接重复 URL 或处理底层响应结构。
- 为请求参数和响应 DTO 提供类型;对不可信响应在运行时校验关键字段。
- 在边界将 DTO 映射为前端领域模型,避免蛇形命名、nullable 约定和后端枚举扩散到整个 UI。
- 支持请求取消或过期响应丢弃;切页、快速搜索和重复提交必须处理竞态。
- 对 mutation 提供重复提交保护、幂等策略或清晰恢复方式。
- 不在客户端持有服务端密钥。`VITE_*` 内容会进入客户端 bundle,只能存放公开配置。
- 将用户可见错误转换为可行动信息,同时保留脱敏的诊断上下文。
## 表单
- 分离原始 DTO、可编辑表单值、验证结果和提交 payload。
- 明确字段 touched、dirty、validating、disabled 和 submitting 状态。
- 客户端验证用于即时反馈,服务端仍是最终业务校验来源。
- 服务端字段错误映射回对应控件,并提供表单级错误摘要或焦点移动。
- 避免 watcher 组成不可追踪的双向同步链;建立单一数据源和显式 reset/submit 流程。
- 离开页面可能丢失编辑内容时,按需求提供确认或草稿策略。
## SSR 与 Hydration
- 先识别项目是否为 Nuxt、SSR、SSG 或纯 SPA,再选择数据获取和生命周期位置。
- 不在服务端共享请求级可变单例,避免用户间状态泄漏。
- 将浏览器 API 放在客户端生命周期或带环境判断的适配层。
- 保证服务端与首次客户端渲染结构一致;时间、随机数、locale 和设备判断需避免 hydration mismatch。
- setup store、持久化插件和请求缓存必须符合项目 SSR 约定。
## 依赖和公共 API
- 新增依赖前证明现有能力不足,并评估体积、维护状态、类型、许可证、SSR 和 tree-shaking。
- 不因单个小功能引入大型工具库。
- 公共组件、composable、store 和类型一旦被多个模块使用,即视为契约;修改时检查全部调用方并补充回归测试。
- 删除或重命名公共导出前,先迁移调用方并确认兼容策略。
## 官方依据
- [Vue Composables](https://vuejs.org/guide/reusability/composables.html)
- [Vue 状态管理](https://vuejs.org/guide/scaling-up/state-management.html)
- [Pinia:定义 Store](https://pinia.vuejs.org/core-concepts/)
- [Vue Router:懒加载路由](https://router.vuejs.org/guide/advanced/lazy-loading.html)
- [Vue Router:导航守卫](https://router.vuejs.org/guide/advanced/navigation-guards.html)
- [Vite 环境变量与模式](https://vite.dev/guide/env-and-mode)