Files
zhangxiang a2ffd6f27b test(acceptance): complete release acceptance testing — conditional pass
All 7 phases of release acceptance testing completed:
- Static quality gates: build, type-check, lint, unit-coverage all passed
- Regression: 356 E2E passed (Chromium core features), CMS workflow, user journeys
- Visual regression: 84/84 passed across 5 browser/device projects (baselines updated)
- Mobile: 173 passed, FCP 68ms / LCP 280ms
- Lighthouse: 7 pages, 4 categories ≥ 0.9, CWV compliant
- Load test: 200 concurrent, p95=7.26ms, 0.28% error rate
- Stress test: 300 concurrent, p95=3.95ms, 0% error rate
- Accessibility: contrast 7/7, headings 10/10, a11y 66/66
- Security: 2 moderate vulnerabilities (accepted risk)
- docs/lessons-learned.md: added 3 new entries (5.7-5.9)

Conclusion: conditional pass — Firefox (127 failed) and mobile (37 failed)
compatibility issues documented as known defects.
2026-08-13 07:11:12 +08:00

13 KiB
Raw Permalink Blame History

Lessons Learned(项目经验教训)

记录跨任务、跨阶段的工程经验教训,避免重复踩坑。按类别组织,定期更新。

目录


1. 技术选型与依赖

1.1 外部字体服务导致白屏

  • 问题:依赖 Google Fonts 等外部字体服务,网络加载失败时页面白屏。
  • 根因:字体加载阻塞首次渲染(FOUT/FOIT 未妥善处理)。
  • 方案:所有字体使用本地文件(src/app/fonts/),禁止外部字体 CDN。
  • 来源2026-03-04 部署事故,project_memory.md Hard Constraints。

1.2 React 19 + Next.js 16 HMR 兼容性

  • 问题:开发模式下 HMR 报错 module factory is not available,需要频繁清除缓存。
  • 根因React 19 与 Next.js 16 的 HMR 模块缓存机制不兼容。
  • 方案:禁用 optimizeCss 实验性功能;若持续影响开发,改用生产模式(npm run build && npm run start)。
  • 来源docs/HMR-ERROR-SOLUTIONS.md

1.3 webpackBuildWorker 导致静态资源 404

  • 问题:启用 experimental.webpackBuildWorker: true 后,客户端静态资源 404。
  • 根因webpack 构建工作线程与 Next.js 静态导出不兼容。
  • 方案:强制禁用该功能,在 next.config.ts 中不启用或显式设为 false
  • 来源project_memory.md Hard Constraints。

2. 构建与部署

2.1 直接复制参考网站代码

  • 问题:从参考网站直接复制 HTML/CSS/JS 到项目中,导致样式冲突和技术债务。
  • 根因:参考网站的样式体系(Bootstrap/其他框架)与项目 Tailwind 体系冲突。
  • 方案:仅参考设计理念和布局,所有代码手动基于 Tailwind 实现。
  • 来源project_memory.md Lessons Learned。

2.2 静态导出限制

  • 问题output: 'export' 模式下,next/image 需要 unoptimized,某些 API 路由不可用。
  • 根因Next.js 静态导出对 Server Components 和 API Routes 的限制。
  • 方案:移除 output: 'export',改用 standalone 或混合模式;图片使用 unoptimized<img> 标签。
  • 来源CLAUDE.md Build Output 章节。

3. 样式与设计

3.1 光晕效果遮挡内容

  • 问题Case Studies 区域卡片悬停光晕(halo effect)尺寸过大,遮挡文字。
  • 根因:光晕半径 256px + 不透明度 100%,超出卡片边界。
  • 方案:尺寸降至 192px,不透明度降至 0.08(降低 92%)。
  • 来源project_memory.md Lessons Learned;后续推广为全局规则:所有光晕效果不透明度降低 30-50%。

3.2 未使用的大文件残留

  • 问题:项目中残留 4.2MB 的 AoyagiReisho 书法字体文件,增加页面加载时间。
  • 根因:早期设计尝试引入书法字体,切换方案后未清理。
  • 方案:定期检查 public/fonts/src/app/fonts/ 中未使用的字体文件,及时删除。
  • 来源project_memory.md Lessons Learned。

3.3 品牌红使用过度

  • 问题:品牌红色(#C41E3A)在页面中占比超过 10%,视觉冲击过强。
  • 根因:缺乏品牌色使用规范约束。
  • 方案:制定品牌红贯穿规则——每页至少 3 处触达点,但面积 ≤ 10%;禁止作为段落文字色、大面积背景色。
  • 来源CONTEXT.md 朱砂点睛章节。

4. 性能优化

4.1 高并发下内存溢出

  • 问题:200 VUs 并发时服务崩溃,单实例无法处理高并发请求。
  • 根因:缺乏负载均衡、缓存策略和资源限制。
  • 方案:多实例部署(Docker Compose 3 实例)+ PM2 进程管理 + Nginx 负载均衡。
  • 来源docs/PERFORMANCE_OPTIMIZATION.md

5. 测试

5.1 测试框架冗余

  • 问题:项目存在三个独立的测试框架(e2e/, e2e-tests/, test-framework/),维护成本高。
  • 根因:早期多次试验不同测试方案,未及时清理废弃框架。
  • 方案:统一到 Playwright + Jest 体系,废弃 Python Playwright 和独立测试框架。
  • 来源docs/OPTIMIZATION_REPORT.md

5.2 测试覆盖率门槛

  • 问题:早期测试覆盖率低(Lines 29%),无法有效保障质量。
  • 根因:缺乏测试文化,工具函数和 hooks 未覆盖。
  • 方案:设置覆盖率门槛(branches 35%, functions/lines/statements 45%),分阶段提升至 80%;后续通过 TDD 流程提升至 85%+。
  • 来源docs/test-coverage-improvement-plan.mdCLAUDE.md 测试命令。

5.3 E2E 断言文本必须与渲染文本保持一致

  • 问题e2e/nav-dropdown.spec.ts 中 hover 测试失败,断言查找 睿新ERP管理系统行业方案,但实际渲染为 ERP 管理系统行业解决方案
  • 根因:UI 文案迭代后,E2E 测试的文本断言未同步更新,导致测试假阴性(功能正常但测试报错)。
  • 方案
    1. 将 UI 文案断言改为稳定标识(如 data-testid、角色/aria 属性)或语义化选择器,减少对文案的强依赖。
    2. 若必须使用文本,在修改文案时同步搜索并更新 e2e/ 中的对应断言。
    3. 建立「文案变更检查清单」,将 E2E 文本断言纳入审查范围。
  • 来源2026-07-26 dogfood 后续验证,e2e/nav-dropdown.spec.ts 修复。

5.4 视觉回归基线需在 UI 变更后主动更新

  • 问题:dogfood 修复后(列表卡片链接、案例筛选、联系表单反馈、新闻占位图等),视觉回归测试大面积失败。
  • 根因:上述修复属于有意的 UI/交互变更,导致已有快照与当前渲染不一致;若不更新基线,后续所有构建都会报告假阳性。
  • 方案
    1. 任何涉及视觉/布局的修复完成后,运行 npx playwright test visual-regression.spec.ts --update-snapshots 更新全项目视觉基线。
    2. 更新前通过浏览器截图/人工复核确认差异符合预期,避免将未发现的回归写入基线。
    3. 将快照变更作为独立提交或 PR 变更的一部分,方便评审时比对视觉差异。
  • 来源2026-07-26 更新 105 张视觉回归快照。

5.5 Playwright storageState 路径必须以配置文件为基准

  • 问题:全量 E2E 运行时 firefox/webkit 下所有测试瞬间失败,报错 Error reading storage state from ./e2e/storageState.json: ENOENT
  • 根因e2e/playwright.config.tsstorageState 被写成 ./e2e/storageState.json,而 npm run test 实际在 e2e/ 目录下执行(cd e2e && npx playwright test),导致 Playwright 去查找 e2e/e2e/storageState.json
  • 方案
    1. 配置文件中的相对路径应基于配置文件自身目录,使用 path.resolve(__dirname, 'storageState.json')
    2. 修改 storageState、快照目录、报告目录等路径时,必须同时验证 npm run test 与从项目根目录直接运行 npx playwright test --config=e2e/playwright.config.ts 两种方式。
    3. storageState.json 纳入版本控制并作为 E2E 环境准备检查项。
  • 来源2026-07-27 全量 E2E 回归失败排查,e2e/playwright.config.ts 修复。

5.6 Firefox 中连续全页导航易触发 Playwright locator 超时

  • 问题e2e/p2-functional-e2e.spec.ts 中「Footer 导航链接可点击」在 Firefox 中反复超时,报错 waiting for "http://localhost:3000/" navigation to finish,即使页面已渲染完成。
  • 根因Playwright 的 locator API 会在页面存在未完成的导航时阻塞;Firefox 对 window.location.href 全页导航的处理比 Chromium 更慢,连续两次 page.goto('/') + 点击 StaticLink 触发全页导航后,locator 评估极易进入等待导航的竞态。
  • 方案
    1. 将涉及两次独立全页导航的测试拆分为两个独立 test case,减少单次测试内的导航次数。
    2. 在 Firefox 等容易触发竞态的浏览器中,对必须触发的全页导航链接点击,改用 page.evaluate(() => element.click()) 绕过 locator 的导航状态检查。
    3. 优先使用 data-testid 或语义选择器定位,点击后通过 page.waitForURL() 显式等待目标 URL,而非依赖 locator 的隐式等待。
  • 来源2026-07-27 e2e/p2-functional-e2e.spec.ts Footer 链接测试修复。

5.7 封版阶段依赖升级需独立评估,避免 --force 一次性修复

  • 问题npm audit fix --force 无差别升级依赖到最新版本,导致 @lhci/cli 被降级到使用 git+ssh 拉取 Lighthouse 的古老版本,且 eslint-config-next@16 与当前 eslint@8 不兼容。
  • 根因--force 会执行 major version 升级,引入 breaking change 风险。
  • 方案
    1. 封版阶段不执行 --force 修复,仅执行向后兼容的 npm audit fix
    2. major version 升级应作为独立专项任务,在封版前或上线后安排。
    3. 修复前备份 package-lock.json,并制定回退策略。
  • 来源2026-08-12 封版验收阶段 1-B。

5.8 性能测试脚本必须与 API 接口契约对齐

  • 问题stress-test.js/api/contact 发送 JSON body,但接口期望 formData,且 IP 限流每小时 5 次/IP,导致 66.7% 请求失败。
  • 根因:测试脚本编写时未审查 API 接口的具体实现(request.formData()isRateLimited)。
  • 方案
    1. 新增/修改 API 后同步审查性能测试脚本,确保请求体格式、认证/限流机制与接口契约一致。
    2. 将对依赖外部服务的接口(如 formsubmit.co)的压测排除在压力测试之外。
    3. 重写后的脚本仅对本地静态页面进行 GET 压力测试,避免外部依赖干扰。
  • 来源2026-08-12 封版验收阶段 5stress-test.js 修复。

5.9 测试摘要文件需在迭代中保护,避免被失败运行覆盖

  • 问题:k6 测试运行时预览服务器被停止,后续重新运行写入的摘要文件包含无效数据(0% 通过率),覆盖了之前成功的运行结果。
  • 根因k6 的 handleSummary 输出路径与 --summary-export 路径不一致,且无版本保护机制。
  • 方案
    1. 使用 --summary-export 指定稳定路径(如 tests/performance/),与脚本内 handleSummary 路径统一。
    2. 每次运行前备份前一次摘要文件。
    3. 服务器重启后重新运行性能测试前,确认端口可用性。
  • 来源2026-08-12 封版验收阶段 5,k6 摘要文件保护。

6. 代码组织

6.1 组件版本膨胀

  • 问题:组件目录存在多个版本迭代(detail-v2/, detail-v3/),造成混淆和冗余。
  • 根因:多次重构未清理旧版本,缺乏组件生命周期管理。
  • 方案:重构完成后立即删除旧版本目录;使用 _archive/ 归档历史版本,并排除在 TypeScript 编译之外。
  • 来源CLAUDE.md Archiving Convention。

6.2 文档与代码不同步

  • 问题docs/ 目录中部分文档(如组件指南、API 文档)与实际代码不一致。
  • 根因:文档更新未纳入代码审查流程。
  • 方案:文档变更必须与代码变更在同一 PR 中审查;CONTEXT.md 作为共享语言文档,与领域模型同步更新。
  • 来源AGENTS.md 文档同步要求。

7. App Router 路由行为

7.1 loading.tsx 会吞掉子动态路由的 notFound()

  • 问题/services/[id]/products/[id]/solutions/[id]/news/[slug] 等动态路由对未知 slug 调用 notFound() 后,仍然返回 HTTP 200(软 404)。
  • 根因(marketing)/loading.tsx 在路由组层级创建了 Next.js Loading/Suspense 边界,该边界会捕获子页面抛出的 NEXT_NOT_FOUND 错误,导致 not-found.tsx 无法被渲染,HTTP 状态也保持为 200。
  • 方案:移除 (marketing)/loading.tsx;如需加载状态,改为在页面内部使用 <Suspense> 包裹特定慢加载区块,而不是在路由组层级全局添加 loading.tsx
  • 验证curl -I http://localhost:3000/services/unknown 返回 404 Not Founde2e/p5-edge-cases.spec.ts 404 相关用例全部通过。
  • 来源2026-07-25 dogfood 修复,dogfood-output/report.md

更新记录

日期 更新内容 来源
2026-07-07 初始创建,从 project_memory.md 和 docs/ 中提取 现有文档 + 记忆文件
2026-07-25 增加 App Router loading.tsxnotFound() 交互的经验教训 dogfood 修复
2026-07-26 增加 E2E 文本断言同步、视觉回归基线更新两条经验教训 dogfood 后续验证
2026-07-27 增加 Playwright storageState 路径、Firefox 全页导航竞态两条经验教训 全量 E2E 回归失败排查