Files
order_site/docs/frontend-optimization-plan.md
T

525 lines
13 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.
# apps/frontend 前端优化计划
## 背景
本计划基于当前 `apps/frontend` 代码现状整理。当前前端使用 Vue 3、Vite、TypeScript、Vue Router、Element Plus,已开启 TypeScript strict 相关检查,路由页面基本采用动态 import,`npm run build` 当前可通过。
当前主要问题不是功能不可用,而是后台管理页面增多后,列表页重复逻辑、请求稳定性、登录态安全、构建产物治理和工程规范需要系统化优化。
## 优化目标
- 提升后台页面的可维护性和复用性。
- 减少列表页请求竞态和状态错乱风险。
- 强化管理端登录态和开发服务器安全边界。
- 规范构建产物和依赖拆包,提升缓存效果。
- 补齐 lint / format / CI 质量门禁。
- 为后续后台功能扩展降低开发成本。
## 当前观察
### 已确认现状
- `apps/frontend/package.json` 当前脚本包含 `dev``build``preview``typecheck`
- `apps/frontend/tsconfig.app.json` 已开启 `strict``noUnusedLocals``noUnusedParameters` 等检查。
- `apps/frontend/src/router/index.ts` 已使用动态 import 拆分页面。
- `apps/frontend/src/lib/http.ts` 统一封装 axios 请求和后台 401 处理。
- 后台列表页普遍存在相同的 loading、error、pagination、load、reset 逻辑。
- `apps/frontend/vite.config.ts` 当前 `server.host``server.allowedHosts` 配置偏开放。
- 管理端 token 当前保存在 `localStorage``hasAdminSession()` 主要检查 token 是否存在。
### 构建验证
已执行:
```bash
cd apps/frontend
npm run build
```
结果:构建成功。
## 优先级 P0:安全与稳定性
### 1. 收紧 Vite dev server 访问控制
涉及文件:
- `apps/frontend/vite.config.ts`
当前配置:
```ts
server: {
host: true,
allowedHosts: true,
}
```
风险:
- `allowedHosts: true` 会允许任意 Host 访问开发服务。
- 如果配合公网隧道或局域网暴露,开发环境边界过宽。
建议方案:
- 默认只允许本地访问。
- 需要公网调试时通过环境变量显式开启。
- 支持 Host 白名单,例如 `VITE_ALLOWED_HOSTS`
建议配置方向:
```ts
const allowedHosts = env.VITE_ALLOWED_HOSTS
? env.VITE_ALLOWED_HOSTS.split(',').map((host) => host.trim()).filter(Boolean)
: ['localhost', '127.0.0.1']
server: {
host: env.VITE_DEV_HOST === 'true',
allowedHosts: env.VITE_ALLOW_ALL_HOSTS === 'true' ? true : allowedHosts,
}
```
验收标准:
- 本地 `npm run dev` 正常。
- 未设置环境变量时不默认允许任意 Host。
- 需要公网调试时可以通过环境变量开启。
### 2. 完善管理端登录态过期判断
涉及文件:
- `apps/frontend/src/utils/admin-auth.ts`
- `apps/frontend/src/router/index.ts`
- `apps/frontend/src/lib/http.ts`
当前问题:
- `hasAdminSession()` 只检查 token 是否存在。
- `expiresAt` 已存储但前端路由守卫未主动判断是否过期。
- `getAdminRole()` 在本地 role 缺失时默认返回 `operator`,权限默认值偏宽。
建议方案:
- `hasAdminSession()` 校验 token 和 expiresAt。
- token 过期时主动 `clearAdminSession()`
- `getAdminRole()` 默认值调整为最低权限,或返回空角色并让调用方处理。
- 保持后端接口鉴权作为最终安全边界,前端仅用于体验优化。
验收标准:
- 过期 token 刷新页面会跳转登录页。
- role 缺失时不会默认获得 operator 权限。
- 401 且错误码为认证失效时仍会清理会话。
### 3. 列表请求增加竞态保护
涉及页面:
- `apps/frontend/src/views/admin/webhook-events/AdminWebhookEventsView.vue`
- `apps/frontend/src/views/admin/orders/AdminOrdersView.vue`
- `apps/frontend/src/views/admin/tasks/AdminTasksView.vue`
- `apps/frontend/src/views/admin/message-deliveries/AdminMessageDeliveriesView.vue`
- `apps/frontend/src/views/admin/users/AdminUsersView.vue`
- `apps/frontend/src/views/admin/audit-logs/AdminAuditLogsView.vue`
- `apps/frontend/src/views/admin/inventory/AdminInventoryView.vue`
当前风险:
- 用户连续查询、翻页或回车搜索时,旧请求可能晚于新请求返回并覆盖新结果。
建议方案:
- 短期使用 `requestId` 忽略过期响应。
- 中期在 HTTP 层或列表 composable 中支持 `AbortController`
示例方向:
```ts
let latestRequestId = 0
async function loadPage(page = pagination.value.page) {
const requestId = ++latestRequestId
loading.value = true
errorMessage.value = ''
try {
const response = await fetchList(page)
if (requestId !== latestRequestId) return
items.value = response.data.items
pagination.value = response.data.pagination
} catch (error) {
if (requestId !== latestRequestId) return
errorMessage.value = error instanceof Error ? error.message : '读取列表失败'
} finally {
if (requestId === latestRequestId) {
loading.value = false
}
}
}
```
验收标准:
- 快速连续点击查询或分页时,页面最终展示最后一次请求结果。
- loading 状态不会被旧请求提前关闭。
## 优先级 P1:后台列表页复用与体验
### 4. 抽取通用 `useAdminListPage`
建议新增:
- `apps/frontend/src/composables/useAdminListPage.ts`
目标统一处理:
- `loading`
- `errorMessage`
- `items`
- `pagination`
- `loadPage`
- `resetPageAndLoad`
- 请求竞态保护
- 默认错误提示
- 默认 `pageSize`
适合改造页面:
- Webhook 日志
- 订单列表
- 任务列表
- 消息发送记录
- 审计日志
- 用户列表
- 库存列表
预期收益:
- 列表页脚本代码减少约 30% 到 50%。
- 分页、查询、错误处理行为统一。
- 后续增加 URL query 同步或请求取消时只需改一处。
验收标准:
- 至少完成 2 个代表页面改造,例如 Webhook 日志和订单列表。
- 改造后 `npm run typecheck``npm run build` 通过。
### 5. 筛选条件同步到 URL Query
适合页面:
- `AdminWebhookEventsView.vue`
- `AdminOrdersView.vue`
- `AdminTasksView.vue`
- `AdminMessageDeliveriesView.vue`
- `AdminAuditLogsView.vue`
当前问题:
- 刷新页面后筛选条件丢失。
- 无法复制当前筛选链接给其他管理员。
建议方案:
- 页面初始化时从 `route.query` 读取筛选条件和页码。
- 查询、重置、分页时使用 `router.replace()` 更新 query。
- 空筛选项不写入 URL。
验收标准:
- 刷新页面保留筛选条件。
- 分享 URL 后能恢复相同列表视图。
- 重置筛选会清理相关 query。
### 6. 统一日期范围组件
当前观察:
- 订单页已经使用 `type="daterange"`
- Webhook、审计日志等页面仍使用两个独立日期选择器。
建议方案:
- 日志类、列表类页面统一使用 `el-date-picker type="daterange"`
- 内部统一映射为 `dateFrom``dateTo` API 参数。
验收标准:
- Webhook 日志、审计日志、消息发送记录日期筛选交互一致。
- API 参数保持兼容。
### 7. 统一筛选触发策略
当前问题:
- 输入框通常支持 Enter 查询。
- select/date 改变后多数页面不自动查询。
- 不同列表页交互不完全一致。
建议二选一:
- 方案 A:所有筛选项变更后不自动查询,统一点击“查询”。
- 方案 Bselect/date 变更自动查询,文本输入 Enter 或防抖查询。
建议优先方案 A,原因是后台列表数据量可能较大,可减少无意请求。
验收标准:
- 后台列表页筛选交互一致。
- 查询按钮与重置按钮行为一致。
## 优先级 P2:工程规范与类型质量
### 8. 增加 ESLint / Prettier
涉及文件:
- `apps/frontend/package.json`
- `apps/frontend/eslint.config.*`
- `apps/frontend/.prettierrc` 或等价配置
当前问题:
- package scripts 未提供 lint / format。
- 仅依赖 TypeScript 编译检查,无法覆盖 Vue 模板规范、Promise 处理、风格一致性等问题。
建议增加脚本:
```json
{
"scripts": {
"lint": "eslint . --ext .ts,.vue",
"format": "prettier --write .",
"format:check": "prettier --check ."
}
}
```
建议依赖:
- `eslint`
- `@eslint/js`
- `typescript-eslint`
- `eslint-plugin-vue`
- `prettier`
- `eslint-config-prettier`
验收标准:
- `npm run lint` 可执行。
- `npm run format:check` 可执行。
- CI 至少执行 `typecheck``lint``build`
### 9. 优化 HTTP 封装类型
涉及文件:
- `apps/frontend/src/lib/http.ts`
- `apps/frontend/src/services/**/*`
当前问题:
```ts
return http.get<ApiEnvelope<T>>(url, { params }) as unknown as Promise<ApiEnvelope<T>>
```
由于 axios interceptor 返回 `response.data`,代码使用 `as unknown as` 消除类型差异。
建议方案:
- 封装明确的 `request<T>()`
- 统一返回 `Promise<ApiEnvelope<T>>`
- blob 请求单独封装。
目标:
- 减少双重类型断言。
- 服务层返回值更可信。
- 错误类型可以逐步标准化。
验收标准:
- `apiGet``apiPost``apiDelete` 不再依赖 `as unknown as`
- 现有 services 类型不退化。
- `npm run typecheck` 通过。
## 优先级 P3:构建产物与性能
### 10. 分析并治理 bundle
当前构建观察:
- 构建成功。
- 输出中出现较多 `css-*.js` 小 chunk。
- Element Plus 相关依赖和样式可能存在进一步优化空间。
建议步骤:
1. 引入 bundle 分析工具。
2. 查看 Vue、Element Plus、lodash、qrcode、dayjs 等依赖占比。
3. 评估是否需要配置 `manualChunks`
4. 检查 Element Plus 样式自动导入是否导致过多碎片 chunk。
可选工具:
- `rollup-plugin-visualizer`
- Vite 官方构建输出分析
验收标准:
- 输出一份主要 chunk 占比结论。
- 明确是否需要手动拆分 vendor。
- 优化后首屏关键 chunk 不变大。
### 11. 稳定 vendor chunk 拆分
建议方向:
```ts
build: {
rollupOptions: {
output: {
manualChunks: {
'vue-vendor': ['vue', 'vue-router'],
'element-plus': ['element-plus'],
},
},
},
}
```
注意事项:
- 不建议盲目拆太细。
- 需要结合 bundle 分析结果决定。
- 后台页面较多时,可以考虑 admin 公共模块分组。
验收标准:
- 构建产物命名更稳定。
- 主要 vendor 包可长期缓存。
- 页面懒加载不被破坏。
## 优先级 P4:大型页面拆分
### 12. 拆分超大 Vue 文件
当前行数较大的文件包括:
- `apps/frontend/src/views/admin/fulfillment/AdminKuaishouCloudFulfillmentView.vue`
- `apps/frontend/src/views/admin/tasks/AdminTaskDetailView.vue`
- `apps/frontend/src/views/admin/fulfillment/AdminFulfillmentBindingsView.vue`
- `apps/frontend/src/views/admin/inventory/AdminInventoryView.vue`
- `apps/frontend/src/views/admin/platform-shops/AdminPlatformShopsView.vue`
建议拆分维度:
- 筛选区组件
- 表格区组件
- 表单区组件
- 弹窗组件
- 业务 composable
- 数据转换函数
验收标准:
- 单个 Vue 文件尽量控制在 300 到 500 行以内。
- 业务状态集中在 composable 或页面容器中。
- 展示组件保持 props / emits 清晰。
## 分阶段执行计划
### 阶段一:安全与稳定性基线
范围:
- 收紧 `vite.config.ts` dev server 配置。
- `hasAdminSession()` 增加过期判断。
- 给 Webhook 列表增加请求竞态保护。
验证:
```bash
cd apps/frontend
npm run typecheck
npm run build
```
### 阶段二:列表页抽象
范围:
- 新增 `useAdminListPage`
- 先改造 Webhook 日志和订单列表。
- 确认抽象合理后推广到任务、消息、用户、审计日志。
验证:
- 手动检查查询、重置、分页、错误提示。
- `npm run typecheck`
- `npm run build`
### 阶段三:筛选体验统一
范围:
- 日期范围统一为 `daterange`
- 列表页 URL query 同步。
- 统一查询触发策略。
验证:
- 刷新页面保留筛选。
- 分享链接可恢复筛选。
- 重置筛选清理 URL。
### 阶段四:工程质量门禁
范围:
- 增加 ESLint / Prettier。
- 增加 lint / format:check 脚本。
- CI 或本地验证流程加入 typecheck、lint、build。
验证:
```bash
cd apps/frontend
npm run typecheck
npm run lint
npm run build
```
### 阶段五:构建与大型页面治理
范围:
- Bundle 分析。
- 评估并配置 manualChunks。
- 拆分 900 行以上的大型 Vue 页面。
验证:
- 构建成功。
- 产物 chunk 数量和体积有明确对比。
- 页面功能无回归。
## 风险与注意事项
- 列表页抽象不要一次性改造所有页面,建议先选 2 个页面试点。
- URL query 同步要避免 watcher 循环触发请求。
- token 存储机制如果改为 Cookie,需要后端配合,不建议前端单独推进。
- manualChunks 需要基于分析结果调整,避免拆包过度导致请求数过多。
- ESLint 首次接入可能暴露大量历史问题,建议先设置合理规则,再逐步收紧。
## 推荐近期落地清单
1. 修改 `vite.config.ts`,收紧 `allowedHosts`
2. 修改 `admin-auth.ts`,增加 token 过期判断。
3.`AdminWebhookEventsView.vue` 试点请求竞态保护和日期范围统一。
4. 抽取 `useAdminListPage`,先改造 Webhook 和订单列表。
5. 增加 ESLint / Prettier 基础配置。
6. 引入 bundle 分析,确认 Element Plus 和 CSS chunk 优化方向。