一个微信公众号 Markdown 渲染组件,如何用 SDD 把需求、架构、任务一次写清
你写了一篇技术文章,想发到微信公众号。Markdown 写得好好的,可一粘进公众号编辑器,标题层级、代码高亮、主题配色全没了------因为公众号只认内联 style ,不认 <style> 和外部 CSS。于是你需要一个组件:把 Markdown 渲染成带主题的漂亮排版,再一键复制成"公众号能直接吃"的 HTML。
wx-md1 就是奔着这个目标去的一个 React 组件项目(md-wx,最终要打成 NPM 包)。但今天我们不聊它怎么写代码------因为代码一行都还没写。这个仓库里只有一整套文档:需求、架构、任务拆分、设计指南、AI 开发规则,外加一个可交互的 UI 原型。
这正是 SDD(Spec Driven Development,规范驱动开发)最直观的样子:文档就是第一次创造,代码是交给 AI 的第二次创造。 本文带你顺着这套文档,看清一个组件在"动笔前"应该被想得多清楚。
一、这个项目到底是什么:一句话定位
md-wx 是一个通用的微信公众号 Markdown 渲染组件,以 NPM 包形式发布,别的 React 项目能直接 import 用。它要解决三件事:
- 渲染:接收一段 Markdown 字符串,实时渲染成 HTML 预览。
- 美化:内置 5 套主题(简约白 / 薄荷绿 / 微信暗黑 / 科技蓝 / 复古橙),代码块做成 macOS 窗口风格,整体走玻璃拟态。
- 复制:一键把预览内容转成"公众号兼容"的 HTML 复制到剪贴板------这是技术含量最高、也最容易踩坑的一环。
它的使用形态是一个带"设置面板"的预览区:顶部固定一组图标按钮(切主题、切手机/桌面视图、复制),下面是白色背景的预览容器。
二、SDD 在这里怎么落地:文档即第一次创造
回看 SDD 的核心心法------优秀的工作都经历"两次创造":先在脑中/文档里把事情设计一遍(第一次创造,心智创造),再动手做出来(第二次创造,物理创造)。vibe coding 崩盘,就是因为直接跳过了第一次创造。
wx-md1 把这套心法落成了四类文档 + 一套规则,彼此有清晰的先后依赖:
这条链的关键不是"多写文档",而是每份文档回答不同的问题、给不同的人看:
proposal.md回答"做什么、做到什么程度"------给产品经理/自己看,定义功能边界。design.md回答"用什么技术、怎么搭"------给架构师/AI 看,锁定选型。design_guide.md回答"长什么样、什么手感"------给设计师/AI 看,锁定视觉。tasks.md回答"先干哪件、后干哪件"------给执行者(AI)看,是可验收的施工蓝图。project_rules.md回答"AI 能做什么、不能做什么"------给 AI 看的行为约束。
心智模型 :把 AI 当新员工。你不会甩一句"做个用户系统"就等交付,而是先给需求、给架构、给排期、给规矩,再让它按任务单一步步来。wx-md1 的文档集,就是这份"新员工 onboarding 材料"。
三、需求文档:五个核心功能,逐个钉死边界
proposal.md 没有堆形容词,而是把功能拆成可验证的条目。重点看五个:
- 预览组件:接收外部传入的 Markdown 字符串,内容变化就实时重渲染。
- 主题切换:内置 ≥5 套主题,默认用第一套;切换时标题竖线颜色、文字颜色、字体联动变化,带平滑过渡。文档甚至把每套主题的 H2~H6 竖线色、文字色、字体都列成了表(如复古橙用衬线体,科技蓝用蓝色系)。
- 代码块样式 :模仿 macOS 窗口,带"红黄绿"三色装饰点,语法高亮默认
github-dark。 - 响应式:提供"手机 / 桌面"两种视图,默认手机视图,方便创作者检查不同端效果。
- 一键复制 :把渲染好的内容转成公众号兼容的 HTML(所有样式以内联形式存在)复制到剪贴板。
还有两块容易被忽略但很关键的可配置性设计:设置区域可见性 (showSettings 控制是否显示顶部栏)、设置功能导出(把主题切换/视图切换函数暴露出去,让使用方自己排布按钮)。这说明作者从一开始就把"组件"当产品想,而不是当玩具。
四、核心机制:为什么"复制到公众号"必须先内联样式
这是整个项目最容易踩、也最该讲透的一个坑。我们先看反例。
踩坑现场 :如果你直接把预览区的 innerHTML 复制到公众号,样式会"消失"。原因是公众号编辑器只接受写在元素 style 属性里的内联样式,它会丢弃 <style> 标签和外部样式表 。你精心写的 .title { color: blue } 在编辑器里根本不生效。
解决方案 :复制前,用 juice 库把 CSS 规则"压"进每个 HTML 元素的 style 属性里。流程如下:
机制心智模型 :juice 做的事就像"把一份班级点名册,逐个学生抄到他们自己胸牌上"------原来样式表是集中管理的(点名册),公众号不认集中管理,只认每人身上挂牌的(内联)。juice 就是那个抄牌的人:.title { color: blue } → <h1 class="title" style="color: blue;">。
两个细节值得记:
- 代码块高亮(
react-syntax-highlighter)本来生成的就是带style的<span>,所以这部分微信能直接认,无需 juice 再处理------这是选型时就考虑到的好处。 - 复制最终走
navigator.clipboard.writeText,要处理成功/失败的反馈。
这一节是整篇文章的"题眼":组件叫"渲染组件",真正的难点不在渲染,而在让渲染结果能被公众号消费。需求文档第 5 条和架构文档第四节,都是围着这个机制转的。
五、技术架构:每个选型都有"为什么"
design.md 的选型不是列清单,而是每条都给了理由------这正是 SDD 文档该有的质量(给 AI 看,AI 才会照做):
| 需求点 | 选型 | 理由(机制) |
|---|---|---|
| 解析 Markdown | react-markdown + remark-gfm |
安全、可定制,GFM 支持表格/删除线 |
| 代码高亮 | react-syntax-highlighter(github-dark) |
生成内联 style,天然适配公众号 |
| 微信内联样式 | juice |
把 CSS 转内联,粘贴不丢样式 |
| 局部样式作用域 | CSS Modules + PostCSS | 避免全局污染,自动加前缀 |
| 极速构建 | Vite | 冷启动快、HMR 好,Rollup 产出优化包 |
| 组件化 UI | React 18+ Hooks | 生态全,Hooks 管理状态/副作用方便 |
目录结构也锁死了:src/components/ 下三个核心组件 Previewer / SettingsPanel / CodeBlock,样式走 styles/themes/ 的 CSS Modules,工具函数进 utils/,自定义 Hook 进 hooks/。注意 :这些目录目前只是规划,仓库里还没有 src/------它们是 tasks.md 要指挥 AI 创建的。
编码规范同样明确:Prettier 强制格式化、ESLint 配 eslint-plugin-react 与 react-hooks 插件、组件 PascalCase、函数变量 camelCase、复杂逻辑必须写注释。
六、任务拆分:九阶段渐进式施工蓝图
tasks.md 是给 AI 的"排期表",原则是渐进式、模块化、优先级、可测试------每个阶段结束都有"可见成果"。九阶段:
每一阶段又拆成带优先级 (🔴最高 / 🟡高 / 🟠中)和验收标准的具体任务。比如"阶段 6.2 微信公众号样式兼容"是 🔴 最高优先级------因为它对应第四节那个核心机制;任务里明确写了"集成 juice / 实现 CSS 转内联 / 获取当前主题 CSS 字符串 / 测试在微信编辑器中的显示效果"。
为什么不能给 AI 一个大任务?文档里给的理由很实在:① 上下文限制,过程一长 AI 会忘早期细节;② 一次性处理越多,对每处细节的关注度越低。所以"像带新员工一样拆小任务",是 SDD 执行层的硬纪律。
七、设计指南:玻璃拟态与 8px 网格
design_guide.md 把"好看"量化成了可执行的规范,而不是"你看着办":
- 设计语言 :玻璃拟态(Glassmorphism)------渐变背景 +
backdrop-filter: blur毛玻璃。 - 间距系统:基于 8px 网格(8/16/24/32/40),圆角分 12/16/20/28 四档。
- 缓动函数 :统一
cubic-bezier(0.4, 0, 0.2, 1),微交互 0.150.2s、标准过渡 0.30.4s。 - 设置面板 :
sticky顶部固定,rgba(255,255,255,0.15) + blur(25px),按钮悬停translateY(-2px) + scale(1.02)。 - 预览容器 :圆角 28px、内边距 40px、阴影
0 16px 60px,内容区始终白底#ffffff。 - 代码块 :macOS 窗口风,三色点红
#ff5f56/ 黄#ffbd2e/ 绿#27ca3f,内容区#1a1a1a、等宽字体、行高 1.6。 - 5 套主题配色:极简白 / 樱花粉 / 森林绿 / 海洋蓝 / 日落橙,各配字体族与主色调。
这些数字不是装饰,是 AI 写 CSS 时直接照抄的"图纸"。
八、AI 开发规则:把 AI 锁在轨道里
.trae/rules/project_rules.md 是整套文档里最"管家"的一份,回答"怎么让 AI 像团队成员一样守规矩,而不是自由发挥"。要点:
- 技术栈锁死:React 18+ / Vite / CSS Modules+PostCSS / react-markdown+remark-gfm / react-syntax-highlighter / juice,禁止擅自替换。
- 目录锁死:不许乱建顶级目录。
- 组件开发指南 :单一职责(Previewer 只渲染、SettingsPanel 只管交互);布尔 Prop 以
is/has/should开头;跨组件状态用状态提升或 Context;可复用逻辑封 Hook。 - NPM 规范 :
react/react-dom必须放peerDependencies,库模式打包排除它们,files只发dist。 - AI 任务执行规范(最关键) :
- 严格按
tasks.md的任务范围执行,单一任务原则------一次只做一个明确任务,做完等确认再下一步; - 禁止自动扩展------不得自己从架构文档衍生新任务;
- 每任务完成对照验收标准自检,用
finish总结并等用户确认。
- 严格按
机制心智模型:规则文件就是 AI 的"岗位说明书 + 权限边界"。前面所有文档是"做什么",这份是"怎么做、做到哪停"。没有它,AI 容易做着做着就超范围、自作主张,把可控的工程变回 vibe coding。
九、这个仓库本身就是 SDD 的成品
回到开头那句话:wx-md1 现在没有一行业务代码 ,但它已经是一个"完成度很高"的项目------因为第一次创造(文档)做扎实了。UI 原型 ui-design.html 也已生成(标题"Markdown → 微信公众号渲染组件 · UI 原型",含主题切换、复制、预览等交互),可以拿去给人或给自己看 MVP 效果。
接下来要做的,就是挑一个任务(比如"阶段 1.1 项目初始化"),把 tasks.md 的验收标准交给 AI,让它产出 src/ 代码------这就是第二次创造。文档和代码之间,靠 git 跟踪保持一致;新需求来了,先改文档、再改代码,循环往复。
小结
| 概念 | 一句话定义 | 关键落点 |
|---|---|---|
| SDD | 规范驱动开发,文档即第一次创造 | wx-md1 文档集就是成品 |
| md-wx | 微信公众号 Markdown 渲染 React 组件(NPM 包) | 渲染 + 5 主题 + 一键复制 |
| 核心机制 | 公众号只认内联 style → 用 juice 转内联 | 复制按钮 → ref 取 HTML → juice → 剪贴板 |
| 文档四件套 | proposal / design / design_guide / tasks | 分别回答 做什么/怎么搭/长啥样/先干啥 |
| AI 规则 | 技术栈锁死 + 单一任务 + 不扩范围 | project_rules.md |
| 两次创造 | 文档(心智)→ 代码(物理) | 本仓库停在第一次创造 |
易错点 · 待补充学习
- 公众号样式坑 :直接复制
innerHTML会丢样式,必须juice转内联------这是需求第 5 条和架构第四节共同指向的核心,别本末倒置只做渲染不做复制兼容。 - 代码尚未实现(事实边界) :本仓库目前只有文档与 UI 原型,没有
src/,组件、Hook、样式文件都是tasks.md规划的"第二次创造"目标,尚未落地。本文描述的是"设计意图",不是"已实现行为"。 - 技术栈为规划选型 :React 18+ / Vite / juice 等是在
design.md中预定的方案,待 AI 按 tasks 落地验证;若实际踩坑(如 juice 对某 CSS 特性不支持),可能要回写文档。 - UI 原型非生产代码 :
ui-design.html是视觉原型(MVP 演示用),不等同于最终组件实现。 - SDD 框架 Spec-kit:概念笔记里点名的 SDD 框架,本仓库未使用具体框架,而是手写四份文档 + trae 规则,属于"轻量 SDD"。
自测清单
- 能说清 md-wx 要解决哪三件事(渲染 / 美化 / 复制兼容公众号)。
- 能解释为什么"复制到公众号"必须先内联样式,以及 juice 在这条链里的位置。
- 能列出 SDD 四份文档各自回答的问题,以及它们和"两次创造"的对应关系。
- 能说明 tasks.md 为什么要把大任务拆小(上下文限制 + 细节关注度下降)。
- 能讲出 project_rules.md 里"单一任务原则 / 禁止自动扩展 / finish 等确认"是在约束 AI 的什么行为。
- 能判断:拿到一个只有文档、没有代码的仓库时,它处于 SDD 的哪一次创造阶段。