个人主页升级实战:Jekyll 增量改造的取舍与落地Upgrading My Personal Site: Jekyll Incremental Refactoring — Trade-offs and What Actually Shipped

从「要不要迁移框架」到「搜索、评论、SEO 全都要」的一次完整记录From 'should I migrate frameworks' to 'search, comments, SEO — I want it all'

Posted by Ryan on September 2, 2026

最近给自己的 GitHub Pages 个人主页做了一次升级。最纠结的一个问题是:要不要迁移到更现代的框架(Astro)? 最终我选择了「在现有 Jekyll 基础上做增量改造」。这篇文章聊聊为什么,以及各项功能是怎么落地的。


一、先看清现状,别急着动手

很多人拿到一个老项目,第一反应是”重写”。但在动手前,我花了半小时把仓库彻底摸了一遍,发现它的真实形态和”老旧”的印象并不完全一样:

  • 框架是 Jekyll(Hux/BY Blog 主题 fork),GitHub Pages 对 master 分支自动构建,不需要任何 CI。
  • 首页是一个纯静态自定义页(约 1100 行),暖色调编辑风 + 中英双语切换 + PWA,手工打磨的痕迹很重。
  • 文章只有 2 篇,而且全站居然没有博客列表页——首页博客区只是外链到 CSDN。
  • 评论系统 Gitalk 的 clientSecret 明文躺在 _config.yml 里(公开仓库的严重安全隐患)。
  • SEO 几乎为零:没有 OG 标签、没有 sitemap、没有结构化数据。

这个”体检”直接改变了我对方案的判断。

二、核心决策:为什么不迁移 Astro

朋友给的计划书建议全量迁移到 Astro(SSG、零 JS、Content Collections、Pagefind……听起来很美好)。我一度也动心,但冷静分析后否决了:

1. 迁移成本与风险不成正比 首页是 1100 行手工定制设计,还带 PWA 和双语逻辑。全量迁移 = 把这套设计用 Astro 组件重写一遍,样式还原、交互还原,风险极高——很可能把用户精心打磨的视觉效果改坏。

2. 功能上 Jekyll 完全够用 分类、标签、归档、RSS、sitemap,这些都是 Jekyll 原生或 GitHub Pages 白名单插件自带的能力。博客内容只有 2 篇,不存在性能或内容规模问题。

3. 部署链路零改动 GitHub Pages 原生构建 Jekyll,push 即上线。迁移 Astro 反而要引入 GitHub Actions + 改部署分支,徒增复杂度。

结论:框架迁移的本质动机应该是”现有技术栈无法满足需求”,而不是”新框架更酷”。 这次需求全部能用增量实现,于是选择了零风险的路线。

三、增量改造的落地清单

最终方案:保留 Jekyll + 现有首页设计,叠加全部新功能。逐项看怎么做的。

1. 文章系统:分类、标签、搜索

  • 新增 /blog/ 列表页(文章卡片:标题、日期、摘要、分类、标签)。
  • 新增 /categories/ 分类归档页;/tags/ 标签页保留。
  • 文章 frontmatter 增加 categories 与 description 字段,Jekyll 自动聚合。

搜索用的是 Fuse.js(轻量模糊搜索库,本地化部署无后端)。核心思路是让 Jekyll 在构建时生成一份 search.json 索引(标题/正文/摘要/分类/标签),前端 Fuse.js 加载后即时模糊搜索,完全离线、零依赖:

// search.json —— Jekyll 构建时自动生成
{
  "title": "文章标题",
  "url": "/blog/my-post/",
  "date": "2026-09-02",
  "content": "正文全文…",
  "categories": ["技术"],
  "tags": ["Jekyll"]
}
// 前端 Fuse.js 全文搜索
const fuse = new Fuse(data, {
  keys: [
    { name: 'title', weight: 0.4 },
    { name: 'content', weight: 0.3 },
    { name: 'tags', weight: 0.1 },
    { name: 'categories', weight: 0.05 }
  ],
  threshold: 0.4,
  ignoreLocation: true
});

新文章自动进入搜索/分类/sitemap/RSS——因为索引都是构建时生成的,写 Markdown 推上去就行,工作流完全不变。

踩坑:加上 categories 后,Jekyll 默认 permalink 会把中文分类名 URL 编码进路径(/%E6%8A%80%E6%9C%AF/...),等于改了所有旧文章链接,会破坏已收录的 SEO。解决:显式设 permalink: /blog/:title/,语义化且与分类解耦,链接永久稳定。

2. 评论系统:Gitalk → Giscus

原 Gitalk 需要把 clientSecret 暴露在前端,密钥明文躺在公开仓库里,这是必须处理的隐患。

换成 Giscus(基于 GitHub Discussions):

  • 无任何密钥暴露,更安全
  • 免费、无广告、支持 GitHub 登录
  • 支持 Markdown、表情回应、跟随系统主题
  • 评论数据直接落在仓库的 Discussions 里

接入就是一段 <script>(repo-id / category-id 从 giscus.app 获取):

<script src="https://giscus.app/client.js"
  data-repo="owner/repo"
  data-repo-id="你的repo-id"
  data-category="Announcements"
  data-category-id="你的category-id"
  data-mapping="pathname"
  data-theme="preferred_color_scheme"
  data-lang="zh-CN"
  crossorigin="anonymous" async></script>

3. SEO:Meta / OG / Twitter / JSON-LD / Sitemap

Jekyll 页面统一在 _includes/head.html 用 Liquid 动态生成:每个页面独有的 Title、Description、Keywords,加上 Open Graph、Twitter Card、canonical,以及 JSON-LD 结构化数据(文章页 BlogPosting,其他页 WebSite + Person):


<meta property="og:title" content="{{ page.title }} - {{ site.SEOTitle }}">
<meta property="og:url" content="{{ page.url | prepend: site.url }}">
<meta property="og:image" content="{{ site.url }}/img/profile.jpg">
<meta name="twitter:card" content="summary_large_image">

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "BlogPosting",
  "headline": "…",
  "datePublished": "…",
  "author": { "@type": "Person", "name": "…" }
}
</script>

Sitemap & robots:GitHub Pages 白名单里有 jekyll-sitemap,加一行配置自动生成 sitemap.xml;再手写 robots.txt 引用它。

特别提醒:如果首页是不走 Jekyll 模板的纯静态文件,那 head.html 对它不生效,需要在首页 <head> 里单独手写一遍 SEO 标签。这是最容易漏的点。

4. 流量统计 GA4

统一在 head.html 注入 gtag.js,Measurement ID 放 _config.yml 配置(好管理):


<script async src="https://www.googletagmanager.com/gtag/js?id={{ site.ga4_track_id }}"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());
  gtag('config', '{{ site.ga4_track_id }}');
</script>

静态首页同样单独加一份。替换真实 ID 后即可在 GA 后台看到访问数据。

5. 打赏:GitHub Sponsors

配置一个链接 https://github.com/sponsors/kjlintong,在页脚、文章页底部、首页都放了入口按钮。别人点击即可通过 GitHub Sponsors 打赏,无手续费、纯官方渠道。

更新(2026-09-14):打赏通道已切换为 Ko-fi,页脚、文章页底部、首页入口均已同步更新。

6. 首页文章区:让 Jekyll 渲染最新文章

原本首页只是两个外链卡片。用户希望像”精选项目”一样展示文章列表。关键技巧:给原本纯静态的 index.html 加一个空 frontmatter(---),Jekyll 就会用 Liquid 渲染它,文章列表随之自动更新:


---
---
{% for post in site.posts limit:3 %}
  <!-- 文章卡片:标题 / 日期 / 摘要 / 分类 / 标签 -->
{% endfor %}

前置检查:确认首页里没有 Liquid 模板语法残留(左花括号加左花括号、或左花括号加百分号这类组合),才敢加 frontmatter。改造后要重新验证 JS 和双语逻辑没被破坏。

四、值得记住的经验

  1. 先体检,再决策。动手前摸清仓库真实结构,避免基于错误假设做方案。
  2. 增量优于重写。框架迁移的成本经常被低估(重写设计、改部署、回归测试),收益常被高估。需求能用现有栈实现,就别折腾。
  3. 公开仓库禁止放任何密钥。Gitalk 的 clientSecret 明文在仓库里 = 裸奔,务必换掉(Giscus 无密钥暴露)。
  4. 改 URL 结构要谨慎。中文分类名进 permalink 会悄悄改变所有旧链接,破坏 SEO——用稳定的自定义 permalink。
  5. 静态首页容易漏 SEO。不走模板的纯静态页面,head.html 不生效,要单独补一遍 OG/JSON-LD/GA4。

五、结语

这次升级没有换框架、没有重写首页,却在保留原有视觉设计的前提下,补齐了文章系统、全文搜索、评论、SEO、统计、打赏这些现代站点标配。有时候最好的重构,就是清醒地决定”不重构”。 希望这次的取舍思路和落地细节,对同样维护着老博客的你有所帮助。

如果你也在折腾 Jekyll / GitHub Pages 站点,欢迎在评论区交流,或者通过 Ko-fi 请我喝杯咖啡 ☕