项目:html5-examples | 版本:v1.1 | 生成日期:2026-10-06 | 依据:源码分析
《06-部署运行》§5 已给出五条 SOP 的速查版;本篇是展开版——每个扩展场景补齐"为什么这样做"的机制依据、易错点与验证清单,并新增两个 06 未覆盖的场景(新增分组、动效/模板数据配置)。所有场景共享同一条底层原则:本项目的新增内容 = 注册数据,而不是写页面。路由、导航、搜索、分类卡大多从清单数据派生,扩展时先问"哪个清单文件是事实源",改它,再补实现,最后只验证"派生 UI 是否全部更新"。每个场景按"步骤 → 机制依据 → 易错点 → 验证"四段展开。
步骤:
topicSlug(当前仅 html5 / css 两主题有量)与 sourceGroup(8 个标准分组之一,文件名即分组值)。不确定归哪个分组时,以内容主题为准而非来源文件——sourceGroup 是治理产物(《04-数据模型》§2.3)。src/content/examples/<分组>.json 数组追加条目。必填:id(全局唯一字符串,数字风格排序靠前)、title、description、category、categorySlug、topicSlug、tags、language、html、sourceFile、difficulty;可选:css、js、sourceGroup(建议显式写)、sourceLine、createdAt/updatedAt、isInteractive、snippets。<!DOCTYPE>/<html>/<head>/<body>——沙箱 buildSrcDoc 负责包全(机制依据:《07-模块详解》§1.1 的拼装管线)。.example-stage 舞台容器包裹;样式若依赖 body 选择器需改写为 .example-stage(body 的样式已被 reset 覆盖,舞台容器才是布局根)。pnpm dev → /examples 按分组筛选找到卡片 → 进 /example/:id 检查预览、三段源码 Tab、设备切换、(若有 snippets)变体切换。易错点:其一,id 永不复用永不变更——它被收藏表、最近浏览、localStorage 覆盖层三处引用(《04-数据模型》§3);其二,js 中不要手写 </script> 字面量(沙箱会转义为 <\/script,但保持源码干净更好);其三,不要手动重排数组——加载后统一排序,手动排序只产生无意义大 diff;其四,tags 复用既有词汇(3-8 个),同义标签会稀释筛选聚合价值;其五,绝对不要向后端 examples 表导入内容(前端已不读该表,《04-数据模型》§6)。
验证清单:卡片出现在列表页且计数正确;搜索 title/description 能命中;详情页预览无白屏;pnpm lint && pnpm typecheck 通过(JSON 受 prettier 门禁管辖)。
步骤:
src/views/css-lab/css/generators/XxxGenerator.vue,组合 BaseGenerator.vue。声明 sections: GeneratorSection[](参数分组,每参数仅 slider/color/select/text/number/toggle 六种控件)与 presets: GeneratorPreset[](一键预设,apply() 回调里改 config);预览区用 #preview 插槽或 previewStyles;codeOutput computed 拼最终 CSS 文本。类型从 ./generatorTypes 导入(不要从 BaseGenerator.vue 导类型——.vue shim 的 TS2614 限制,BaseGenerator 的再导出仅为兼容)。src/views/css-lab/css/generators/routes.ts 的 generatorComponents 加 xxx: defineAsyncComponent(() => import('./XxxGenerator.vue'))。路由 css/gen-:kind 自动承接,无需改路由文件。src/config/css-lab/catalog.ts 的 cssTools 加 ToolItem:id 建议 css-gen-xxx、link 必须用 toolPath('gen-xxx') 派生(TOOL_ROUTE_PATHS 缺键会导致类型错误——这是刻意设计,防止 link 拼错)、categoryId 选八个子分类之一、icon 用 AppIcon ms: 词表、accent 十色之一。cssToolSubgroups 对应子分类的 ids 数组。generators/cardShowcases/ 提供该 kind 的变体数据,卡片示例 Tab 即有内容;生成器组件可暴露 applyCardShowcase(id, item) 支持点卡片回填参数(未暴露时走 SourcePreview 回退渲染,机制见《07-模块详解》§2.3)。机制依据:首页 Bento、侧边栏、搜索、CssGeneratorRouter 的标题反查全部派生自 catalog 与 generatorComponents——登记完成即全链路生效,这正是"清单驱动 UI"的回报。
易错点:TOOL_ROUTE_PATHS 不加键而 catalog 直接写字符串 link 会绕过类型保护(不要这么做);同族生成器(按钮/卡片/文本/进度四族)会被 FAMILY_TABS 聚合子 tab,加入同族需改 CssGeneratorRouter.vue 的 FAMILY_MAP/FAMILY_TABS 并把非主入口成员从 catalog 侧栏移除——新工具默认不要挤进同族,除非语义确属同一交互族;BaseGenerator 的隐藏 header 由 Router 深度选择器 .css-generator-router :deep(.base-generator__header) 关闭,别在组件里重复渲染标题。
验证清单:/css-lab/css/gen-xxx 直达;首页分类卡计数 +1;侧边栏可搜索到;卡片墙/生成器两 Tab 正常;代码浮层复制可用。
步骤:
src/components/examples/demos/<kebab-name>/<Name>Demo.vue,<script setup lang="ts">,自包含(不依赖路由上下文;用全局组件 xy-*/AppIcon 可直接使用;第三方库优先复用 package.json 既有依赖)。src/views/PlaygroundPage.vue 加两处:一行 const XxxDemo = defineAsyncComponent(() => import('@/components/examples/demos/<kebab-name>/<Name>Demo.vue'))(现约 74 个同类登记),以及在演示清单数据结构中登记菜单项。易错点:演示直接渲染在页面内(非 iframe),因此可以访问浏览器 API 与全局组件——这是它与 JSON 示例的本质区别(《01-系统架构》§2.1 的四形态表);但也意味着演示代码的质量标准与站点代码相同(lint/typecheck 全量管辖),不要把它当"示例草稿区";有副作用的演示(定时器、事件监听)务必在 onUnmounted 清理——页面是 KeepAlive 缓存的,切走不卸载、切回复用,泄漏会累积。
这类内容的特点是"数据 + 演示组件"成对出现,注册位置各不相同:
| 内容类型 | 数据注册处 | 消费方 | 关键契约 |
|---|---|---|---|
| 动效库条目 | src/config/effects/{animate-css,hover,motion-path,gsap}.ts | 统一入口 src/config/effects/index.ts 聚合为 ALL_EFFECTS/ALL_CATEGORIES/TECH_META/categoryById/totalEffects | EffectItem 必含 tech/categoryId/name/nameCn/desc/tags;页面只 import 统一入口(文件头注释明示),不要直接 import 板块文件;新增条目后 Drawer 详情组件按 tech 分发渲染 |
| Tailwind 本地模板 | src/config/tailwindTemplates.ts + TAILWIND_CATEGORY_META | TailwindExamplesPage.vue | 本地五分类(components/sections/pages/dashboards/navsites);sites 分类走后端 /api/static-sites + public/static-sites/{id}/ 实体文件——新增静态站点是"目录 + 数据库记录"两步(遵循 AGENTS.md 8.8 的六条教训:绝对路径、缩略图记录生成方式、保留 source_directory 溯源等) |
| SVG 课程章节 | src/config/svgLessons*.ts | SvgLessonsPage.vue + views/svg-lessons/ 章节组件 | 章节数据与演示组件成对新增 |
易错点:动效条目的 tech 值必须在 TECH_META 四键之一,否则 Drawer 分发落到空分支;effects 板块合并过四条旧路由(/animate-css 等 redirect 到 /effects?tech=),新增板块(而非条目)时需要同步路由 redirect 与 useHeaderNav。
步骤(对照既有模块的固定结构,《03-接口设计》§5 的五步清单的机制展开):
server/src/modules/<name>/entities/<name>.entity.ts:TypeORM 实体。命名惯例:实体类 PascalCase 单数、表名 snake_case 复数、列名 snake_case 且用 @Column({ name: ... }) 显式映射;主键选型——业务实体(要对齐前端字符串 id)用 varchar,关系实体用 uuid 自增;时间列一律 @CreateDateColumn/@UpdateDateColumn。<name>.service.ts:业务逻辑;列表分页统一走 clampPage/clampLimit(server/src/common/utils/pagination.util.ts)——"列表接口永远给出可用结果"的语义由它保证。<name>.controller.ts:@Controller('api/<name>');公开端点显式标 @Public()(默认拒绝漏斗的"显式放行",审查安全性时数 @Public 个数即可);写端点叠加 @UseGuards(AdminGuard);强制登录域用 RequireAuthGuard;认证类入口再加 RateLimitGuard。<name>.module.ts:TypeOrmModule.forFeature([Entity]);模块注册进 app.module.ts,实体同步登记 entities 数组(漏登记会得到 "Repository not found" 运行时错误)。cd server && pnpm migration:generate -- -n AddXxx && pnpm migration:run(data-source.ts 提供连接;DB_SYNC 保持关闭)。src/api/<name>.ts 用 get/post/patch/del 写"函数即端点"的薄封装(一个函数一个端点、路径参数 encodeURIComponent、类型与 src/types/** 同构),并在 src/api/index.ts 桶导出——注意现状:canvas-examples/ui-elements/visual-demos 三个模块不在桶里,页面直接从模块文件 import,新模块应回归桶导出惯例(《13-技术债务》第 11 条)。易错点:DTO 必须用 class-validator 装饰器声明每个字段——全局 ValidationPipe{ whitelist, forbidNonWhitelisted } 会把未声明字段直接 400,这不是 bug 而是 DTO 即契约的设计;公开/鉴权的选择宁严勿松,事后放宽容易、收紧困难;写端点忘挂 AdminGuard 在宽容模式全局守卫下会匿名可写(AdminGuard 文件头注释专门警告过这一点)。
验证清单:curl 未带凭证访问公开端点 200 / 受保护端点 401(宽容模式:无凭证 401 仅对 RequireAuthGuard/业务自行判断生效,需按端点语义验);非 admin 调写端点 403;DTO 多余字段 400 且 message 为数组;迁移 up/down 幂等。
750 个示例(历史迁移基线 1057 个)装在 8 个分组文件里,若内容规模再长出新的主题维度,可新增分组:
src/content/examples/<new-group>.json(CodeExample[] 裸数组)。src/api/examples.ts:GroupKey 联合类型加一项 + groupLoaders 表加一行动态 import——两处缺一不可,漏登不会报错但内容永远不可见(TypeScript 会在 Record<GroupKey, ...> 上报缺键,因此 typecheck 能兜住)。categories 表补对应 sourceGroup 的子分类数据(GET /api/categories/subcategories/:sourceGroup 数据源)。易错点:分组文件名即 sourceGroup 值,JSON 内条目的 sourceGroup 字段应与文件名一致(过滤按字段匹配,文件名只是打包单元);体积控制——单分组建议不超过现有最大分组 css-layout 的 3.2MB 量级,否则首次加载该分组显著变慢(结合《10-性能设计》§1 的按需分组优化一起评估)。
无论哪个场景,收尾三件事相同:跑 pnpm format && pnpm lint && pnpm typecheck(三道本地门禁)→ 按 AGENTS.md 15.3 的五处回写清单检查是否需要同步文档(README、产品需求文档、useHeaderNav.ts 导航、SiteFooter.vue 页脚、HeaderSearchModal.vue 搜索索引——板块级扩展才涉及,条目级扩展通常不涉及)→ 提交信息用 Conventional Commits(如 feat(data): 新增 xxx 示例 / feat(css-lab): 新增 xxx 生成器)。若扩展涉及新依赖,先到 vite.config.ts 的 manualChunks 决策:它属于哪个既有 vendor 组、还是需要新开一组(《10-性能设计》§3.1),并确认 optimizeDeps 是否需要补入口——gsap 的教训是"缺任一子路径都会触发跨实例污染"。
本篇完。下一篇:12-FAQ与故障排查.md