Day 17 / 共 20 天 · 第 4 周 前端/部署
前端结构与页面
今天看前端"长什么样、怎么组织":目录结构、整体布局与菜单、API 调用层,最后精读最典型的路由管理页 CRUD。理解了这套模式,前端所有页面都是同一套路。
📍 你在整门课的位置 · 第 4 周 前端/部署(Day 16-20)
D16 前端技术栈→
D17 前端结构→
D18 动态表单→
D19 部署→
D20 收官串讲
L01
目录结构
🤔 痛点:文件一多就找不到东西
昨天认识了技术栈,今天进代码。可一个前端工程动辄几百个文件——页面、组件、请求、类型、翻译混在一起,改个功能得翻半天。
💡 本质:按"职责"分抽屉,各就各位
这套目录就是"给代码分抽屉":pages 放页面、components 放复用组件、services 放 API 调用、interfaces 放类型、models 放全局状态、locales 放翻译。想改哪类东西直接开对应抽屉。理解了这套分层,前端所有页面都是同一套路。
📝 举个例子(前后端对应)
后端有
/v1/routes 接口 → 前端 services/route.ts 封装它 → interfaces/ 里的 TS 类型对应后端 Java DTO → pages/route/ 是它的页面。一个资源,一条竖线贯穿前后端。frontend/src/
app.ts 应用配置(Day 16)
i18n.ts 国际化
store.ts 全局 store
switches.ts 功能开关(SHOW_INTERNAL_ROUTES=false)
pages/ 页面(约定式路由,每个子目录 = 一个路由段)
components/ 公共组件(CodeEditor/Navbar/ChatRobot/AvatarDropdown…)
services/ API 层(按领域拆分)
interfaces/ TS 类型(与后端 DTO 对应)
models/ 全局 store model
locales/ 中英翻译
读法:典型的 React 项目分层:pages 放页面、components 放复用组件、services 放 API 调用、interfaces 放类型、models 放状态。
interfaces/ 里的 TS 类型和后端的 Java DTO 一一对应——前后端用同一套数据形状对话。L02
ProLayout 布局
pages/layout.tsx:39-88 用 @ant-design/pro-layout 的 ProLayout 当整体外壳(layout="mix"):右上角 Navbar + LanguageDropdown + AvatarDropdown,内容区 <Outlet/>(子路由出口)+ 全局 ChatRobot。
menuDataRender(:60-77):按hideFromMenu和visiblePredicate(configData)过滤,再t()翻译——菜单可按后端 config 动态显隐。:27-34若被同源 iframe 嵌套则显示 500(防嵌套攻击)。
<Outlet/> 是什么?
布局是"固定的框"(顶栏、侧边菜单),内容区随路由变化。
<Outlet/> 就是"内容区的占位坑"——你点不同菜单,对应页面就渲染进这个坑里,外框不动。好比 App 底部导航栏不变、上面内容切换。L03
路由 / 菜单定义
pages/_defaultProps.tsx:14-117 静态定义菜单/路由树,路径 → pages/ 目录约定映射:/dashboard、/service-source、/service、/route、AI 分组(/ai/provider、/ai/route、/ai/dashboard、/mcp/list)、/domain、/tls-certificate、/consumer、/plugin、/system。
读法:这张表和第 1-3 周后端的控制器几乎一一对应——每个后端资源都有一个前端页面。
/ai/dashboard 带 visiblePredicate(:76)演示条件显隐;/init 和 /login 用 hideFromMenu + usePureLayout(无外框)。L04
request 拦截器
拦截器 = 前端的"前台统一接待":进门自动盖 token 章,出门自动拆包裹、遇到 401 统一送去登录页。
🧭 第一人称:你是一个 GET /v1/routes 请求
页面调
getRoutes(),你诞生了。先过请求拦截器:它从 localStorage 摸出 token 塞进你的 Authorization 头,又给你尾巴加了个 ts=时间戳 防缓存。你飞到后端拿回一坨 {data:{...}};响应拦截器替你拆掉外壳,只把里面的业务对象交给页面——所以页面代码从不写 .data.data。要是后端回你 401,错误拦截器直接把用户送去 /login?redirect=...。这些"每次都做的事",你一次都不用自己操心。services/request.tsx 是 axios 封装,核心是拦截器:
// :14-26 请求拦截器:从 localStorage 取 token 塞进 Authorization 头;
// GET 追加 ts=Date.now() 防缓存
// :28-41 响应拦截器(成功):解包 data.data(后端统一包 {data:...})
// :42-77 错误拦截器:401 且非 login → 跳 /login?redirect=...;
// 其它错误按 i18n 文案弹 Modal
"拦截器"帮你自动做什么?
每个 API 请求都要带 token、每个响应都要解包、每个 401 都要跳登录——拦截器让你把这些"每次都做的事"写一次,所有请求自动享用。回想后端 Day 05 的 AOP 切面——前端拦截器就是前端版的"切面",一进一出统一处理。响应拦截器自动解包
data.data,所以各 service 直接拿到业务对象(对应后端 Day 04 的 Response 三段式)。L05
service 按领域拆分
services/ 每个文件对应一组后端端点,都 import request from './request'。例如 services/route.ts:
GET /v1/routes :4-6
GET /v1/routes/{name} :9-11
POST /v1/routes :13-15
DELETE /v1/routes/{name} :17-19
PUT /v1/routes/{name} :21-23
读法:前端 service 和后端 Controller 端点一一对应(Day 04 的表)。
llm-provider.ts/ai-route.ts/dashboard.ts/system.ts/plugin.ts 各管一块。前端 service 层的职责就是"把后端 REST 封装成 JS 函数",页面调函数即可,不用关心 URL。L06
精读路由管理页
👶 小白 vs 👨🏫 老师
👶:权重之和必须=100,后端 Day 09 不是已经校验过了吗?前端为什么还要再查一遍?
👨🏫:为了体验。前端先拦一道,用户点保存立刻看到红字提示,不用等一个来回的网络请求再报错。
👶:那后端那道能省掉吗?
👨🏫:绝不能。前端校验只是"礼貌提醒",绕过前端直接调 API 的请求它拦不住——后端校验才是最后防线。前后端各校验一遍是常规操作,不是重复劳动。
👨🏫:为了体验。前端先拦一道,用户点保存立刻看到红字提示,不用等一个来回的网络请求再报错。
👶:那后端那道能省掉吗?
👨🏫:绝不能。前端校验只是"礼貌提醒",绕过前端直接调 API 的请求它拦不住——后端校验才是最后防线。前后端各校验一遍是常规操作,不是重复劳动。
pages/route/index.tsx(670 行)是最典型的"列表 + 抽屉表单 + 删除确认"CRUD 页:
- 表格列(
:53-139):name/domains/routePredicates/services/auth/action 六列。服务列(:91-105)展示服务名 + 权重百分比,权重 0 标黄。 - 数据加载用 ahooks 的
useRequest(:181-209)。 - 保存
handleDrawerOK(:241+):前端也校验"服务权重之和必须 = 100"(:250-254),呼应后端 Day 09。 - 渲染骨架(
:568-665):PageContainer+ 顶部筛选 + 主Table+ 删除Modal+ 编辑Drawer(内含<RouteForm/>)。
"抽屉表单"是什么体验?
点"新建/编辑"时从屏幕右侧滑出一个面板(Drawer 抽屉)填表单,不用跳页。列表页保持在原位,抽屉里编辑,保存后关闭抽屉刷新列表。这是管理后台最常见的交互,所有资源页(域名/证书/消费者…)都是这个套路。
L07
能力探测降级
route/index.tsx:50-51:从 system model 读 capabilities,判断是否支持 config.ingress.v1,不支持就禁用"新建路由"按钮。
读法:回想后端 Day 08 的"能力探测"——后端探测集群支持哪个 Ingress 版本,把结果放进 system 信息;前端读到这个能力位,若集群不支持就把按钮灰掉,避免用户点了却失败。前后端配合的优雅降级。
L08
今日小结 + 动手
🧠 今天你应该能回答
- 前端目录怎么分层?interfaces 和后端什么关系?
- ProLayout +
<Outlet/>怎么组织布局? - request 拦截器帮你自动做了哪三件事?
- 路由页的"列表 + 抽屉 + 删除"套路?权重校验前后端都做?
✋ 动手
cd /Users/bitmart/work/codes/github/higress-group/higress-console/frontend/src
ls pages/ components/ services/
sed -n '39,88p' pages/layout.tsx
sed -n '14,77p' services/request.tsx
sed -n '241,254p' pages/route/index.tsx
明天预告 · Day 18:动态插件配置表单——前端最精华的部分:从后端返回的 JSON Schema 递归渲染出表单,还能在"表单"和"YAML 源码"两个 tab 间双向同步。这是整个前端的技术高峰。