Skip to content

整体架构

本篇给出 forge-admin-web 的分层与数据流向:九层各自管什么、数据为什么是单向流动的、一次页面渲染要穿过哪些层。

九层分层

从浏览器请求到屏幕像素,前端分九层,自底向上:

目录职责关键文件
① 入口与路由main.ts router/ App.vue装配应用、菜单驱动路由、登录守卫、主题 Providerrouter/index.ts
② 网络层api/axios 实例、统一错误、雪花 ID、元数据/CRUD/鉴权端点http.ts meta.ts data.ts
③ 状态管理stores/模块/菜单/schema 三级缓存、token/用户、壳偏好app.ts auth.ts
④ 组合式逻辑composables/URL 列表状态、schema 工具、可见性表达式、ToastlistQuery.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 请求。

依赖方向

层与层之间的依赖是自上而下的(上层依赖下层),没有反向依赖,避免循环:

  • 视图/组件 → 依赖 → composables / stores / api / widgets
  • widgets → 只依赖 → protocol.ts 类型与 antd,不反向依赖任何业务组件
  • api http.ts → 通过回调(setUnauthorizedHandler)解耦对 router 的依赖,避免 http ↔ router 循环

http 与 router 的循环依赖如何破

http.ts 需要在 401 时跳登录页,但它若直接 import router 会与 router 的守卫形成循环。解法是 http.ts 暴露 setUnauthorizedHandler(fn),由 main.ts 注入「跳登录」的回调。这是典型的依赖倒置破环,见网络层与状态

下一步