示例内容构建机制
relation-graph 官网的示例详情页不仅面向普通用户,也需要面向搜索引擎和大模型。为了让大模型直接通过示例 URL 读取到示例文章、文件树和源码内容,示例详情页在静态构建阶段会为免费示例注入源码快照。
本文说明 /examples/* 相关页面的路由、源码注入、增量构建和缓存机制。
路由模型
示例详情页使用平台路径作为静态 HTML 的变体维度:
| 路由 | 含义 |
|---|---|
/examples/<exampleId> |
旧入口,按 React 示例渲染,并设置 canonical 到 /examples/<exampleId>/react。 |
/examples/<exampleId>/react |
React 示例详情页。 |
/examples/<exampleId>/vue3 |
Vue3 示例详情页。 |
/examples/<exampleId>/vue2 |
Vue2 示例详情页。 |
/examples/<exampleId>/svelte |
Svelte 示例详情页。 |
/examples/<exampleId>/web-component |
HTML/Web Component 示例详情页。 |
/zh/examples/<exampleId>/... |
中文 locale 对应页面。 |
旧的 ?lang=<platform> query 不再作为静态 HTML 变体依据。客户端遇到旧 URL 时会跳转到对应的平台路径,例如:
/zh/examples/basic-custom-node-and-minimap?lang=vue3
会跳转到:
/zh/examples/basic-custom-node-and-minimap/vue3
这样每个 locale + exampleId + platform 都有独立 HTML,大模型抓取 URL 时不会拿到错误平台的源码。
源码注入边界
构建阶段只为免费示例注入源码快照。免费示例的判断逻辑是:
!example.meta?.vip && !example.meta?.entVip
付费示例仍然会生成 platform 页面,但最终 HTML 中不会包含源码快照。用户点击源码区域时,仍然通过 source-code-v2/* 接口读取源码,由接口完成登录和权限校验。
免费示例的源码快照会在 Nuxt 生成 HTML 后注入到页面底部:
<script id="rg-example-source-snapshot" type="application/json">
...
</script>
这个 JSON 包含:
| 字段 | 说明 |
|---|---|
exampleId |
示例 ID。 |
platform |
平台名称,如 vue3。 |
sourceRoot |
源码所在目录。 |
mainFile |
默认打开的源码文件。 |
filesTree |
完整文件树,文件节点包含完整文本源码。 |
files |
文件路径、大小和 hash 摘要列表。 |
前端源码面板打开时会优先读取这个内嵌快照。命中快照时,点击文件树不会请求源码接口;未命中快照时才回退到接口读取。这意味着:
| 示例类型 | SSR HTML 是否含源码 | 点击文件树是否请求源码接口 |
|---|---|---|
| 免费示例 | 是 | 否,优先使用内嵌快照。 |
| 付费示例 | 否 | 是,由接口做权限校验。 |
| 非首屏 SPA 跳转且未加载目标 HTML | 不一定 | 可能回退接口;平台切换使用完整页面跳转以尽量加载目标 HTML。 |
源码目录
构建脚本直接读取本机固定源码仓库:
| platform | 源码目录 |
|---|---|
react |
../relation-graph-site-react/src/v3-examples/<exampleId> |
vue3 |
../relation-graph-site-vue3/src/v3-examples/<exampleId> |
vue2 |
../relation-graph-site-vue2/src/v3-examples/<exampleId> |
svelte |
../relation-graph-site-svelte/src/v3-examples/<exampleId> |
web-component |
../relation-graph-site-web-component/src/v3-examples/<exampleId> |
免费示例任意平台源码目录缺失、不可读或没有可读取的文本源码时,构建会失败并输出具体 exampleId + platform + file 信息。
构建会跳过以下内容:
| 类型 | 说明 |
|---|---|
| 依赖和构建目录 | node_modules、.nuxt、.output、dist、build、coverage 等。 |
| 锁文件 | package-lock.json、pnpm-lock.yaml、yarn.lock、bun.lockb。 |
| 二进制资源 | 图片、字体、音视频、压缩包、PDF、WASM 等。 |
文本源码文件必须完整注入,不会截断。为了避免意外把超大文本塞进 HTML,单文件和单快照有构建上限;超过上限时构建失败,而不是生成不完整源码。
构建模式
yarn generate:relation-graph 默认使用 changed 模式:
yarn generate:relation-graph
等价于:
RG_EXAMPLES_PRERENDER_MODE=changed yarn generate:relation-graph
支持的模式如下:
| 模式 | 行为 | 使用场景 |
|---|---|---|
full |
重新生成所有示例详情页并刷新缓存。 | 首次启用机制、路由模板变化、缓存失效。 |
changed |
默认模式。根据指纹只生成变化过的示例详情页,其余从缓存恢复。 | 日常构建。 |
skip |
不生成示例详情页,只从缓存恢复。缓存不安全时直接失败。 | 明确确认 /examples/* 无关变更时的快速构建。 |
首次启用或缓存缺失时需要运行:
RG_EXAMPLES_PRERENDER_MODE=full yarn generate:relation-graph
之后日常执行:
yarn generate:relation-graph
指纹规则
changed 模式会为每个示例详情 route 计算 fingerprint。影响 fingerprint 的内容包括:
| 内容 | 影响范围 |
|---|---|
locale |
中文和英文页面分别计算。 |
exampleId 和 platform |
每个平台页面分别计算。 |
| 示例 meta、标题、描述 | 示例目录数据变更会触发重建。 |
| 当前 locale 的示例 markdown | 示例文章变化会触发对应 locale 重建。 |
| 免费示例源码文件路径和内容 hash | 源码变化会触发对应 platform 重建。 |
| 示例详情页模板、源码面板、platform 工具、站点配置 | 这些公共模板变化时,changed 会退化为 full。 |
| 扫描器版本 | 扫描规则升级时会退化为 full。 |
付费示例不会把源码 hash 纳入 fingerprint,因为构建阶段不会读取或注入付费源码。
缓存结构
示例静态构建缓存位于:
.cache/relation-graph-example-prerender/
主要内容:
| 路径 | 说明 |
|---|---|
manifest.json |
记录 route fingerprint、模板 hash、源码快照 hash 和更新时间。 |
routes/ |
缓存每个示例详情 route 目录下的 index.html 和 _payload.json。_payload.json 保存 Nuxt payload,其中包含示例文章等 SSR 数据。 |
assets/ |
缓存示例 HTML 引用到的旧 _nuxt 资源。 |
work/current-plan.json |
当前构建计划。 |
work/source-snapshots/ |
当前构建生成的免费示例源码快照。 |
changed 或 skip 模式会把未变化示例 route 的 index.html 和 _payload.json 从 routes/ 恢复到:
apps/relation-graph-site/.output/public
同时会把缓存中的 _nuxt 资源合并回输出目录。输出目录短期存在多版本 _nuxt 文件是预期行为,因为复用旧 HTML 时必须保留旧 HTML 引用的 hash 资源。
构建流程
yarn generate:relation-graph 实际执行三段流程:
1. ai-tools/example-prerender/prepare.mjs
扫描示例源码,计算 fingerprint,生成 current-plan.json。
2. nuxi generate apps/relation-graph-site
只 prerender 当前计划允许的示例详情路由,以及其它站点页面。
3. ai-tools/example-prerender/finish.mjs
恢复未变化示例页的 HTML 和 payload,注入免费示例源码快照,缓存新 HTML、payload 和 _nuxt 资源,更新 manifest。
Nuxt prerender 的 crawlLinks 仍然开启,但示例详情页会通过 ignore 规则限制:只有构建计划里的无 query route 可以被生成。带 fromGroupId、lang 等 query 的示例详情 URL 不会写入静态 HTML。
常用命令
# 日常构建:默认 changed
yarn generate:relation-graph
# 强制刷新所有示例详情页和缓存
RG_EXAMPLES_PRERENDER_MODE=full yarn generate:relation-graph
# 明确跳过示例详情页生成,仅复用缓存
RG_EXAMPLES_PRERENDER_MODE=skip yarn generate:relation-graph
# 只生成 Nuxt 静态站点,不执行示例缓存流程。仅用于排查问题。
yarn generate:relation-graph:nuxt
故障处理
| 问题 | 处理方式 |
|---|---|
skip 模式失败 |
说明缓存不完整或模板 hash 不匹配,改用 full。 |
| 免费示例源码目录缺失 | 补齐对应平台源码目录,或从示例目录中移除该示例。 |
| 文本源码过大 | 拆分源码文件,或确认是否应作为二进制/生成产物被排除。 |
| 付费示例 HTML 出现源码 | 这是严重问题,应立即检查免费/付费判断和注入脚本。 |
示例文章先显示后变成 Markdown File Not Found |
通常说明对应 route 的 _payload.json 没有被恢复到 .output/public,浏览器 hydration 后回退请求静态站不存在的 /api/example-markdown-content。需要刷新示例缓存。 |
页面引用旧 _nuxt 资源 404 |
确认 .cache/relation-graph-example-prerender/assets/ 已合并到 .output/public/_nuxt。 |