搜索 K
Appearance
Appearance
本篇给出 forge-admin-web 的分层与数据流向:九层各自管什么、数据为什么是单向流动的、一次页面渲染要穿过哪些层。
从浏览器请求到屏幕像素,前端分九层,自底向上:
| 层 | 目录 | 职责 | 关键文件 |
|---|---|---|---|
| ① 入口与路由 | main.ts router/ App.vue | 装配应用、菜单驱动路由、登录守卫、主题 Provider | router/index.ts |
| ② 网络层 | api/ | axios 实例、统一错误、雪花 ID、元数据/CRUD/鉴权端点 | http.ts meta.ts data.ts |
| ③ 状态管理 | stores/ | 模块/菜单/schema 三级缓存、token/用户、壳偏好 | app.ts auth.ts |
| ④ 组合式逻辑 | composables/ | URL 列表状态、schema 工具、可见性表达式、Toast | listQuery.ts resource.ts |
| ⑤ 应用壳 | layouts/ | 骨架、三导航、顶栏、命令面板 | MainLayout.vue |
| ⑥ 路由视图 | views/ | 页头/返回、拉 schema、组合元数据组件 | ResourceListView.vue |
| ⑦ 业务组件 | components/ | 按 schema 布局表格/表单/详情/搜索 | MetaTable.vue MetaForm.vue |
| ⑧ 渲染引擎 ★ | widgets/ | 多维注册表,为字段选组件 | registry.ts index.ts |
| ⑨ 主题 | theme/ | antd 令牌、壳 CSS 变量、图标 | tokens.ts forge.css |
九层之间有三条关键分界,理解了它们就理解了整个架构:
| 腰线 | 上方 | 下方 | 切分理由 |
|---|---|---|---|
| 数据 / 布局 | 网络层+状态只管「拿到 schema 和数据」 | 视图+组件只管「怎么排」 | 数据获取可缓存、可复用;布局纯展示、无 IO |
| 布局 / 渲染 | 组件按 schema 决定「哪些字段、怎么分组、列顺序」 | 引擎决定「单个字段用什么组件」 | 结构通用、字段可插拔,各自独立演进 |
| 容器 / 元数据组件 | Resource* 容器管 IO(拉数据、提交、回退端点) | Meta* 组件纯按 props 渲染 | 同一 Meta* 既服务整页路由、也服务列表抽屉 |
为什么 Resource* 与 Meta* 要拆开
ResourceForm(容器)负责「拉 schema、拉记录、按三级回退选提交端点、提交」;MetaForm(元数据组件)只接收 schema/record 然后渲染。这条切分让 MetaForm 成为纯函数式组件——列表页抽屉和独立表单页都复用它,IO 差异全被容器吸收。详见请求时序 · 保存。
前端严格遵循单向数据流,没有「组件直接改全局数据」的暗箱:
具体到列表页,这个环尤其清晰:用户翻页 → 改 URL query → watch 到 query 变化 → 重新解析状态 + 重新请求 → 新数据 props 下发 → 表格重渲。用户操作从不直接写数据,只改 URL 或发 API,由此驱动下一轮渲染。这套机制的设计动机见网络层与状态 · URL 单一事实源。
把九层落到一次具体的「打开商品列表」上:
注意两条独立的请求线:元数据线(schema,可长缓存)与业务数据线(records,随 URL 状态刷新)。这种分离让「同一页面切页签/翻页」只重发数据请求、不重发 schema 请求。
层与层之间的依赖是自上而下的(上层依赖下层),没有反向依赖,避免循环:
protocol.ts 类型与 antd,不反向依赖任何业务组件http.ts → 通过回调(setUnauthorizedHandler)解耦对 router 的依赖,避免 http ↔ router 循环http 与 router 的循环依赖如何破
http.ts 需要在 401 时跳登录页,但它若直接 import router 会与 router 的守卫形成循环。解法是 http.ts 暴露 setUnauthorizedHandler(fn),由 main.ts 注入「跳登录」的回调。这是典型的依赖倒置破环,见网络层与状态。