Skip to content

feat(site): 预渲染 503 静态课程页修复 SEO 空壳#28

Merged
fancyboi999 merged 2 commits into
mainfrom
feat/lesson-prerender
Jun 12, 2026
Merged

feat(site): 预渲染 503 静态课程页修复 SEO 空壳#28
fancyboi999 merged 2 commits into
mainfrom
feat/lesson-prerender

Conversation

@fancyboi999

Copy link
Copy Markdown
Owner

动机

503 个课程页此前是 lesson.html?path= 单页 + 运行时 fetch 渲染,爬虫拿到的是字节级相同的空壳(实测两课 md5 一致):百度不执行跨域 fetch,Google 对新站渲染预算极少。sitemap 交了 508 个 URL,实际收录约等于只有首页。

方案

  • site/md-render.js(新增):parseMd / inlineFormat / renderCodeBlock 等纯函数簇从 lesson.html 原样抽出,UMD 导出,浏览器与 Node 共用同一份渲染器 → 预渲染产物 = 运行时渲染产物,不存在两套渲染器漂移。
  • build.js 新增 writeLessonPages:构建时把正文烤进 site/lessons/<phase>/<lesson>/index.html,每课独立 title / description / canonical / og:url / JSON-LD,lang=zh-CN;模板相对路径绝对化;上下课导航与客户端 addBottomNav 同构。产物 gitignore(同 sitemap/llms 策略,Vercel buildCommand 生成),503 页共 67MB。
  • lesson.html boot:识别 __PRERENDERED__ 标记 → 跳过 fetch,setTimeout(0) 后 enhanceLesson + fetchQuiz(同步调用会踩 IIFE 底部 var 赋值未执行的坑,已实测踩中并修复)。renderLesson 拆出 enhanceLesson 供两条路径共用。
  • 内链迁移:sidebar / 上下课 / 时间线 / continue / cmdpalette / app.js / prereqs 全部切到 /lessons/ 静态 URL;sitemap、llms.txt 同步。旧 ?path= 入口保留兼容,canonical 指向静态页。
  • vercel.json:补 /lessons/(.*) 缓存头。

已验证(本地 http.server + Chrome 实测)

  • node 渲染 503/503 全成功,--check 课数护栏通过
  • 预渲染页:正文/标题/meta/canonical 正确,两课 HTML md5 不同
  • quiz 在预渲染 DOM 上正常注入(findH2 中文锚点);mermaid 3 图渲染成 SVG;figure 交互组件正常挂载;底部导航正确
  • ?path= 入口照常工作,与预渲染页渲染结果一致
  • 全程控制台零错误;改动 JS 全部 node --check 通过

没验证到

  • Vercel 线上构建(部署后需 curl 验证 /lessons/ 页与 sitemap)
  • progress.js 的课程完成标记在新 URL 下的读写(存储 key 用的是 lessonPath 变量,两种入口一致,理论无影响但没实测点击)
  • catalog/index 页面的课程弹窗链接跳转(机械替换 + 语法校验过,没逐个点击)

- 新增 writeLessonPages:构建时用与浏览器同一份渲染器把正文烤进
  site/lessons/<phase>/<lesson>/index.html,每课独立 title/desc/canonical/JSON-LD
- parseMd 等纯函数簇从 lesson.html 抽至 md-render.js(浏览器与 Node 共用,
  预渲染产物 = 运行时渲染产物)
- lesson.html boot 识别 __PRERENDERED__:跳过 fetch,setTimeout(0) 后直接
  enhanceLesson + fetchQuiz(同步调用会踩 var 赋值未执行的坑)
- 站内全部 lesson 链接(sidebar/上下课/时间线/cmdpalette/app/prereqs)和
  sitemap/llms.txt 切到 /lessons/ 静态 URL;旧 ?path= 入口保留,canonical 指静态页
- 产物 gitignore(同 sitemap/llms 策略,Vercel buildCommand 生成);vercel.json
  补 /lessons/ 缓存头
@vercel

vercel Bot commented Jun 12, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
ai-engineering-from-scratch-zh Ready Ready Preview, Comment Jun 12, 2026 6:47am

- writeLessonPages 三个模板锚点(LOADING_BLOCK / md-render script /
  </head>)前置校验,任一失配即中止构建;__PRERENDERED__ 注入后逐页断言
  (注入丢失的后果是 boot showError 把烤好的正文覆盖成错误页,必须拦死)
- 烤进静态页的 prev/next 导航只指向 zh.md 真实存在的课,避免 /lessons/ 404
- figure 块 data-figure 属性改用 escapeAttr(含双引号会截断属性,
  lesson.html 旧实现同款潜在 bug,共享渲染器一并修正)
- updateLessonSeo 空 path 防御(避免生成 /lessons// 坏 canonical)
@fancyboi999

Copy link
Copy Markdown
Owner Author

双审(Codex + 独立 subagent)已完成,结论:无严重 bug,5 个有效发现已全部落实在 cca900b

  1. ✅ [HIGH] PRERENDERED 注入静默失败 → 模板锚点前置校验 + 逐页断言,已用故意破坏锚点演练 fail-fast(exit=1)
  2. ✅ [HIGH] 替换无守卫 → 并入锚点校验(要求恰好 1 处)
  3. ✅ [MEDIUM] 烤进导航可能指向未生成页 → isReadable 改为「zh.md 真实存在」
  4. ✅ [MEDIUM] figure data-figure 属性 escapeHtml 不转义引号 → escapeAttr(现有 503 课 figure 内容零双引号,属潜在 bug 顺带修)
  5. ✅ [MEDIUM] updateLessonSeo 空 path 边界 → 加防御提前返回

不修(有意决策):

  • [LOW] 绝对化正则不处理 ./ 开头引用——模板中不存在该形态,正则刻意保守避免误伤 JS 拼接串;锚点校验已兜住主要静默失败面
  • isReadable 与客户端 flatLessons 定义在「登记未翻译」课上存在分歧——该数据形态当前不存在,且旧版行为同样是死链,非本 PR 回归

修复后已重新本地验证:build 503/503、--check 通过、浏览器实测正文/quiz/mermaid/figure/控制台零错误。

@fancyboi999 fancyboi999 merged commit fbd98c4 into main Jun 12, 2026
3 checks passed
@fancyboi999 fancyboi999 deleted the feat/lesson-prerender branch June 12, 2026 06:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant