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

13 KiB
Raw Blame History

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 当前脚本包含 devbuildpreviewtypecheck
  • apps/frontend/tsconfig.app.json 已开启 strictnoUnusedLocalsnoUnusedParameters 等检查。
  • 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.hostserver.allowedHosts 配置偏开放。
  • 管理端 token 当前保存在 localStoragehasAdminSession() 主要检查 token 是否存在。

构建验证

已执行:

cd apps/frontend
npm run build

结果:构建成功。

优先级 P0:安全与稳定性

1. 收紧 Vite dev server 访问控制

涉及文件:

  • apps/frontend/vite.config.ts

当前配置:

server: {
  host: true,
  allowedHosts: true,
}

风险:

  • allowedHosts: true 会允许任意 Host 访问开发服务。
  • 如果配合公网隧道或局域网暴露,开发环境边界过宽。

建议方案:

  • 默认只允许本地访问。
  • 需要公网调试时通过环境变量显式开启。
  • 支持 Host 白名单,例如 VITE_ALLOWED_HOSTS

建议配置方向:

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

示例方向:

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 typechecknpm 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"
  • 内部统一映射为 dateFromdateTo 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 处理、风格一致性等问题。

建议增加脚本:

{
  "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 至少执行 typechecklintbuild

9. 优化 HTTP 封装类型

涉及文件:

  • apps/frontend/src/lib/http.ts
  • apps/frontend/src/services/**/*

当前问题:

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 请求单独封装。

目标:

  • 减少双重类型断言。
  • 服务层返回值更可信。
  • 错误类型可以逐步标准化。

验收标准:

  • apiGetapiPostapiDelete 不再依赖 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 拆分

建议方向:

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 列表增加请求竞态保护。

验证:

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。

验证:

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 优化方向。