
1. 为什么现在还要用 GitHub Pages 做个人博客——一个被低估的“极简基建”选择你可能刚刷到某篇热文标题写着“Jekyll 和 Hexo 哪个好”点进去全是配置对比、插件生态、渲染速度表格最后却没说清楚一件事你到底需要什么不是“哪个框架更炫”而是“你写第一篇文章时最不想花时间在哪儿”我用 GitHub Pages 搭建过 7 个不同定位的个人站点——技术笔记站、设计作品集、读书摘录库、开源项目文档页、甚至给父母做的家庭相册站。它们共用同一个底层逻辑不碰服务器、不装数据库、不配 Nginx、不设防火墙但上线只要 37 秒访问零运维成本且三年没出过一次 500 错误。这背后不是玄学是 GitHub Pages 的硬核设计哲学它只托管静态文件不做任何动态解析。你提交的是 HTML、CSS、JS、图片它就原样返回你本地生成的是 237 个 .html 文件它就托管这 237 个文件。没有 PHP 解释器、没有 Node.js 进程、没有 MySQL 连接池——也就没有这些组件带来的崩溃、超时、内存泄漏、版本冲突。很多人一听说“静态网站”下意识觉得“功能弱”“不能留言”“没法搜索”。但现实是90% 的个人博客根本不需要后端。你写技术总结读者要的是快速加载和代码高亮你发摄影作品核心诉求是高清图直链和响应式布局你做读书笔记关键在分类归档和关键词检索——这些全都能用纯前端方案解决。Jekyll 生成的静态页面里搜索靠 Lunr.js48KB JS 库离线运行评论用 utterancesGitHub Issue 驱动零服务端代码订阅用 RSS浏览器原生支持连 Feedly 都不用装。更重要的是GitHub Pages 天然绑定 Git 工作流。你改一行 CSSgit commit -m fix header padding→git push30 秒后全球 CDN 就生效。没有 FTP 上传失败的焦虑没有宝塔面板里“重启 Nginx”按钮按了三次才成功的尴尬也没有凌晨两点收到“服务器磁盘满”的告警短信。它把建站这件事拉回到最原始也最可靠的层面你控制源码GitHub 控制分发浏览器控制呈现。所以当热搜里争论“Jekyll 还是 Hexo”时真正该问的是你愿不愿意为博客多维护一个本地环境Jekyll 依赖 Ruby 生态Hexo 依赖 Node.js 版本而 GitHub Pages 原生支持 Jekyll无需本地构建也支持直接上传静态文件完全绕过构建工具。这意味着——你可以今天用 VS Code 写 Markdown拖进 GitHub 仓库明天就上线也可以用 Obsidian 导出 HTML批量上传甚至用 Notion Next.js 导出静态包扔进 gh-pages 分支。它的开放性远超多数人认知。提示GitHub Pages 不是“过时技术”而是刻意做减法的成熟方案。它不追求“能做什么”而坚守“不该做什么”。当你发现自己的博客需求清单里95% 的条目都指向“内容展示”而非“用户交互”那 GitHub Pages 就不是备选而是最优解。2. 绕过 Ruby 环境零依赖部署 Jekyll 博客的三种实操路径很多人卡在第一步“安装 Ruby 太麻烦”“Windows 上 bundle install 总报错”“Mac M1 芯片 Ruby 版本冲突”。这其实是个认知偏差——你不需要在本地运行 Jekyll就能用它生成 GitHub Pages 站点。GitHub 官方明确支持两种模式本地构建 上传静态文件完全脱离 RubyGitHub Actions 自动构建Ruby 环境由 GitHub 托管纯静态上传 启用 Jekyll 插件GitHub 自动解析 _config.yml下面拆解每种路径的实操细节、适用场景和避坑要点2.1 路径一VS Code GitHub Web 编辑器 —— 真·零环境部署这是最轻量的启动方式适合纯文字博主或想快速验证想法的人。核心逻辑用 GitHub 自带的 Jekyll 渲染引擎跳过所有本地构建步骤。操作流程新建 GitHub 仓库命名格式必须为username.github.io如zhangsan.github.io在仓库根目录创建_config.yml文件内容极简# _config.yml title: 我的技术笔记 theme: jekyll-theme-minimal plugins: - jekyll-feed - jekyll-sitemap创建_posts目录在其中新建2024-06-15-hello-world.md--- layout: post title: 欢迎来到我的博客 date: 2024-06-15 --- 这是第一篇文章。Jekyll 会自动将它渲染为 HTML。提交后等待 2 分钟访问https://username.github.io即可看到生成的页面。原理很简单GitHub Pages 服务检测到_config.yml和_posts目录会自动调用内置 Jekyll 引擎版本 4.3.2锁定安全版本进行构建。你本地连 Ruby 都不用装所有解析都在 GitHub 服务器完成。注意此方式仅支持 GitHub 官方白名单插件如jekyll-feed、jekyll-sitemap、jekyll-include-cache。自定义插件如jekyll-paginate-v2会失效因为 GitHub 不允许执行未审核的 Ruby 代码。但对 90% 的个人博客官方插件已覆盖全部刚需RSS 订阅、站点地图、CDN 缓存控制。2.2 路径二GitHub Actions 自动化构建 —— 本地无 RubyCI/CD 全托管当你需要自定义主题、复杂布局或第三方插件时本地构建不可避免。但不必在自己电脑装 Ruby——让 GitHub Actions 代劳。实操步骤在仓库根目录创建.github/workflows/jekyll-build.ymlname: Build and Deploy Jekyll on: push: branches: [main] paths: [_posts/**, _layouts/**, _includes/**, _config.yml, index.md] jobs: build-and-deploy: runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 - name: Setup Ruby uses: ruby/setup-rubyv1 with: ruby-version: 3.1 bundler-cache: true - name: Install Dependencies run: bundle install - name: Build Jekyll Site run: bundle exec jekyll build --destination ./_site - name: Deploy to GitHub Pages uses: peaceiris/actions-gh-pagesv3 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./_site在本地初始化 Jekyll 项目用 Docker 避免污染系统环境# 无需安装 Ruby用官方镜像临时构建 docker run --rm -v $(pwd):/srv/jekyll -w /srv/jekyll jekyll/jekyll:4.3.2 \ jekyll new . --force修改_config.yml添加自定义插件如jekyll-archivesplugins: - jekyll-archives - jekyll-scholar # 学术引用支持 archivedir: /archives/提交代码Actions 会自动触发下载 Ruby、安装 Bundler、执行jekyll build、推送_site到gh-pages分支。关键优势本地开发环境彻底干净Ruby 版本、Gem 依赖全在容器内隔离构建日志实时可见报错直接定位到bundle exec jekyll build的哪一行支持所有 Jekyll 插件包括需编译的 native extensions构建缓存机制让第二次构建提速 70%平均耗时 42 秒实测教训不要在jekyll build命令后加--watch参数GitHub Actions 是无状态环境--watch会导致构建永远挂起。所有构建必须是单次、确定性、无交互的。2.3 路径三Obsidian Publish 插件 —— 写作即发布如果你用 Obsidian 做知识管理这条路径能消灭“写作→导出→上传”的断层。Obsidian 官方 Publish 功能付费本质就是静态站点生成器但免费方案同样强大安装社区插件obsidian-export开源MIT 协议配置export.json{ output: ./_site, template: templates/default.html, frontmatter: { layout: post, date: {{date}} } }在 Obsidian 中写笔记添加 YAML frontmatter--- title: Git 重写历史的 5 种安全姿势 tags: [git, workflow] date: 2024-06-10 --- 正文内容...执行导出命令生成标准 Jekyll 兼容的_posts结构。此时你获得的是纯静态 HTML 文件可直接上传到 GitHub Pages 仓库的main分支无需_config.ymlGitHub Pages 会识别为静态站点。若需 Jekyll 功能如归档页、标签云再补上_config.yml和少量 Liquid 模板即可。这种模式的核心价值在于写作环境与发布系统解耦。你在 Obsidian 里享受双向链接、图谱视图、块引用发布时只需一键导出所有样式、导航、SEO 元数据由模板控制。比传统“先写 Markdown再塞进 Jekyll 模板”少 3 步手动操作错误率下降 82%基于我跟踪的 12 位博主数据。3. 主题定制实战从 jekyll-theme-minimal 到企业级响应式布局很多人以为 GitHub Pages 主题只能用官方那几个简陋模板其实 Jekyll 主题机制极其灵活——你可以 fork 任意开源主题修改 CSS/JS甚至完全重写布局而 GitHub Pages 依然能完美渲染。关键在于理解 Jekyll 的模板继承体系和资产加载逻辑。3.1 主题加载机制Liquid 模板的三层继承结构Jekyll 主题并非“整体替换”而是通过layout、include、content三者组合实现模块化。以jekyll-theme-minimal为例其结构如下_layouts/ default.html # 基础骨架HTML 结构、head 标签、全局 JS post.html # 文章页继承 default.html插入文章内容 _includes/ head.html # head 内容meta、link、script footer.html # 页脚版权信息、社交链接 assets/ css/ style.scss # Sass 源文件编译为 main.css当你在文章中声明layout: postJekyll 会加载_layouts/post.html在body内插入文章 Markdown 渲染后的 HTML通过{% include head.html %}注入头部资源最终输出完整 HTML这意味着你只需修改_includes/head.html就能全局注入 Google Analytics覆盖_layouts/default.html就能替换整个页面结构重写assets/css/style.scss就能彻底改变视觉风格。所有改动都不影响 GitHub Pages 的构建流程。3.2 实战为 minimalist 主题添加暗色模式切换原版jekyll-theme-minimal无暗色模式但只需 4 步即可实现Step 1在_includes/head.html添加 CSS 变量定义style :root { --bg-color: #ffffff; --text-color: #333333; --link-color: #007bff; } media (prefers-color-scheme: dark) { :root { --bg-color: #121212; --text-color: #e0e0e0; --link-color: #bb8f00; } } /styleStep 2修改assets/css/style.scss用变量替代硬编码颜色body { background-color: var(--bg-color); color: var(--text-color); } a { color: var(--link-color); }Step 3添加 JavaScript 切换逻辑存于_includes/scripts.htmlscript function toggleDarkMode() { document.documentElement.classList.toggle(dark-mode); const isDark document.documentElement.classList.contains(dark-mode); localStorage.setItem(darkMode, isDark); } // 初始化时读取 localStorage if (localStorage.getItem(darkMode) true) { document.documentElement.classList.add(dark-mode); } /scriptStep 4在_layouts/default.html添加切换按钮button onclicktoggleDarkMode() aria-label切换暗色模式 {% if site.dark_mode_icon %}{{ site.dark_mode_icon }}{% else %}{% endif %} /button关键细节GitHub Pages 默认禁用内联 JavaScript出于安全考虑因此必须将脚本存为独立文件。将上述 JS 保存为assets/js/dark-mode.js并在_includes/head.html中用script src/assets/js/dark-mode.js/script引入。否则按钮点击无效。3.3 响应式优化针对移动设备的 3 个致命陷阱很多博客在手机端体验糟糕并非 CSS 写得不好而是忽略了 Jekyll 的静态特性陷阱一图片未设置srcset导致移动端加载桌面图错误写法img src/assets/images/photo.jpg alt风景正确写法利用 Jekyll 的picture插件或手动编写{% assign img_path /assets/images/photo.jpg %} picture source media(min-width: 768px) srcset{{ img_path }} 1x, {{ img_path | replace: .jpg, 2x.jpg }} 2x source media(max-width: 767px) srcset{{ img_path | replace: .jpg, -mobile.jpg }} 1x img src{{ img_path }} alt风景 loadinglazy /picture原理Jekyll 在构建时生成photo2x.jpg和photo-mobile.jpgGitHub Pages 直接托管这些文件浏览器根据媒体查询自动选择最优尺寸。陷阱二字体加载阻塞渲染Google Fonts 的import会阻塞 CSS 解析。解决方案使用link relpreconnect提前建立连接用font-display: swap确保文本立即显示link relpreconnect hrefhttps://fonts.googleapis.com link relpreconnect hrefhttps://fonts.gstatic.com crossorigin link hrefhttps://fonts.googleapis.com/css2?familyNotoSansSC:wght300;400;500;700displayswap relstylesheet陷阱三导航菜单在小屏消失原主题的导航栏用display: flex小屏直接溢出。修复方案.navbar { media (max-width: 767px) { display: none; // 默认隐藏 } } .nav-toggle { display: none; media (max-width: 767px) { display: block; // 小屏显示汉堡按钮 } }再配合简单 JS 切换菜单显隐比 Bootstrap 的 JS 依赖更轻量。4. 搜索、评论、订阅静态网站的“动态功能”实现方案静态网站常被诟病“无法交互”但现代前端技术已让三大核心功能——全文搜索、用户评论、内容订阅——在零后端前提下稳定运行。关键是选对工具链而非强行套用动态方案。4.1 全文搜索Lunr.js 的离线索引构建策略Lunr.js 是纯前端全文搜索库体积仅 48KB支持中文分词需额外加载lunr-languages。难点不在集成而在索引构建时机和更新机制。错误做法在页面加载时用fetch请求所有文章 HTML用正则提取正文实时构建索引——导致首屏加载延迟 3 秒以上。正确做法在 Jekyll 构建阶段生成预编译索引。步骤创建_data/search.jsonJekyll 会自动将其注入site.data.search--- --- [ {% for post in site.posts %} { title: {{ post.title | jsonify }}, url: {{ post.url | jsonify }}, content: {{ post.content | strip_html | truncatewords: 50 | jsonify }} }{% unless forloop.last %},{% endunless %} {% endfor %} ]在assets/js/search.js中加载// 预加载 JSON避免阻塞渲染 const searchIndex await fetch(/assets/data/search.json).then(r r.json()); const idx lunr(function () { this.ref(url); this.field(title, { boost: 10 }); this.field(content); this.metadataWhitelist [position]; }); searchIndex.forEach(doc idx.add(doc));搜索时function performSearch(query) { return idx.search(query).map(result { const doc searchIndex.find(d d.url result.ref); return { ...doc, score: result.score }; }); }实测数据127 篇文章的索引文件仅 1.2MBgzip 后 320KB。首次搜索响应时间 80msiPhone 12 测试比 Algolia 的 API 调用更快且无月度请求限额。4.2 用户评论utterances 的深度定制与隐私合规utterances 是基于 GitHub Issues 的评论系统零服务端、开源、无追踪。但默认样式与博客主题割裂且需处理 GDPR 合规问题。定制 CSS/* utterances.css */ .utterances { max-width: 800px; margin: 0 auto; } .utterances-frame { border-radius: 8px; box-shadow: 0 2px 12px rgba(0,0,0,0.08); } /* 适配暗色模式 */ media (prefers-color-scheme: dark) { .utterances-frame { filter: invert(90%) hue-rotate(180deg); } }引入方式在_includes/comments.html中script srchttps://utteranc.es/client.js repousername/username.github.io issue-termpathname themepreferred-color-scheme crossoriginanonymous async /scriptGDPR 合规要点utterances 不收集用户 IP、不设 Cookie、不嵌入第三方 tracker但需在隐私政策页声明“评论功能由 utterances 提供数据存储于 GitHub Issues受 GitHub 隐私政策约束”提供“删除评论”指引用户可自行登录 GitHub 删除对应 Issue注意issue-termpathname表示每篇文章对应一个 Issue。若文章 URL 改变如/post/old-title→/post/new-title旧评论会丢失。解决方案是固定issue-termurl用文章永久链接作为 Issue 标识。4.3 内容订阅RSS 2.0 的最小可行实现Jekyll 官方插件jekyll-feed生成标准 RSS 2.0但默认包含所有文章。对技术博主需过滤掉“随笔”“生活”类文章。精准控制 RSS 输出在_config.yml中配置feed: categories: - tech - tutorial exclude: - draft - personal为每篇文章添加 frontmatter--- layout: post title: Webpack 5 模块联邦实战 categories: [tech, webpack] tags: [webpack, micro-frontend] --- 正文...在feed.xml模板中强化过滤?xml version1.0 encodingUTF-8? rss version2.0 xmlns:atomhttp://www.w3.org/2005/Atom channel title{{ site.title | xml_escape }}/title description{{ site.description | xml_escape }}/description link{{ site.url }}{{ site.baseurl }}//link atom:link href{{ /feed.xml | prepend: site.baseurl | prepend: site.url }} relself typeapplication/rssxml/ {% for post in site.posts limit:10 %} !-- 只包含 tech 和 tutorial 分类 -- {% if post.categories contains tech or post.categories contains tutorial %} item title{{ post.title | xml_escape }}/title description{{ post.content | xml_escape }}/description pubDate{{ post.date | date_to_rfc822 }}/pubDate link{{ post.url | prepend: site.baseurl | prepend: site.url }}/link guid{{ post.url | prepend: site.baseurl | prepend: site.url }}/guid /item {% endif %} {% endfor %} /channel /rss验证方法用 W3C Feed Validation Service 检查生成的/feed.xml确保无 XML 格式错误。实测发现若post.content包含未转义的符号如代码片段会导致 RSS 解析失败必须用| xml_escape过滤。5. 宝塔面板 vs GitHub Pages静态网站部署的决策树网络热词“使用宝塔部署静态网站”背后是大量用户对部署逻辑的误解。宝塔本质是 Linux 服务器的可视化管理工具而 GitHub Pages 是 CDN 托管服务。二者不是替代关系而是不同层级的基础设施。选择前必须厘清你的真实需求。5.1 一张表看透本质差异维度GitHub Pages宝塔面板Nginx基础设施GitHub 全球 CDNCloudflare 节点你租用的 VPS如腾讯云轻量应用服务器运维责任GitHub 全权负责SSL、DDoS、扩容你负责系统更新、安全加固、备份恢复部署流程git push→ 自动构建 → CDN 分发30秒上传文件 → 配置 Nginx → 重启服务 → 验证 HTTPS5分钟起HTTPS 证书Lets Encrypt 自动续期无需操作需手动申请或配置 acme.sh 自动续期流量成本免费100GB/月带宽实际不限按 VPS 带宽计费如 1TB/月 30扩展能力仅静态文件HTML/CSS/JS/图片可部署 PHP/Node.js/Python 后端应用关键结论如果你的博客未来三年内不会增加用户登录、支付、实时聊天等后端功能那么宝塔部署是过度设计。它把“托管静态文件”这个简单任务包装成“运维服务器”的复杂工程。5.2 什么情况下该用宝塔——三个真实场景场景一需要自定义域名 泛解析GitHub Pages 仅支持username.github.io或custom.com需 CNAME不支持*.blog.custom.com。若你计划做子域名博客如react.blog.com、vue.blog.com宝塔的 Nginx 泛解析更灵活。场景二已有 VPS 且需整合其他服务你已在 VPS 上运行 WordPress 企业站想把技术博客作为子目录/blog/部署。此时用宝塔在同一台服务器托管多个服务比 GitHub Pages Cloudflare 页面规则更可控。场景三合规审计强制要求数据境内存储某些行业如金融、医疗要求用户数据不出境。GitHub 服务器位于美国而国内 VPS 可满足属地化存储要求。此时宝塔是合规刚需而非技术偏好。5.3 混合架构GitHub Pages Cloudflare Workers 的进阶玩法当静态网站需要轻量动态能力如 A/B 测试、地域化内容、URL 重定向又不想运维服务器时Cloudflare Workers 是最佳桥梁。例如实现“新访客显示欢迎弹窗老访客不显示”在 GitHub Pages 仓库添加sw.jsService Worker// sw.js self.addEventListener(fetch, event { event.respondWith( fetch(event.request) .then(response { if (response.url.endsWith(.html)) { return response.text().then(html { const hasVisited self.registration.active?.waiting?.state activated; const modifiedHtml html.replace( /body, scriptif (!${hasVisited}) { showWelcomePopup(); }/script/body ); return new Response(modifiedHtml, { headers: response.headers, status: response.status }); }); } return response; }) ); });在 Cloudflare Workers 中部署重定向逻辑addEventListener(fetch, event { event.respondWith(handleRequest(event.request)) }) async function handleRequest(request) { const url new URL(request.url) if (url.pathname.startsWith(/old-post)) { return Response.redirect(https://new-domain.com/new-post, 301) } return fetch(request) }这种架构下GitHub Pages 仍是内容源Cloudflare Workers 承担边缘计算两者结合既保持静态网站的稳定性又获得动态能力。且 Workers 免费额度足够个人博客使用10万请求/天。6. 我踩过的 7 个 GitHub Pages 坑及解决方案最后分享我在 7 年 GitHub Pages 实践中反复遇到、反复验证的 7 个真实问题。它们不写在官方文档里但每个都曾让我调试超过 2 小时。6.1 坑一_config.yml缩进错误导致构建失败但 GitHub 不报具体行号现象Actions 构建日志显示jekyll build失败错误信息只有YAML Exception: (unknown): did not find expected key while parsing a block mapping at line 1 column 1。原因YAML 对缩进极其敏感plugins:下的- jekyll-feed若用 Tab 而非空格或缩进不一致2空格 vs 4空格就会解析失败。解决方案在 VS Code 中安装插件YAMLRed Hat开启editor.detectIndentation: false强制使用 2 空格缩进在_config.yml顶部添加注释行# yaml-language-server: $schemahttps://raw.githubusercontent.com/jekyll/jekyll/master/lib/jekyll/schema.json启用 Schema 校验6.2 坑二中文路径文章无法访问返回 404现象文章文件名为2024-06-15-如何配置Git子模块.md生成的 URL 是/2024/06/15/%E5%A6%82%E4%BD%95%E9%85%8D%E7%BD%AEGit%E5%AD%90%E6%A8%A1%E5%9D%97.html但访问时 404。原因GitHub Pages 的 URL 编码规则与本地 Jekyll 不一致。解决方案在_config.yml中添加permalink: /:year/:month/:day/:title.html将文件名改为英文2024-06-15-how-to-config-git-submodule.md用slugfrontmatter 保留中文显示--- title: 如何配置 Git 子模块 slug: 如何配置Git子模块 ---6.3 坑三自定义域名 HTTPS 不生效显示“Not Secure”现象已配置 CNAMEDNS 解析正常但浏览器仍显示 HTTP。原因GitHub Pages 的 HTTPS 开关默认关闭需手动启用。解决方案进入仓库 Settings → Pages → Custom domain输入域名如blog.example.com勾选 “Enforce HTTPS”关键等待 1-2 分钟GitHub 自动申请 Lets Encrypt 证书注意若域名已存在其他 SSL 证书如宝塔生成的需先删除否则 GitHub 申请会失败。6.4 坑四jekyll-archives插件生成的归档页 404现象访问/archives/返回 404但_site/archives/index.html确实存在。原因GitHub Pages 默认只托管main分支根目录下的文件/archives/是子目录需确保archives/index.html路径正确。解决方案在_config.yml中指定archivedir: /archives/结尾必须有/确认插件生成的文件路径为_site/archives/index.html而非_site/archives.html在archives/index.html中检查base href/是否存在避免资源路径错误6.5 坑五图片相对路径在本地预览正常GitHub Pages 上 404现象本地用jekyll serve预览显示正常部署后图片 404。原因Jekyll 的baseurl设置影响所有相对路径。解决方案在_config.yml中设置baseurl: 根目录部署或baseurl: /blog子目录部署图片路径统一用{{ site.baseurl }}/assets/image.jpg或直接用绝对路径/assets/image.jpg推荐简洁可靠6.6 坑六jekyll-scholar引用格式在 GitHub Pages 上不渲染现象本地jekyll build生成的参考文献格式正确GitHub Pages 上显示原始 BibTeX。原因jekyll-scholar依赖 Ruby 的bibtex-ruby库GitHub Pages 白名单插件不包含它。解决方案放弃jekyll-scholar改用纯前端方案citeproc-js在_data/references.json中存入 CSL JSON 格式数据用 JavaScript 渲染引用完全脱离 Ruby 依赖6.7 坑七jekyll-paginate-v2分页插件在 GitHub Pages 上失效现象首页只显示 5 篇文章page2链接点击后 404。原因GitHub Pages 不支持jekyll-paginate-v2非白名单插件且其分页逻辑需jekyll-archives配合。解决方案改用jekyll-paginate官方支持但功能较弱或彻底放弃插件用 JavaScript 实现前端分页构建时生成all-posts.json包含所有文章元数据页面加载时用 JS 切割数组动态渲染分页内容SEO 友好为每页生成独立 HTML用 Jekyll 的collections实现这些坑的共同教训是GitHub Pages 的“魔法”只发生在白名单范围内。超出部分必须用前端方案兜底。接受这个边界反而能设计出更健壮的架构。