只需写一次代码片段,就能把它嵌入任意页面。单独占一行的 <include> 语句会在构建时嵌入另一个文件,就如同它的内容直接写在那里一样——其中的标题会并入页面的目录,文字会被搜索索引,这些内容也会出现在页面的 .md 镜像和 llms-full.txt 中。即使格式化工具把语句折成多行,它依然会被嵌入;而 Blume 无法解析的语句会在其所在行报出 BLUME_INCLUDE_MALFORMED 警告,而不是渲染成一个原始标签。
<include>./_snippets/prerequisites.mdx</include>
路径相对于包含它的文件来解析。以 / 开头的路径从内容根目录解析,因此层级很深的页面无需 ../../.. 就能引用共享片段:
<include>/_snippets/prerequisites.mdx</include>
该语法与 Fumadocs 的 include 语法一致,因此迁移过来的内容无需改动即可使用。
下面就是实际效果——紧接着的这个提示框是从一个共享片段嵌入进来的:
局部片段#
默认情况下,文件名(或文件夹名)以下划线开头的文件不会进入路由、导航、搜索和站点地图——这个约定天然适合用来放共享片段:
docs/
_snippets/
prerequisites.mdx
cli-flags.md
guides/
quickstart.mdx ← <include>../_snippets/prerequisites.mdx</include>
index.mdx
局部片段就是普通的 Markdown 或 MDX 文件。被嵌入时它的 frontmatter 会被剥离(以包含它的页面的 frontmatter 为准),其余内容——提示框、代码围栏、组件、公式——都和直接内联书写时渲染得完全一样。局部片段还可以再包含其他局部片段;循环包含会报错。
局部片段内部的相对图片引用依然有效:它们会以包含它的页面为基准重新解析,因此与片段放在一起编写的 ,无论片段被嵌入到哪里都能正确解析。
在 blume dev 运行期间修改局部片段,会让所有包含它的页面重新加载。
属性#
以属性的形式把值传进局部片段,再在片段里用 {{name}} 读取,语法和变量完全一样。这样一个片段就能服务所有需要不同变体的页面:
Upgrade to the **{{plan}}** plan to use {{feature}}.
<include plan="Enterprise" feature="SSO">
./_snippets/upgrade.mdx
</include>
属性既作用于被包含的文件,也作用于它继续包含的内容,并且优先于同名的站点级变量。除 lang 和 meta 之外,其他每个属性都是一个 prop;属性值是纯文本。
包含代码文件#
如果目标文件不是 .md/.mdx,它会以围栏代码块的形式嵌入,并从扩展名推断语言。用 lang 可以覆盖语言(也可以把 Markdown 文件按源码展示而不嵌入内容),用 meta 可以传入围栏的 meta 字符串,例如标题:
<include>./examples/config.ts</include>
<include meta='title="config.ts"'>./examples/config.ts</include>
<include lang="mdx">./_snippets/prerequisites.mdx</include>
规则与诊断#
包含语句必须独占一行——它们是块级的,不是行内的。围栏代码块内部的语句不会被处理,所以你可以照常讲解这套语法(就像本页所做的)。
目标文件必须位于内容根目录之内:根目录之外的文件会在版本快照和 eject 出来的项目中被悄悄漏掉,因此 blume 会报 BLUME_INCLUDE_OUTSIDE_ROOT 而不嵌入它。目标不存在是 BLUME_INCLUDE_NOT_FOUND,包含成环则是 BLUME_INCLUDE_CYCLE——这三者都会让 blume build 失败(可以加 --no-strict 强行构建),并且会出现在 blume validate 中。
局部片段内部的失效链接会记在片段文件上,而不是记在嵌入它的那些页面上,所以你在片段所在的位置修正它们即可。