
Jekyll 主题系统完全指南gem-based 主题的安装、覆盖、创作与发布【免费下载链接】jekyll:globe_with_meridians: Jekyll is a blog-aware static site generator in Ruby项目地址: https://gitcode.com/gh_mirrors/je/jekyllJekyll 拥有完备的主题体系允许你借助社区维护的模板与样式快速定制站点的呈现方式。本文以 themes.md 为核心骨架结合本仓库源码系统讲解 gem-based 主题的查找安装、站点目录覆盖机制、_data数据分发、gem 主题向普通主题的转换以及主题开发者从脚手架生成到 RubyGems 发布的全流程帮助你既会用主题、也能写主题。主题从哪来挑选一个合适的主题Jekyll 主题本质上是一套可复用的「插件 资源包」它将布局layouts、包含文件includes、样式表stylesheets与静态资源assets打包并允许站点内容按需覆盖。你可以在多个主题画廊中预览与挑选GitHub 上打上jekyll-theme话题标签的仓库jamstackthemes.dev 的 Jekyll 分类jekyllthemes.org、jekyllthemes.io、jekyll-themes.com、jekyllup.com 等主题站点同时可以参考仓库 docs/_docs/resources.md 获取更多资源。理解 gem-based 主题什么是 gem-based 主题当你通过jekyll new PATH创建新站点时Jekyll 默认安装的是基于 gem 的主题Minima。所谓 gem-based是指站点的部分目录——assets、_data、_layouts、_includes、_sass——并不直接出现在你的站点目录里而是存放在主题 gem 内部构建时由 Jekyll 统一读取处理。以 Minima 为例你的站点目录中只会看到. ├── Gemfile ├── Gemfile.lock ├── _config.yml ├── _posts │ └── 2016-12-04-welcome-to-jekyll.markdown ├── about.markdown └── index.markdown其中Gemfile与Gemfile.lock由 Bundler 管理用于记录构建站点所需的 gem 及其版本。主题更新的传播机制gem-based 主题最大的优势是更新便捷主题开发者把更新推送到 RubyGems使用者即可通过 Bundler 拉取。你可以执行bundle update更新项目内全部 gem也可以执行bundle update minima只更新主题 gem。样式表、包含文件等任何新增或更新都会自动进入你的项目。其设计目标很明确让你享受一个持续演进、健壮主题的全部好处又不必让主题文件堆积在站点目录中干扰你创作内容这一核心事务。源码中的主题模型仓库 lib/jekyll/theme.rb 定义了 Jekyll 的主题类其关键行为与文档描述一一对应主题名在构造时被规范化name name.downcase.strip因此配置里大小写、首尾空格都不会影响查找。通过Gem::Specification.find_by_name(name)定位 gem找不到时抛出Jekyll::Errors::MissingDependencyException“The xxx theme could not be found”。主题根目录使用File.realpath(gemspec.full_gem_path)解析以消除 rbenv 等工具创建的符号链接带来的路径歧义确保后续路径净化sanitized path逻辑正确。六个核心子目录各有一个访问器includes_path、layouts_path、sass_path、assets_path、data_path它们通过path_for检查目录真实存在且必须位于主题根目录之内realpath_for中对 symlink 做了防逃逸校验仅允许指向主题内部的符号链接。jekyll new之后站点里之所以“看不到”这些目录正是因为 lib/jekyll/site.rb 中的configure_theme在构建时按config[theme]实例化Jekyll::Theme随后各 Reader 分别从主题中取数。覆盖主题默认值Jekyll 主题会提供默认的数据、布局、包含文件与样式表但站点可以覆盖其中任意一项。覆盖布局与包含文件要替换主题中的布局或包含文件只需在你站点的_layouts或_includes目录中新建同名文件复制一份再修改或从零编写均可。例如主题自带page布局你在_layouts/page.html创建自己的版本即可生效。定位主题文件先用 Bundler 找到主题 gem 的安装路径执行bundle info --path minima以 Jekyll 默认主题为例返回 gem 文件所在目录。按平台打开该目录# macOS open $(bundle info --path minima) # Windows先用 bundle info --path minima 拿到路径再把 / 换成 \ explorer C:\Ruby26-x64\lib\ruby\gems\ruby版本\gems\minima-2.5.1 # Linux xdg-open $(bundle info --path minima)Minima 主题 gem 的典型目录结构如下. ├── LICENSE.txt ├── README.md ├── _includes │ ├── disqus_comments.html │ ├── footer.html │ ├── google-analytics.html │ ├── head.html │ ├── header.html │ ├── icon-github.html │ ├── icon-github.svg │ ├── icon-twitter.html │ └── icon-twitter.svg ├── _layouts │ ├── default.html │ ├── home.html │ ├── page.html │ └── post.html ├── _sass │ ├── minima │ │ ├── _base.scss │ │ ├── _layout.scss │ │ └── _syntax-highlighting.scss │ └── minima.scss └── assets └── main.scss举例想覆盖 Minima 的 footer就在站点里新建_includes目录并加入footer.htmlJekyll 会优先使用你站点的文件。覆盖样式表的额外步骤修改样式需要多一步把主 sass 文件Minima 中为_sass/minima.scss也复制进站点源码的_sass目录因为你的新增样式要通过它import进主题样式。查找优先级与覆盖代价Jekyll 在以下目录中会“先看站点、再看主题”/assets/_data/_layouts/_includes/_sass从源码看这一优先级是明确的lib/jekyll/readers/layout_reader.rb 先遍历站点_layouts填充layouts哈希再遍历主题_layouts且仅在键不存在时layouts[layout_name(layout_file)] || ...才采用主题布局。同理lib/jekyll/readers/theme_assets_reader.rb 中的append_unless_exists也会在站点已存在相同相对路径文件时跳过主题资源此时日志输出Ignoring ... in theme due to existing file with that path in site。需要注意复制主题文件后这些文件将不再跟随主题更新。若希望继续获得全部样式更新可以在你自己的、独立命名的 CSS 文件中使用更高特异性的选择器来覆盖样式。提示具体哪些文件可覆盖请以所选主题自身的文档和源码仓库为准。主题中的_data目录4.3.0从 Jekyll 4.3.0 起主题的_data目录也会被纳入构建允许数据随主题一起分发。典型场景是设计元素中的文案例如主题提供一个testimonials.html包含文件其在页面上渲染一个带 h3 标题的推荐语区块。传统做法下主题作者直接把英文标题写死在 HTML 里使用者若想改文案就得复制整个包含文件到项目中修改并因此放弃主题后续更新。而借助_data机制设计者可以在模板中引用文本目录如site.data.i18n.testimonials.header并在主题的_data/i18n/testimonials.yml中把标题放在header键下由 Jekyll 完成剩余工作。对主题作者而言这看似增加了初期工作量对使用者而言定制成本大幅降低例如一位德国用户想改文案只需在自己的项目数据文件中定义site.data.i18n.testimonials.header写入德文翻译即可无需复制包含文件更不会因此失去主题更新。覆盖键的三种落点由于数据文件组织灵活同一个覆盖键在消费端可能出现在三个不同位置_data/i18n.yml键为testimonials.header_data/i18n/testimonials.yml键为header与上述示例结构一致_data/i18n/testimonials/header.yml无需键标题直接写入文件主题作者在支持用户配置文案时应留意这种歧义。在引入数据键时也要自问该键是否存在时会改变主题行为如果改变行为应放入site.config仅仅是展示数据则放在site.data即可。把会改变主题行为的数据塞进_data被视为反模式应尽量避免保证所有提供的数据都能被使用者轻松覆盖是主题作者的责任。源码中的合并顺序仓库 lib/jekyll/reader.rb 对主题数据读取逻辑的注释与实现印证了覆盖语义“If a theme is specified and it contains data, it will be read. Site data will overwrite theme data with the same key using the deep_merge_hashes”。具体实现为先通过DataReader读取主题_data以site.in_theme_dir作为源目录再执行Jekyll::Utils.deep_merge_hashes(theme_data, site.data)——站点数据作为后者合并进结果从而覆盖同名键。测试文件 test/test_theme.rb 也覆盖了assets/_data/_layouts/_includes/_sass这些目录的读取验证。将 gem-based 主题转换为普通主题如果希望摆脱 gem 依赖、让所有文件都出现在站点目录中可以按以下步骤把 gem 主题“物化”为普通主题将主题 gem 目录中的文件复制进站点目录例如站点位于/myblog就复制到该目录。处理主题引用的插件查看主题 gemspec 文件中的 runtime dependencies。以 Minima 为例可能看到spec.add_runtime_dependency jekyll-feed, ~ 0.12 spec.add_runtime_dependency jekyll-seo-tag, ~ 2.6在Gemfile中登记这些依赖有两种方式方式一同时在Gemfile与_config.yml中列出# ./Gemfile gem jekyll-feed, ~ 0.12 gem jekyll-seo-tag, ~ 2.6# ./_config.yml plugins: - jekyll-feed - jekyll-seo-tag方式二仅在Gemfile中用:jekyll_plugins分组显式声明不改_config.yml# ./Gemfile group :jekyll_plugins do gem jekyll-feed, ~ 0.12 gem jekyll-seo-tag, ~ 2.6 end两种方式任选其一别忘了执行bundle update。若部署在 GitHub Pages 上只需更新_config.yml因为 GitHub Pages 不通过 Bundler 加载插件。最后移除对主题 gem 的引用删除Gemfile中的gem minima, ~ 2.5删除_config.yml中的theme: minima。此后bundle update不再更新该主题 gem。安装一个 gem-based 主题jekyll new PATH并非唯一引入主题的途径你也可以在 RubyGems 上搜索jekyll-theme关键词找到其他 gem 主题注意并非所有主题都遵循jekyll-theme命名约定。安装步骤如下在站点的Gemfile中声明主题 gem# ./Gemfile gem jekyll-theme-minimal若站点由jekyll new生成则替换默认声明# ./Gemfile - gem minima, ~ 2.5 gem jekyll-theme-minimal安装主题bundle install在_config.yml中激活主题theme: jekyll-theme-minimal构建站点bundle exec jekyll serveGemfile中可以声明多个主题 gem但_config.yml中只能选中一个。若发布到 GitHub Pages注意它只支持部分 gem 主题同时它也支持通过remote_theme配置直接使用 GitHub 上托管的任意主题效果与 gem-based 主题类似。另外本仓库 lib/jekyll/site.rb 的configure_theme还给出了一条防御性实现如果config[theme]不是字符串例如误写成数组Jekyll 会输出警告“value of theme in config should be String to use gem-based themes”并将主题置空——因此theme值务必写成字符串。创建 gem-based 主题如果你站在主题开发者一侧可以把主题打包成 RubyGems 供用户通过 Bundler 安装。用jekyll new-theme脚手架起步对 Ruby gem 开发不熟悉也没关系Jekyll 提供new-theme命令生成脚手架。执行jekyll new-theme jekyll-theme-awesome输出大致如下create /path/to/jekyll-theme-awesome/_layouts create /path/to/jekyll-theme-awesome/_includes create /path/to/jekyll-theme-awesome/_sass create /path/to/jekyll-theme-awesome/_layouts/page.html create /path/to/jekyll-theme-awesome/_layouts/post.html create /path/to/jekyll-theme-awesome/_layouts/default.html create /path/to/jekyll-theme-awesome/Gemfile create /path/to/jekyll-theme-awesome/jekyll-theme-awesome.gemspec create /path/to/jekyll-theme-awesome/README.md create /path/to/jekyll-theme-awesome/LICENSE.txt initialize /path/to/jekyll-theme-awesome/.git create /path/to/jekyll-theme-awesome/.gitignore Your new Jekyll theme, jekyll-theme-awesome, is ready for you in /path/to/jekyll-theme-awesome! For help getting started, read /path/to/jekyll-theme-awesome/README.md.随后在对应目录中放置模板文件并根据需要完善.gemspec与 README。源码层面lib/jekyll/theme_builder.rb 的ThemeBuilder定义了脚手架逻辑SCAFFOLD_DIRECTORIES为assets _data _layouts _includes _sass五个目录create!依次创建目录、生成_layouts/{page,post,default}.html起始布局、写入Gemfile与name.gemspec、生成 README/LICENSE可选 CODE_OF_CONDUCT、执行git init并写入.gitignore。其中 gemspec 通过 ERB 模板渲染模板位于 lib/theme_template版本号取自当前 Jekyll 版本的前两段。主题名中的空格会被替换为下划线并合并连续下划线。布局与包含文件Layouts and includes主题的布局与包含文件行为和普通 Jekyll 站点完全一致布局放/_layouts包含文件放/_includes。例如主题提供/_layouts/page.html而某页面 front matter 声明layout: pageJekyll 会先查站点_layouts找不到才使用主题的page布局——这与前文 layout reader 的实现先站点后主题、同名不覆盖吻合。资源文件Assets主题/assets下的任何文件都会在构建时复制到用户站点除非用户已有相同相对路径的文件。这里可以放 SCSS、图片、webfont 等任意资源。这些文件遵循 Jekyll 对页面与静态文件的处理规则文件顶部有 front matter 时会被渲染没有 front matter 时直接原样复制进产物站点。这让主题作者可以提供一个默认的/assets/styles.scss并让布局依赖编译后的/assets/styles.css。所有/assets文件最终都会输出到产物站点的/assets目录。这正是 lib/jekyll/readers/theme_assets_reader.rb 所实现的它遍历主题 assets 路径有 YAML 头则作为Jekyll::Page加入site.pages否则作为Jekyll::StaticFile加入site.static_files同时会忽略符号链接的资源并输出警告。样式表Stylesheets主题样式放在_sass目录与编写普通 Jekyll 站点一致_sass └── jekyll-theme-awesome.scss用户侧通过import指令引入主题样式import {{ site.theme }};主题 gem 的运行时依赖3.5.0即使站点_config.yml的plugins数组没有显式列出Jekyll 也会自动 require 主题 gem 所有白名单内的runtime_dependencies注意仅在以--safe选项构建或服务时才要求白名单。这样最终用户无需在配置文件中手工维护主题所需插件。主题的预配置4.0Jekyll 会读取主题 gem 根目录下的_config.yml并将其数据合并进站点的既有配置。但与主题内其他实体不同加载主题配置文件有几条限制主题配置不能覆盖 Jekyll 的默认设置这部分决定权仍在用户手中主题配置文件不能是符号链接无论是否处于 safe 模式、符号链接指向的文件是否合法主题配置必须是键值对结构。空配置文件、仅列出条目如纯数组的文件或单纯一段文本都会被静默忽略用户不会收到任何警告或日志用户配置可以覆盖主题配置定义的任何设置。该特性旨在降低主题的上手门槛同时保证主题配置不会以危险方式影响构建主题所需插件仍需用户手动列出或由主题的 gemspec 提供。借助它主题可以开箱即用地携带主题专属的配置变量。源码佐证见 lib/jekyll/site.rb 的load_theme_configuration若站点配置ignore_theme_config: true直接跳过主题配置这也是一个可用的逃生开关主题配置路径由in_theme_dir(_config.yml)计算文件不存在则跳过File.symlink?检查使符号链接配置被直接忽略SafeYAML.load_file加载后要求结果为 Hash否则放弃theme_config.delete_if { |key, _| Configuration::DEFAULTS.key?(key) }实现了“不能覆盖 Jekyll 默认设置”的限制最终Utils.deep_merge_hashes(theme_config, config)让用户配置覆盖主题配置并返回Jekyll::Configuration实例。为你的主题写文档主题应包含/README.md说明站点作者如何安装与使用主题包含哪些布局哪些包含文件站点配置文件是否需要添加特殊内容添加截图主题是视觉化的。请在主题仓库中放置/screenshot.png作为可程序化获取的预览图也可在文档中引用它。预览主题创作过程中可以在/index.html、/page.html等文件中放入占位内容然后用jekyll build/jekyll serve像预览普通站点一样预览主题。注意若在本地预览过务必把/_site加入主题的.gitignore避免把编译产物随主题一起分发脚手架生成的.gitignore已默认处理。发布主题主题通过 RubyGems.org 发布需要一个免费RubyGems 账号。流程如下先纳入 git 仓库git init # 仅首次 git add -A git commit -m Init commit打包主题将jekyll-theme-awesome替换为你的主题名gem build jekyll-theme-awesome.gemspec推送至 RubyGemsgem push jekyll-theme-awesome-*.gem发布新版本更新 gemspec 中的版本号然后重复步骤 1-3。版本号建议遵循 Semantic Versioning 规范。总结从使用者的角度看gem-based 主题把assets/_data/_layouts/_includes/_sass藏进 gem构建时再由 Jekyll 按“站点优先、主题兜底”的顺序统一读取从_data文案到布局样式均可按需覆盖。从开发者的角度看jekyll new-theme脚手架、主题自动 require 运行时依赖、主题级_config.yml预配置这三项能力分别自 3.5.0、4.0 与 4.3.0 起逐步完善显著降低了主题开发与分发成本。掌握本文的安装、覆盖、转换与发布全流程你就能在消费主题与创作主题之间自如切换。【免费下载链接】jekyll:globe_with_meridians: Jekyll is a blog-aware static site generator in Ruby项目地址: https://gitcode.com/gh_mirrors/je/jekyll创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考