写技术博客时,我常遇到一个痛点:代码示例和讲解内容分离。代码在文件里,讲解在文章里,改一处要同步两处。
最近我在尝试一种新方式:在 Markdown 里直接嵌入前端组件。
为什么需要这样做¶
传统技术博客的问题:
- 代码示例静态化 — 读者只能看,不能改
- 内容维护成本高 — 示例代码和文章分开,容易不同步
- 交互体验差 — 复杂概念用文字描述不如可视化直观
方案:Markdown + 组件¶
核心思路是把 Markdown 解析成 AST,然后在特定节点插入组件渲染:
from markdown import Markdown
from markdown.extensions import Extension
from markdown.preprocessors import Preprocessor
class ComponentPreprocessor(Preprocessor):
def run(self, lines):
result = []
for line in lines:
# 匹配 <component name="..." props="..." />
if line.strip().startswith("<component"):
result.append(self._render_component(line))
else:
result.append(line)
return result
def _render_component(self, line):
# 解析组件名和属性
# 返回对应的 HTML
pass
实际例子:可交互的代码示例¶
## 异步爬虫示例
下面是一个简单的异步爬虫,你可以修改 URL 试试:
<component name="async-crawler"
default-url="https://example.com"
max-depth="2" />
渲染后变成一个可交互的组件,读者可以直接输入 URL 运行爬虫,看到结果。
另一个例子:数据可视化¶
## 性能对比
<component name="chart"
type="bar"
data='[{"name":"S3","value":100},{"name":"R2","value":0}]'
labels='["存储费用($/GB)","出站流量($/GB)"]' />
渲染出一个柱状图,数据直接写在 Markdown 里,改数据就是改文章。
实现细节¶
组件注册表¶
COMPONENTS = {
"async-crawler": AsyncCrawlerComponent,
"chart": ChartComponent,
"code-runner": CodeRunnerComponent,
}
渲染流程¶
- Markdown 解析成 AST
- 遍历 AST,找到组件节点
- 根据组件名查找注册表
- 渲染组件为 HTML
- 剩余内容用普通 Markdown 渲染
安全性¶
组件渲染在服务器端完成,不会执行用户输入的代码。所有交互逻辑通过前端 JS 实现,和文章内容完全隔离。
适用场景¶
| 场景 | 传统方式 | 组件方式 |
|---|---|---|
| 代码教程 | 贴代码块 | 可运行的代码示例 |
| 数据对比 | 文字描述 | 交互式图表 |
| 算法演示 | GIF 动图 | 可调节参数的可视化 |
| API 文档 | 静态示例 | 可测试的 API 调用 |
局限¶
- 构建时需要渲染组件,静态站点生成会变慢
- 组件代码需要和维护的博客框架兼容
- 搜索引擎无法索引组件内容(但可以用 JSON-LD 补充)
总结¶
在 Markdown 里嵌入组件,本质上是把「写作」和「演示」合二为一。读者不再是被动阅读,而是可以动手尝试。对于技术博客来说,这种体验的提升是值得投入的。