CMCLink 平台级秒开性能 + 高可用稳定性治理方案

日期: 2026-02-27
定位: 基于现有微前端架构,面向海运业务后台系统的性能与稳定性系统化治理方案
关联文档: 架构质量评估报告 | Dev 性能优化 | 埋点方案


一、业务场景与挑战

1.1 海运业务特征

特征技术挑战
集装箱订单量大单表 10w+ 行,列表页滚动/筛选/导出性能敏感
多模块协作7+ 子应用(单证/箱管/业财/营销/EDI/运营/IBS),频繁跨模块切换
表单复杂提单、订舱单字段 100+,动态联动、校验规则复杂
全球化部署多时区、多语言、多租户,CDN 分发与接口延迟敏感
操作人员日均高频单人日操作 500+,页面切换秒级响应是基本要求
数据准确性要求极高运费计算、关务合规零容错

1.2 当前架构现状

技术栈: Vue 3.5 + Vite 7 + TypeScript 5 + Wujie 微前端 + Pinia + Element Plus
Monorepo: pnpm workspace + Turborepo
共享包: 12 个 @cmclink/* packages
子应用: 7 个业务子应用(iframe 沙箱隔离)

已完成的治理(架构质量评估综合 4.5/5):

  • 子应用接入成本极低(3 步 5 API)
  • Dev 模式 327 请求/6s → ~150 请求/2-3s
  • 主应用壳层精简(AuthenticatedLayout 400+ → ~200 行)
  • 死代码清理(DICT_TYPE 100 → 5 个成员)
  • 可插拔埋点适配层(TelemetryAdapter)

二、治理目标与核心指标

2.1 性能 SLO(Service Level Objectives)

指标当前估值目标值衡量方式
主应用首屏 FCP~2s (dev)≤ 1.5s (prod)Lighthouse / RUM
子应用首次进入 TTI~3-4s≤ 2s埋点 child_before_load → child_after_mount
子应用二次切换~0.5-1s≤ 300ms保活模式下 wujie activated
大表格渲染(1w 行)卡顿≤ 500ms 可交互虚拟滚动 + Performance API
复杂表单首次渲染~800ms≤ 400ms异步分片渲染

2.2 稳定性 SLO

指标目标值衡量方式
JS 错误率≤ 0.1% PV全局 onerror + Vue errorHandler
接口成功率≥ 99.5%Axios 拦截器统计
子应用加载成功率≥ 99.9%埋点 + 重试机制
白屏率≤ 0.05%MutationObserver + 心跳检测
页面崩溃率0Service Worker 兜底

三、秒开性能治理体系

3.1 分层加载策略

┌──────────────────────────────────────────────────────┐
│                  用户首次访问                          │
│                                                      │
│  ① 主壳秒出(静态 HTML + 骨架屏)                      │
│  ② 认证 + 菜单数据并行拉取                             │
│  ③ 当前子应用按需加载(exec: true 已预执行)            │
│  ④ 非当前子应用空闲预加载                              │
└──────────────────────────────────────────────────────┘

3.1.1 主壳秒出(已具备 + 待增强)

策略状态说明
HTML 内联关键 CSS⏳ 待实施首屏骨架屏样式内联到 index.html,无需等 CSS 文件加载
Vite 预构建✅ 已实施optimizeDeps.include 覆盖 20+ 核心依赖
资源预热✅ 已实施server.warmup 预转换关键入口文件
路由懒加载✅ 已实施views 按路由 () => import() 分割
DNS 预解析⏳ 待实施子应用域名 <link rel="dns-prefetch">

3.1.2 子应用预加载(已具备,架构优势)

当前 wujie.ts 中的预加载机制是性能核心:

// exec: true → 预加载时即执行 JS,定义 __WUJIE_MOUNT
// 用户首次进入时已无需下载和解析 JS → 秒开
setupApp({ name, url, exec: true, fiber: true, props: getSharedProps() })
preloadApp({ name })

增强方向

增强项方案预期收益
分级预加载高频子应用(doc/ibs-manage)立即预加载;低频子应用(EDI/MKT)空闲时预加载首屏加载减少 ~30% 并发资源
预加载调度使用 requestIdleCallback 在主线程空闲时触发低优先级预加载避免预加载阻塞用户交互
预加载监控对每个子应用预加载耗时埋点,建立基线发现预加载退化

3.1.3 资源加载优化(Prod 构建链路)

策略实施方案预期收益
代码分割Vite manualChunks 按路由 + 第三方库分割首屏 JS 减少 40%+
Tree-shakingElement Plus 按需(已有)+ lodash-es(已有)减少未使用代码
Gzip/BrotliNginx 开启 br 压缩(优先于 gzip)传输体积减少 70%+
CDN 静态资源JS/CSS/字体/图片上 CDN,base 配置 CDN 域名全球访问延迟 < 100ms
HTTP/2 Server Push关键资源(vendor.js/app.css)服务端推送消除请求等待
资源版本化文件名 hash + 强缓存 1 年 + index.html 不缓存重复访问零下载

3.2 大数据量场景优化

这是海运业务系统的核心痛点 —— 集装箱订单列表动辄万级数据。

3.2.1 表格虚拟滚动

┌─────────────────────────────────────┐
│          可视区域(~20 行)           │  ← 只渲染这些 DOM
├─────────────────────────────────────┤
│                                     │
│      缓冲区(上下各 5 行)            │  ← 平滑滚动缓冲
│                                     │
├─────────────────────────────────────┤
│                                     │
│    未渲染区域(占位高度)             │  ← 仅 padding/transform
│                                     │
└─────────────────────────────────────┘

实施方案

层级方案适用场景
组件级@cmclink/uiCmcTable 集成 el-table-v2(Element Plus 虚拟表格)标准列表页
高级场景结合 @tanstack/vue-virtual 实现自定义虚拟滚动复杂自定义布局
性能基线10,000 行 × 20 列,滚动帧率 ≥ 55fps验收标准

3.2.2 分页与无限加载

策略说明
服务端分页默认 pageSize=20,用户可调 50/100,禁止全量拉取
光标分页订单流水等时序数据使用 cursor-based pagination,避免 offset 性能劣化
搜索降级模糊搜索 > 500ms 无结果时,自动切换为精确搜索提示

3.2.3 大表单性能

策略说明
分片渲染100+ 字段表单分 Tab/Collapse 区域,首屏只渲染第一个区域
v-memo静态区域(如收货人地址块)使用 v-memo 避免重复渲染
computed 缓存联动计算(运费 = 基价 × 数量 × 汇率)使用 computed 避免重复计算
防抖校验输入框校验 debounce 300ms,避免每次击键触发远程校验

3.3 运行时性能守卫

3.3.1 内存管理

问题方案
子应用内存泄漏wujie iframe 沙箱天然隔离;子应用 deactivate 时清理 EventListener/Timer
Pinia Store 膨胀大列表数据不存 Store,仅组件级 ref 持有;Store 只存 session 级状态
DOM 节点过多虚拟滚动限制 DOM < 500 节点;关闭的 Tab 对应的子应用 deactivate
长列表图片loading="lazy" + IntersectionObserver,可视区外不加载

3.3.2 网络请求优化

策略实施位置说明
请求去重@cmclink/api Axios 拦截器相同 URL + 参数 500ms 内只发一次
请求取消路由切换时 AbortController.abort()避免已离开页面的请求回调执行
数据缓存SWR 模式(stale-while-revalidate)字典/港口等低变频数据优先展示缓存
字典批量主应用统一拉取 → shared-provider 广播N+1 → 1 次请求(已实施)
接口合并列表 + 统计 + 配置信息 → 一个 BFF 接口减少首屏请求数

四、高可用稳定性治理体系

4.1 错误防御三层模型

┌─────────────────────────────────────────────────┐
  Layer 1: 编译时防御(TypeScript strict + ESLint)  
   类型错误、空安全、未处理 Promise 编译期拦截       
├─────────────────────────────────────────────────┤
  Layer 2: 运行时防御(全局拦截 + 边界隔离)          
   onerror / unhandledrejection / Vue errorHandler 
├─────────────────────────────────────────────────┤
  Layer 3: 降级兜底(重试 + 缓存 + 骨架屏)          
   接口重试 / 子应用重试 / 离线缓存 / 白屏恢复      
└─────────────────────────────────────────────────┘

4.1.1 Layer 1 — 编译时防御(已具备)

措施状态说明
TypeScript strict 模式noImplicitAny / strictNullChecks / strictFunctionTypes
ESLint + @antfu/eslint-config统一代码规范,@cmclink/eslint-config 工厂函数
类型化事件通信MicroBridgeEventMap 编译时校验事件名和 payload
API 类型约束@cmclink/api 请求/响应类型定义

4.1.2 Layer 2 — 运行时防御

机制实施位置说明
Vue errorHandlermain.ts 全局注册捕获组件渲染/生命周期错误,上报 + 展示友好提示
window.onerrorwujie-compat-error.ts抑制 wujie 沙箱已知错误,转发真实错误到埋点
unhandledrejectionmain.ts 全局监听捕获未处理的 Promise 异常
Axios 拦截器@cmclink/api 统一错误处理401 自动登出 / 403 权限提示 / 500 友好降级 / 网络超时重试
路由守卫router/permission.ts未登录跳转 / 无权限拦截 / 404 兜底

4.1.3 Layer 3 — 降级兜底

场景降级方案
子应用不可达超时提示 + 重试按钮(已实施:useChildAppLoader 可达性探测 + 重试)
接口超时自动重试 2 次(指数退避)→ 展示缓存数据 → 提示用户手动刷新
白屏检测页面 mount 5s 后检测 document.body.children.length,为 0 则自动刷新
登录态过期Token 刷新机制 → 无感续期 → 刷新失败跳登录页(shared-provider 广播登出)
CDN 故障静态资源加载失败时回退到源站(<script onerror> 备用地址)

4.2 微前端隔离与容错

这是 wujie iframe 沙箱架构的天然优势,需进一步发挥:

能力当前状态增强方向
JS 隔离✅ iframe 天然隔离子应用 JS 错误不影响主壳
CSS 隔离✅ iframe 天然隔离子应用样式不污染主壳
子应用崩溃隔离✅ iframe 崩溃不影响主壳增加崩溃检测 → 自动重载 iframe
子应用超时熔断✅ 已实施加载超时 → 展示降级 UI → 可重试
子应用独立部署✅ 已具备子应用独立构建/部署/回滚,不影响其他应用
灰度发布⏳ 待实施基于用户/租户/比例的子应用版本灰度

4.3 接口高可用策略

海运业务系统后端微服务众多,前端需具备接口层面的容错能力:

策略实施层说明
超时控制Axios timeout: 30000防止无响应请求长时间挂起
自动重试Axios 拦截器GET 请求自动重试 2 次(指数退避 1s → 2s)
请求队列并发控制同时最多 6 个请求,超出排队(避免浏览器限制导致阻塞)
Token 无感刷新401 拦截Token 过期时暂存请求 → 刷新 Token → 重放暂存请求
降级缓存@vueuse/core useStorage字典/配置等数据接口失败时读 localStorage 缓存
健康检查心跳 ping定期检测后端网关可达性,不可达时全局提示

五、可观测性体系

5.1 监控三板斧

┌──────────────────────────────────────────────┐
│              Metrics(指标)                   │
│  FCP / TTI / CLS / 接口耗时 / 错误率          │
├──────────────────────────────────────────────┤
│              Logging(日志)                   │
│  结构化日志 / 操作审计 / 调试日志              │
├──────────────────────────────────────────────┤
│              Tracing(链路追踪)               │
│  全链路 TraceID:前端请求 → 网关 → 微服务       │
└──────────────────────────────────────────────┘

5.1.1 前端性能指标采集

指标采集方式用途
FCP / LCP / CLSPerformanceObserverCore Web Vitals 监控
子应用加载耗时埋点 child_before_load → child_after_mount子应用性能基线
接口耗时分布Axios 拦截器 + performance.now()慢接口定位
页面停留时长visibilitychange + beforeunload用户行为分析
内存使用performance.memory(Chrome)内存泄漏预警
长任务PerformanceObserver (longtask)卡顿定位

5.1.2 错误监控

类型采集方式信息
JS 运行时错误window.onerror + Vue.config.errorHandler错误堆栈 + 组件名 + 用户操作路径
Promise 未捕获unhandledrejection异步操作错误
资源加载失败window.addEventListener('error', ...) 捕获 <script> / <link>CDN / 静态资源故障
接口异常Axios 拦截器状态码 + 请求参数 + 响应体
子应用加载超时useChildAppLoader 埋点超时应用 + 耗时 + 重试次数

5.1.3 全链路 TraceID(前后端协同)

前端请求 → Axios interceptor 注入 X-Trace-ID (uuid)
         → Nginx 转发保留 Header
         → 后端网关提取并传播到微服务链路
         → 日志平台关联 TraceID 查询全链路耗时

实施:在 @cmclink/api 的 Axios 请求拦截器中统一注入:

config.headers['X-Trace-ID'] = `${Date.now()}-${Math.random().toString(36).slice(2, 10)}`

5.2 与已有埋点体系衔接

当前已有的可插拔埋点架构(TelemetryAdapter)完美适配此方案:

阶段Adapter 实现输出
Phase A(当前)ConsoleReporter开发环境 console 日志
Phase BBufferedReporter批量上报 + 本地文件落盘
Phase C(平台就绪)HttpReporter对接公司监控平台 → 看板 + 告警

六、质量保障体系

6.1 测试策略金字塔

                    ┌─────────┐
                    │  E2E    │  ← Playwright:核心链路 6 Case
                    │  测试   │     (子应用加载/切换/路由同步)
                    ├─────────┤
                  ┌─┤ 集成测试 ├─┐  ← Vitest + Vue Test Utils
                  │ │         │ │    组件 + Store + Composable
                  │ └─────────┘ │
                ┌─┤   单元测试   ├─┐  ← Vitest:工具函数/类型守卫
                │ │             │ │    @cmclink/utils / micro-bridge
                │ └─────────────┘ │
              ┌─┤   静态分析       ├─┐  ← TypeScript strict + ESLint
              │ │                 │ │    编译时类型检查
              │ └─────────────────┘ │
              └─────────────────────┘

6.1.1 核心链路 E2E 测试(已有清单,需自动化)

已有 6 个冒烟测试 Case(05-父应用核心链路冒烟测试清单.md),需升级为 Playwright 自动化:

Case测试内容自动化优先级
Case 1子应用首次进入(加载 + 埋点)P0
Case 2根路径自动重放P0
Case 3深链刷新恢复P0
Case 4标签切换路由同步P1
Case 5页面标签关闭与缓存联动P1
Case 6超时与重试降级P0

6.1.2 性能回归测试

方案工具触发时机
Lighthouse CI@lhci/cli每次 PR 合并到 main
Bundle Size Checkbundlesize / turbo outputs每次构建检查产物体积
Performance BudgetVite build.chunkSizeWarningLimit构建时超限告警

6.1.3 构建质量门禁

# CI Pipeline(建议)
steps:
  - pnpm type-check          # TypeScript 类型检查
  - pnpm lint                 # ESLint 代码规范
  - pnpm test:unit            # 单元测试 + 覆盖率
  - turbo build --affected    # 增量构建
  - bundlesize check          # 产物体积检查
  - lighthouse-ci             # 性能评分检查(≥ 90 分)

6.2 发布安全策略

策略说明
子应用独立发布每个子应用独立构建 + 独立部署 + 独立回滚
灰度发布基于租户 ID / 用户角色 / 百分比灰度新版本
版本锁定pnpm-lock.yaml + engines 锁定 Node/pnpm 版本
分支策略feature → develop → release → main,hotfix 走快速通道
回滚机制静态资源回退到上一版本 hash;子应用 URL 版本化

6.3 持续质量度量

度量维度指标采集频率
构建健康构建成功率、构建耗时、产物体积趋势每次 CI
代码健康TypeScript 覆盖率、ESLint 违规数、死代码比例每周
性能健康Lighthouse 评分趋势、FCP/LCP P75每次发布
稳定性健康JS 错误率、接口成功率、白屏率实时(平台就绪后)
依赖健康过期依赖数、安全漏洞数每月

七、分阶段实施路线图

Phase 1(1-2 周):性能基线建立

优先级任务产出
P0接入 Lighthouse CI每次 PR 性能评分 ≥ 90
P0配置 Prod 构建链路优化(manualChunks + Brotli)首屏 JS 减少 40%
P0主壳骨架屏内联FCP ≤ 1s
P1DNS 预解析 + 子应用资源预连接子应用首开减少 ~200ms
P1Bundle Size 检查加入 CI产物体积不退化

Phase 2(2-4 周):大数据量场景优化

优先级任务产出
P0CmcTable 集成虚拟滚动万级数据 55fps
P0大表单分片渲染方案100+ 字段表单 ≤ 400ms
P1服务端分页规范化(游标分页)深翻页无性能劣化
P1请求去重 + SWR 缓存重复请求减少 60%

Phase 3(4-6 周):稳定性加固

优先级任务产出
P0核心链路 Playwright 自动化(6 Case)CI 级别回归保障
P0全局错误监控 + 白屏检测线上问题 < 5min 感知
P1Token 无感刷新机制用户无感知的登录态续期
P1接口自动重试 + 降级缓存网络抖动无感知
P2全链路 TraceID 打通前后端问题 1min 定位

Phase 4(6-8 周):可观测平台对接

优先级任务产出
P1TelemetryAdapter 升级为 HttpReporter实时上报
P1性能看板搭建(FCP/TTI/CLS 趋势)性能退化实时告警
P2业务看板搭建(子应用加载成功率/耗时分布)SLO 达标监控
P2灰度发布机制基于租户/百分比的安全发布

八、架构决策记录(ADR)

ADR-001:为什么选择虚拟滚动而非分页加载

  • 背景:海运订单列表日常操作需要对比多行数据
  • 决策:采用虚拟滚动 + 服务端分页组合
  • 理由:用户可"无限"滚动浏览同时保持 DOM < 500 节点
  • 代价:虚拟滚动组件开发成本略高,但 el-table-v2 已提供成熟方案

ADR-002:为什么不引入 SSR

  • 背景:B 端后台系统,登录后使用,无 SEO 需求
  • 决策:保持 CSR(Client-Side Rendering)
  • 理由:SSR 增加服务端复杂度,B 端场景 ROI 低;预加载 + 骨架屏可满足秒开需求
  • 代价:首屏 FCP 略慢于 SSR,但 TTI 差异不大

ADR-003:为什么不用 Service Worker 缓存

  • 背景:海运业务数据实时性要求高,缓存可能导致数据不一致
  • 决策:暂不引入 SW 缓存策略,仅预留 SW 注册点
  • 理由:B 端用户对数据新鲜度敏感,缓存策略复杂度高
  • 代价:离线场景不可用,但 B 端后台系统离线场景极少

九、风险与缓解

风险影响缓解措施
虚拟滚动与业务逻辑冲突(如行合并/树形表格)部分复杂列表无法使用虚拟滚动提供 virtual prop 开关,复杂场景回退标准表格
性能优化引入新 Bug回归范围大Playwright E2E + Lighthouse CI 双重守卫
埋点数据量过大存储/传输成本采样 + 聚合 + TTL 过期清理
子应用团队推广阻力优化方案落地慢渐进式推广,先在 doc/ibs-manage 验证
后端接口不配合优化前端单方面无法解决慢查询协同后端建立接口 SLA(P95 ≤ 500ms)

十、总结

本方案从性能(秒开)、稳定性(高可用)、可观测性、质量保障四个维度,基于现有微前端架构提出系统化治理策略:

维度核心策略预期收益
秒开性能预加载 + 骨架屏 + 虚拟滚动 + CDN + 分片渲染子应用首开 ≤ 2s,二次切换 ≤ 300ms
高可用稳定性三层错误防御 + 子应用隔离 + 接口容错 + 降级兜底白屏率 ≤ 0.05%,子应用加载成功率 ≥ 99.9%
可观测性性能指标 + 错误监控 + 全链路 TraceID线上问题 5min 内感知,1min 定位
质量保障测试金字塔 + CI 门禁 + 灰度发布 + 持续度量每次发布 0 回归,性能不退化

核心优势:方案充分利用了现有 Wujie + Monorepo + Turborepo 架构的天然能力(iframe 隔离、预加载 exec:true、增量构建),不需要大规模架构迁移,通过渐进式增强即可达成目标。