- Fix list-to-detail navigation on product/service/solution/case pages - Fix soft 404 on service detail by removing (marketing)/loading.tsx and using force-dynamic - Fix contact form submission feedback and news placeholder image handling - Unify SSR/client authentication state in auth.ts - Add "新闻动态" to main navigation - Fix Playwright storageState path and Firefox footer link flakiness - Add E2E coverage for nav dropdown, cases filter and auth token parsing - Update visual regression baselines (desktop/tablet/mobile, chromium/webkit/firefox) - Update README, lessons-learned and add REGRESSION_REPORT_2026-07-27.md - Ignore .lighthouseci/ and heading-hierarchy-report.json
190 lines
11 KiB
Markdown
190 lines
11 KiB
Markdown
# Lessons Learned(项目经验教训)
|
||
|
||
> 记录跨任务、跨阶段的工程经验教训,避免重复踩坑。按类别组织,定期更新。
|
||
|
||
## 目录
|
||
|
||
- [1. 技术选型与依赖](#1-技术选型与依赖)
|
||
- [2. 构建与部署](#2-构建与部署)
|
||
- [3. 样式与设计](#3-样式与设计)
|
||
- [4. 性能优化](#4-性能优化)
|
||
- [5. 测试](#5-测试)
|
||
- [6. 代码组织](#6-代码组织)
|
||
|
||
---
|
||
|
||
## 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.md`,`CLAUDE.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.ts` 中 `storageState` 被写成 `./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 链接测试修复。
|
||
|
||
---
|
||
|
||
## 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 Found`;`e2e/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.tsx` 与 `notFound()` 交互的经验教训 | dogfood 修复 |
|
||
| 2026-07-26 | 增加 E2E 文本断言同步、视觉回归基线更新两条经验教训 | dogfood 后续验证 |
|
||
| 2026-07-27 | 增加 Playwright storageState 路径、Firefox 全页导航竞态两条经验教训 | 全量 E2E 回归失败排查 | |