机制完全解析:Front Matter 声明、目录推断与站点分类索引)
Jekyll 分类Categories机制完全解析Front Matter 声明、目录推断与站点分类索引【免费下载链接】jekyll:globe_with_meridians: Jekyll is a blog-aware static site generator in Ruby项目地址: https://gitcode.com/gh_mirrors/je/jekyllJekyll 是 Ruby 编写的博客型静态站点生成器而“分类”categories是组织博客文章、生成归档 URL 的核心机制。本文将围绕仓库测试夹具 test/source/_posts/2009-01-27-categories.markdown 所演示的 front matter 分类声明方式完整讲解 Jekyll 中分类的多种写法、底层解析与合并逻辑、在 URL 与站点数据中的呈现方式并结合 lib/jekyll/document.rb 等源码与测试验证每个结论。读完后你将掌握在 Jekyll 中正确声明、查看与使用分类的全部方法。一、分类在 Jekyll 中的角色分类是 Jekyll 面向博客场景内置的两种元数据之一另一种是标签 tags。它承担三重职责内容归类将文章按主题分组方便读者按类别浏览URL 生成分类名会出现在文章 URL 中如/:categories/:year/:month/:day/:title.html站点索引通过site.categories暴露“分类 → 文章列表”的映射供 Liquid 模板循环输出分类归档页。由于分类直接参与 URL 构造Jekyll 在解析时对它有专门的处理逻辑这也是它不同于普通 front matter 字段的根本原因。二、通过 Front Matter 声明分类两种写法在文章的 YAML front matter 中可以使用categories键声明分类。仓库测试夹具提供了完整写法矩阵写法一字符串空格分隔——见 test/source/_posts/2009-01-27-categories.markdown--- layout: default title: Categories in YAML categories: foo bar baz --- Best *post* evercategories: foo bar baz表示这篇文章同时属于foo、bar、baz三个分类。写法二YAML 数组——见 test/source/_posts/2009-01-27-array-categories.markdown--- layout: default title: Array categories in YAML categories: - foo - bar - baz --- Best *post* ever两种写法语义完全等价该文都属于foo、bar、baz三个分类。数组写法适合分类名本身包含空格或较多、需要换行维护的场景。此外还有两种边界情形被仓库夹具覆盖空值categories:后接空值见 test/source/_posts/2009-01-27-empty-categories.markdown等价于未声明分类完全不声明front matter 中无categories键见 test/source/_posts/2009-01-27-no-category.markdown此时文章只可能获得来自文件路径的目录分类。三、单数category键兼容写法除了复数categoriesJekyll 还兼容单数category键。test/source/_posts/2009-01-27-category.markdown 展示了这一写法--- layout: default title: Category in YAML category: foo ---在 populate_categories 的解析过程中单数与复数会被合并处理详见下一节因此category: foo与categories: foo效果一致。官方新站点模板 lib/site_template/_posts/0000-00-00-welcome-to-jekyll.markdown.erb 中使用的正是categories: jekyll update这一复数写法这也是社区中最常见的约定。四、底层实现分类如何被解析与合并分类的解析并不只是简单地读取 front matter而是发生在 lib/jekyll/document.rb 中一条明确的调用链上理解它有助于排查分类不生效的问题。4.1 解析入口文章Document在 read_content 中读取文件内容、解析 YAML front matter 之后会调用read_post_data其中依次执行def read_post_data populate_title populate_categories populate_tags generate_excerpt end对应源码见 lib/jekyll/document.rb。4.2 单复数合并与规范化populate_categories 是分类解析的核心方法def populate_categories categories Array(data[categories]) Utils.pluralized_array_from_hash( data, category, categories ) categories.map!(:to_s) categories.flatten! categories.uniq! merge_data!({ categories categories }) end其行为可以拆解为四步将已有的categories包装为数组并与从category单数键若存在解析出的数组拼接将所有元素转为字符串map!(:to_s)——即使 YAML 中写了数字分类也会被规范化摊平嵌套数组flatten!去重uniq!。所以category: foo与categories: [foo, bar]同时出现时最终分类集合是[foo, bar]。4.3 字符串分类的分词merge_categories! 负责在每次合并数据时对字符串形式的分类做特殊处理def merge_categories!(other) if other.key?(categories) !other[categories].nil? other[categories] other[categories].split if other[categories].is_a?(String) if data[categories].is_a?(Array) other[categories] data[categories] | other[categories] end end end这就是categories: foo bar baz会被拆成[foo, bar, baz]的原因——字符串按空白分词且与已有分类做去重合并|为数组并集运算。因此同一篇文章即使通过目录和 front matter 引入了相同分类名也不会产生重复分类。4.4 合并优先级merge_data!是 Document 统一的数据合并入口lib/jekyll/document.rb它先调用merge_categories!再做深度合并调用顺序如下构造时categories_from_path来自文件路径见下节作为初始值读取 front matter 时merge_data!(data_file, :source YAML front matter)应用 front matter defaults 时merge_data!(defaults, :source front matter defaults)。由于分类采取“并集”而非“覆盖”语义三类来源最终会合并共存而其他普通字段则遵循后写覆盖先写的规则。五、隐藏来源从目录结构推断分类分类还有一个容易忽略的来源——文件路径。categories_from_pathlib/jekyll/document.rb会在 Document 初始化时执行def categories_from_path(special_dir) if relative_path.start_with?(special_dir) superdirs [] else superdirs relative_path.sub(Document.superdirs_regex(special_dir), ) superdirs superdirs.split(File::SEPARATOR) superdirs.reject! { |c| c.empty? || c special_dir || c basename } end merge_data!({ categories superdirs }, :source file path) end源码注释给出了关键规则lib/jekyll/document.rb文章位于es/_posts这样的子目录下时es会被自动加入分类文章直接位于_posts下或草稿位于_drafts下时不产生路径分类。例如仓库测试夹具 test/source/_posts/es/2008-11-21-complex.markdown 就会自动获得es这个分类。这也是为什么很多 Jekyll 多语言站点直接把语言代码作为_posts的子目录——语言会自动成为分类配合 configuration.rb 中的:categoriesURL 占位符即可生成/es/...形式的链接。六、分类如何进入 URL分类是 URL 占位符体系的一部分。默认 permalink 模板中内置了:categories占位符见 lib/jekyll/configuration.rb:date /:categories/:year/:month/:day/:title:output_ext, :pretty /:categories/:year/:month/:day/:title/,URL 的实际生成由 lib/jekyll/url.rb 完成generate_url会把模板中的:categories替换为 lib/jekyll/drops/url_drop.rb 提供的分类值def categories Array(obj.data[categories]).each do |category| yield category end end分类为数组时生成的是以/分隔的嵌套路径。例如categories: foo bar baz的文章在:pretty模板下会生成形如/foo/bar/baz/2009/01/27/categories-in-yaml/的 URL。这也解释了为什么 Jekyll 官方建议分类数量不宜过多、层级不宜过深——它们会直接拉长 URL 路径。七、在模板中遍历分类分类对模板的暴露通过 Drops 完成。文章级可用page.categories见 lib/jekyll/drops/document_drop.rb 的委托声明data_delegators title, categories, tags因此你可以在文章模板中这样输出分类{% for category in page.categories %} a href/{{ category }}/{{ category }}/a {% endfor %}站点级则通过site.categories暴露其实现位于 lib/jekyll/site.rbdef categories post_attr_hash(categories) endpost_attr_hash 会遍历所有文章把每个分类值映射到文章列表并按日期倒序排序最终得到{ 分类名 [文章...] }结构。仓库测试 test/test_site.rb 验证了这一点site.categories[foo]返回该分类下的 5 篇文章。典型的分类归档页模板如下{% for category in site.categories %} h2 id{{ category[0] }}{{ category[0] }}/h2 ul {% for post in category[1] %} lia href{{ post.url }}{{ post.title }}/a/li {% endfor %} /ul {% endfor %}八、与标签tags的异同分类与标签共用大部分机制但存在关键差异维度categoriestagsFront Matter 键categories兼容单数categorytags兼容单数tag字符串写法空格分词空格分词是否进入 URL是作为路径层级否站点索引site.categoriessite.tags规范化处理to_sflatten!uniq!flatten!标签的解析在 populate_tags其结构几乎与分类一致但没有路径推断来源。实践中两者可以灵活组合用少量分类组织站点大结构用标签描述文章的细粒度主题。九、验证分类行为的测试线索想深入验证本文所述行为可以直接查看仓库中的测试与夹具声明写法矩阵test/source/_posts/目录下的2009-01-27-*.markdown系列字符串、数组、单数键、空值、无键建议对比阅读 2009-01-27-categories.markdown、2009-01-27-array-categories.markdown、2009-01-27-category.markdown 与 2009-01-27-empty-categories.markdown站点级分类索引test/test_site.rb 断言site.categories[foo]的文章数量摘要中的分类透传test/test_excerpt.rb 验证excerpt.to_liquid[categories]返回分类数组。十、实践建议与常见问题优先使用复数categories键与官方新站点模板保持一致见 lib/site_template/_posts/0000-00-00-welcome-to-jekyll.markdown.erb分类名不要包含空格建议使用连字符如web-development避免 URL 中需要转义、也避免与字符串分词语义混淆若确需含空格请使用 YAML 数组写法控制分类数量分类会整体嵌入 URL 路径层级分类越多、URL 越长不利于可读性与 SEO善用目录分类多语言站点可将_posts/es/、_posts/fr/等语言目录作为天然分类无需在每篇文章中重复声明分类与标签分工分类构建站点的主导航结构标签提供文章级细粒度检索二者通过site.categories与site.tags分别访问排错思路若某篇文章的分类没有生效按本文 4.4 节的合并顺序逐层排查——先确认 front matter 语法空值、缩进再确认是否与路径推断的分类混淆最后检查site.categories输出是否符合预期。【免费下载链接】jekyll:globe_with_meridians: Jekyll is a blog-aware static site generator in Ruby项目地址: https://gitcode.com/gh_mirrors/je/jekyll创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考