ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

VitePress 区域标记(Region)机制深度解析:从 foo.md 到代码片段导入与文件包含

VitePress 区域标记(Region)机制深度解析:从 foo.md 到代码片段导入与文件包含 VitePress 区域标记Region机制深度解析从 foo.md 到代码片段导入与文件包含【免费下载链接】vitepressVite Vue powered static site generator.项目地址: https://gitcode.com/gh_mirrors/vi/vitepress导读在 VitePress 中!-- #region --/!-- #endregion --注释标记region markers是一套被多处复用的底层机制它既是代码片段导入语法按需截取源文件内容的依据也是Markdown 文件包含!-- include: --语法选择文件局部内容的入口。本文以仓库测试夹具 foo.md 为贯穿全文的最小示例结合 regions.ts、snippet.ts、include.ts 的源码实现与 markdown-extensions.test.ts 的端到端测试完整讲解区域标记的语法、匹配原理、组合用法与配置项读完即可在自己的文档项目中熟练使用按区域导入/包含这一高价值能力。一、认识 foo.md区域标记的最小载体测试夹具文件 foo.md 全文只有 11 行却完整承载了区域标记的全部要素# Foo This is before region !-- #region snippet -- ## Region This is a region !-- #endregion snippet -- This is after region从中可以拆解出三个要点标记注释!-- #region snippet --与!-- #endregion snippet --是一对开启/关闭标记二者之间的内容第 68 行构成一个名为snippet的区域区域边界不含标记行本身findRegions返回的区间只覆盖标记之间的正文## Region与This is a region才是实际被提取的内容区域之外的内容保留This is before region与This is after region不会被自动删除是否出现取决于使用者是否选择了该区域。这份文件之所以被放在__tests__/e2e/markdown-extensions/目录下是因为它同时是代码片段导入与Markdown 文件包含两条功能链路的共享测试素材——这正是区域机制一处定义、多处复用设计意图的直接体现。二、区域标记语法不止 HTML 注释一种风格虽然 foo.md 用的是 HTML 注释风格!-- #region --但区域机制的设计远不止于此。在 regions.ts 中markers数组定义了一整套跨语言、跨注释语法的匹配规则注释风格匹配的语言场景开启标记正则节选行注释// #regionjs、ts、go、rust、java、带注释的 json、sql、bat^\s*(?:\/\/\s*#?\|...)#region\b哈希注释#regionC#、coffeescript、python、yaml、shell、Pascal^\s*#\s*(?:#\s*\|pragma\s)?[rR]egion\bHTML 注释!-- #region --markdown、vue 模板^\s*!--\s*#?region\b\s*(.*?)\s*--CSS 块注释/* #region */css、less、scss^\s*\/\*\s*#region\b\s*(.*?)\s*\*\/F# 块注释(* #region *)f#^\s*\(\*\s*#region\b\s*(.*?)\s*\*\)JSON 键注释带注释的 json 文件^\s*\/{2,}\s*#region\b\s*(.*?):\s*源码注释中特别说明了设计取舍这些规则是 VS Code 各语言folding.markers定义的超集——由于 markdown 渲染器无法预知被导入文件的编程语言因此对每个文件都会尝试全部风格做到编辑器能折叠的都能被提取。同时区域名若带引号如 Visual Basic 的#Region Name会被自动剥离引号见 unquote 函数以便用裸名字引用。区域名的合法字符在 snippet.ts 中区域名通过/#([\w.-])$/提取即允许字母、数字、下划线、连字符与点号。由于区域名取自路径末尾若文件本身文件名含有#必须显式写出区域例如写 ./my#file.js#region而非 ./my#file.js。三、区域检测源码原理findRegions 的匹配与嵌套处理findRegions 是区域提取的核心函数输入文件的行数组与区域名输出所有同名区域的{ start, end, marker }区间。从源码可以梳理出四条关键行为廉价预过滤每行先经maybeMarkerRE /region/i快速筛选L13只有可能含标记的行才进入完整正则匹配避免在长文件上做昂贵的逐风格匹配同名多区域合并文件里可以存在多个同名区域它们会被全部收集并按文档顺序返回——这在 Vue SFC 中尤其有用例如模板里用!-- #region --、script里用// #region声明同名区域导入时会拼接在一起。端到端测试中也验证了多区域拼接这一行为具名结束标记按名字闭合一个带名字的#endregion name会关闭任意注释风格中最近开启的同名区域而无名字的#endregion只关闭同一种注释风格内最近开启的区域避免跨语言误关嵌套区域取最外层注释说明当多个同名区域嵌套时返回最外层跨度。紧随其后的两个辅助函数也值得了解stripRegionMarkers删除标记行本身默认移除所有风格也可通过参数限定只移除特定风格的标记dedent计算所有行空格/制表符逐个字符计数的最小公共缩进并统一去除保证从缩进较深的位置提取出的代码片段顶格输出、语法高亮不受干扰。四、代码片段导入语法如何消费区域foo.md 在 markdown-extensions/index.md 中被多次以语法引用这里依次展开。4.1 基础导入整文件 /markdown-extensions/foo.md对应 source root默认是项目根配置了srcDir后则为该目录也支持相对路径 ../snippets/snippet.js解析逻辑位于 parseSnippetPath路径后缀从右往左剥离——先是可选标题[title]再是可选的{lines lang attrs}元信息块然后是#region剩余部分才是真实文件路径。因此路径本身可以包含空格和点号。文件扩展名中只有纯字母数字后缀才会被当作语言推断像main.c、scss.code-snippets这类文件必须显式声明语言。4.2 按区域导入核心场景 /markdown-extensions/foo.md#snippet这是 foo.md 区域标记的直接消费场景导入方通过#snippet指定区域名渲染阶段createSnippetRenderer读取文件 → 调用findRegions定位 → 拼接所有同名区域 → 用dedent去除公共缩进 → 再按配置移除标记行最终生成的内容是## Region This is a region即只含区域正文、不含!-- #region --标记行。4.3 区域 高亮 行号 自定义标题组合用法 /markdown-extensions/foo.md#snippet{1 ts:line-numbers} [snippet with region]花括号内的元信息按序解析parseSnippetMeta1是行高亮规格ts是语言覆盖ts:line-numbers中冒号后的line-numbers作为额外属性原样透传给代码块方括号[snippet with region]是自定义标题。注意属性中不允许出现方括号。其余属性还可用于接入第三方能力例如{ts twoslash}可在配置了shikijs/vitepress-twoslash时启用 twoslash 处理。4.4 失败行为与配置导入不存在的文件或区域默认会抛出构建错误。可在配置中开启容错export default defineConfig({ markdown: { snippet: { silent: true, // 缺失时仅告警并渲染为空而不是抛错 stripRegionMarkers: true // 默认 true移除匹配到的区域标记all移除所有风格标记false全部保留 } } })对应 snippet.ts 的 Options 接口。stripRegionMarkers的默认值true表示只移除与所请求区域同风格的标记因此整文件导入时区域标记会原样保留在输出中。五、Markdown 文件包含!-- include: --的三种内容选择方式除了导入代码片段区域机制还支撑了 Markdown 文件的按需包含。以 index.md 为例5.1 整文件包含!--include: ./foo.md--直接展开整个文件。测试render markdown断言展开后页面出现idfoo的h1证明包含生效。也支持前缀定位 source root!--include: /markdown-extensions/bar.md--。5.2 按区域包含!--include: ./region-include.md#snippet--其中 region-include.md 用同名标记定义了range-region与snippet两个区域。包含时若文件是.md且使用了区域VitePress 会先用 gray-matter 剥离 frontmatter见 include.ts L121-L123再按区域截取。测试断言该用法下渲染出了idregion-snippet的h2。5.3 按行范围包含范围语法格式为{start,end}起止均可省略!--include: ./foo.md{6,8}-- !-- 只取第 68 行 -- !--include: ./foo.md{,8}-- !-- 从头到第 8 行 -- !--include: ./foo.md{6,}-- !-- 从第 6 行到文件尾 --范围解析见 include.ts 的 rangeRE 与越界校验逻辑L147-L156。一个值得注意的细节当范围与区域同时使用时行号以区域内容为基准所以./region-include.md#range-region{3,4}取到的是区域内第 34 行## Range Region Line 2{,2}与{5,}分别对应区域内第 1 行与第 3 行——这一语义差异由测试support markdown region snippet系列用例逐一验证。5.4 按标题锚点包含区域未命中时的回退当#名称没有匹配到任何编辑器风格区域时include.ts 会回退到标题锚点匹配findHeadingSection 解析目标文件的标题树按锚点找到对应标题取其从该标题之后到下一个同级或更高级标题之前的整段内容。例如 header-include.md 定义了多层标题结构!--include: ./header-include.md#header-1-1--便只包含## header 1.1及其子标题header 1.1.1、header 1.1.2。5.5 嵌套包含、循环防护与相对路径重写嵌套包含包含进的内容会递归地继续展开其中的include指令。测试素材 nested-include.md 依次包含 foo.md 与subfolder/inside-subfolder.md而后者又包含subsub/subsub.md再包含subsubsub/subsubsub.md形成三级嵌套链测试断言了各级标题foo-1、inside-sub-folder、sub-sub、sub-sub-sub的渲染顺序。循环防护processIncludes 记录祖先链若待包含路径等于当前文件或在祖先链中则保持原样不展开避免无限递归。相对路径重写默认rebaseRelativeUrls: true被包含文件中的相对图片/链接会以被包含文件自身所在目录为基准重写L261-L283从而保证包含后链接不失效。5.6 include 的失败行为与配置与 snippet 一致markdown: { include: { silent: true, // 缺失/越界时告警并跳过而不是抛错 rebaseRelativeUrls: true // 默认 true重写被包含文件中的相对 URL } }对应 include.ts 的 Options 接口。六、端到端测试如何验证这套机制markdown-extensions.test.ts 是区域机制的权威行为契约几个与 foo.md 直接相关的用例Import Code Snippets / basic断言#basic-code-snippet div code span共 11 行即整文件导入foo.md 共 11 行内容Import Code Snippets / specify region断言 3 行即#snippet区域仅含## Region、空行与This is a regionImport Code Snippets / with other features断言同时具备line-numbers-mode类、首行高亮验证{1 ts:line-numbers} [snippet with region]的完整组合Markdown File Inclusion / support selecting range等系列用例验证{6,8}、{,8}、{6,}三种范围语义Markdown File Inclusion / support markdown region snippet验证区域 范围组合时行号以区域为基准Markdown File Inclusion / render markdown using nested inclusion验证多层嵌套包含的展开顺序。这些用例共同构成了区域机制的回归防线也提示读者任何对区域匹配规则的修改都必须同时保证上述语义不回归。七、在文档项目中应用区域机制的实践建议综合 foo.md、源码与测试整理出可直接落地的实践清单组织零件文件把可复用的内容拆成独立文件用命名区域如snippet、basic-usage、range-region标注关键段落一个文件可定义多个区域供不同页面按需引用代码优先用文档优先用include展示源码片段用 /path#region{...}拼接 Markdown 正文段落用!--include: ./part.md#region--二者共享同一套区域匹配引擎标记风格互相兼容善用组合区域名 行高亮 语言覆盖 行号 标题可以一次写全 /markdown-extensions/foo.md#snippet{1 ts:line-numbers} [snippet with region]开启容错配置内容尚在编写中时可临时设置markdown.snippet.silent与markdown.include.silent为true避免缺失文件阻断整个构建注意行号基准区域与范围并用时行号相对区域内容计算与整文件行号不同这是最容易踩坑的地方利用标题锚点回退不想在零件文件里手写区域标记时直接按标题锚点#my-base-section包含即可规则是从该标题到下一个同级或更高级标题之前。八、总结从一份 11 行的 foo.md 出发可以看到 VitePress 把区域标记这一朴素概念打磨成了覆盖多语言注释风格、支持同名多区域拼接、嵌套匹配与自动缩进消除的完整机制并让它同时服务于代码片段导入与!-- include: --文件包含两条链路。理解 regions.ts 的匹配器设计、snippet.ts 与 include.ts 的参数解析与回退策略再对照 markdown-extensions.test.ts 的行为契约便能在自己的文档项目中精准、安全地使用区域导入与包含能力让单一事实来源single source of truth的文档组织方式真正落地。【免费下载链接】vitepressVite Vue powered static site generator.项目地址: https://gitcode.com/gh_mirrors/vi/vitepress创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表