whexy1999

构建自己的博客系统

Whexy /
February 03, 2022
博客搭建手记

经常遇到朋友问我:“你这个网站不错,用的是什么模板?” 其实这个网站是我自己写的。从 2020 年开始,我尝试自己从零开始构建一个 CMS 及内容分发站点(后文统称博客系统),经过了多年的迭代更新,现在已经成为支持我的个人博客网站PhD 学术主页,以及一些项目文档的底层框架。这个博客系统的特点是:使用一套成熟的前端技术栈,支持 MDX,具有良好的 SEO 收录和性能表现。

我的博客系统已经开源到 Github,点个 Star 支持一下吧~

博客系统

简单来说,“博客系统” 负责将博文内容转换成可供阅览的博客网页。以最著名的开源博客系统 Hexo 为例:站长只需要提供文章的 Markdown 文稿,即可用 Node.js 脚本生成一个静态博客网站。他也可以使用网上公开的博客主题来调整网站的外观。这些博客系统非常方便,很适合快速搭建博客网站。配合近年来蓬勃发展的静态网页托管服务(如 GitHub Pages,Netlify,对象存储服务等),可以迅速把博客部署到互联网上。搭建及运营维护的成本极低。

自建博客系统

那么为什么还要构建自己的博客系统呢?根本原因是:静态网页生成器缺乏定制性。这里的定制性不是说更换主题,而是对网页所呈现内容的深度定制。

根据我的个人经验,静态网页生成器可以迅速搭建出简单的博客网站。但是,当需求越来越复杂时,实现成本就会陡峭地增加。例如,我曾经使用 Hexo 作为博客系统。有一些复杂的功能需要自己写插件;为了写插件,我就需要了解 Hexo 的底层知识;终于我写出了插件,但是它不生效,最后发现是 Hexo 本身有 Bug,只好去开源社区提 Issue…… 而在自己构建的博客系统里,你可以用最舒适的方式完成深度定制 —— 系统从上到下都是你的,想怎么改就怎么改,想实现什么就实现什么。


文章接下来将分三个部分讨论如何搭建自己的博客系统。

  1. 设计:优化排版、组件、页面,构建兼具观赏性和实用性的阅读页面。
  2. 技术:推荐适合博客系统开发的各类框架和开源项目。
  3. 分发:通过选择 CDN、优化 SEO、提供 RSS 订阅等方法提升博客访问量。

Part 1. 设计

✍️

完全掌握站点的设计

自建博客系统的一个好处就是:博主可以完全掌握站点的设计,而不是在主题商店里左右为难。这样的设计甚至可以细化到元素在鼠标悬浮时的动画效果。例如,这张卡片是一个动画组件,在鼠标悬浮并缓慢移动时会有 3D 特效。

好的设计可以更加清晰地表达你的想法,更加简洁地表现你的作品。在设计博客系统的过程中,需要特别留意下面提到的三个要点。

文字排版

排印(Typography)是一种职业。规范、舒适的排版可以极大地增加博客的阅读体验,留住你的读者。我们作为业余人士当然无法排出书籍般的精美。不过,只要遵循规范,一样可以得到舒服的版面。这里我推荐 GitHub 上点赞过万的排版规范《中文文案排版指北》。它包含中英文混排空格、标点符号全半角、英文名词大小写等规则,非常适合博客系统参照。

组件设计

博客内容通常为富文本。一般使用 Markdown 语法编写。不过,Markdown 只定义了一小部分常用的样式。相比于只能使用标准 Markdown 语法的博客生成器,自建博客系统中可以方便地添加独特的样式,体现博主的风格。

将样式抽象成组件,就可以得到更加一致、美观的阅读体验。甚至,组件也可以增加简单的交互,或提供一些基础的功能。例如展示实时状态、折叠问题的答案等等。组件使得一段平凡的文本上升为可以与读者交流的媒介。

这些是我的博客中经常使用的自定义组件:

  • Callout,以稍突出于背景色的色块标记文本段落,用于插入游离于主线叙述之外的相关内容,提供背景知识,或延展讨论某一话题等。
  • 对话气泡,创造类似即时通讯聊天的效果,用于提出问题或者提供参考信息。

我也创建了一些动态组件,例如显示实时的 GitHub 仓库信息。动态组件使得博客具有更长的时效,为读者提供更直观的信息。

一篇文章中的组件不宜过多,它应当作为提升文章阅读体验的辅助存在,而不是影响阅读的阻碍。

页面优化

读者的阅读环境不同,可能会对博客页面的显示效果产生不同的影响。例如,在小屏设备上的字体不宜过大,以免影响页面版式;在启动深色模式的设备上不宜使用浅色主题,以免影响读者的体验连贯性。

移动端优先。使用小屏幕设备(视口宽度小于 640px)的读者越来越多。根据站点的统计数据,有 70% 的流量来自移动端。采用响应式设计是博客在移动端优美显示的关键。使用 media 查询可以方便地进行样式的切换。

深色模式。在黑色的大环境里浏览白底黑字内容对读者是一种折磨,所以博客需要单独适配深色模式。为了不影响文本的辨别,文本与背景的颜色对比度至少需要达到 4.5:1。这里我推荐阅读 Google Material Design 团队的深色模式设计指南 Dark theme - Material Design,其中包含了深色模式下详细的设计规则。

Part 2. 技术

如果说后端技术是 “日新月异”,那么前端技术可谓是 “瞬息万变”。这里简单介绍一下我在构建博客系统的过程中使用过的技术框架和开源项目,以供参考。

React 生态

React 是常年占据市场份额第一的前端框架。它将 HTML 嵌入 JS 中,利用函数计算动态返回 HTML,以达到最佳性能。这种网站称为「单页面应用」。看起来博客是多个页面组成,实际上只是单个页面,里面的组件被不断的替换更新而已。目前我的博客系统是全面基于 React 编写的。在博客网页间跳转时,重复的内容(如顶栏、底栏、LOGO 等)不会重新加载。

不过,在浏览器里执行 JS 生成页面,这种「客户端渲染」的模式会导致部分性能落后的设备卡顿,甚至会出现页面渲染错位或空白。这时候需要用「服务端渲染」的框架来弥补 React 的缺点。

Next.js

Next.js 可以说是官方钦定的服务端渲染框架了,最近他们开了 Next.js Conf 大会。听名字也可以看出,这个框架代表了未来前端技术发展的方向。

Next.js 的思路是,先在服务端渲染好一个 HTML 传递给用户,然后再运行 JS,把 “过时” 或 “粗糙” 的内容替换掉。这个过程被形象地称为「注水」—— 向一个干瘪的网页慢慢填充生动的内容。我的博客多处也运用了这种技术:

  • 网页里的图片会先显示一个模糊的占位图,等到进入用户可视区域后再加载。
  • 动态字体加载,没用到的字会从字体文件中剔除。
  • GitHub 小组件会在渲染时缓存 star 数,用户打开页面时会更新缓存。

MDX 与小组件

MDX 是 Markdown 在 React 生态下的拓展,官方号称是「Markdown for the component era」。通过引入 MDX,我们可以在博客网页里使用 Markdown 格式的文本,并且可以嵌入 React 组件,例如可交互的图表、实时投票等,极大的丰富博客的功能性。

💡

加入组件

在博客文章中加入组件有两种方式。一种是我现在使用的 MDX,可以在其中添加 React Components。另一种是我之前使用的小 trick:修改 Markdown 渲染器,加入一些自定义语法,使之渲染出特殊的 HTML 标签,再利用标签选择器在 CSS 里定义对应的样式、利用文档查询在 JS 里实现对应的逻辑。

Tailwind CSS

Tailwind CSS 极大的方便了网页样式的开发。它将繁琐的 CSS 样式表简化为数个简单的属性。例如,如果要实现一个 Flex 动态布局将方框居中。

使用传统 HTML+CSS,你需要写很多样式:

wo/Tailwind.html
<style type="text/css">
  .box {
    display: flex;
    align-items: center;
    justify-content: center;
    border-width: 1px;
  }

  .box div {
    width: 100px;
    height: 100px;
    background-color: rgb(220 252 231);
  }
</style>

<div class="box">
  <div></div>
</div>

用 Tailwind CSS,连 CSS 文件都不需要,只用写一个 HTML 页面:

w/Tailwind.html
<div class="flex items-center justify-center border">
  <div class="h-[100px] w-[100px] bg-red-100"></div>
</div>

可以说 Tailwind CSS 就是这个博客系统诞生的原因。没有 CSS 的知识也能轻松设计网页。它用极短的 className 处理屏幕尺寸、深色模式、鼠标悬浮状态等等复杂场景。而这些场景用 CSS 写往往要数十行,甚至百行代码。

互动

博客通常附带一个评论系统。传统评论需要强大的后端数据库支持注册、登录、富文本、广告封禁等功能,增加了博客系统开发的难度。现在可以使用通用评论平台。这是一种嵌入博客的视图,调用第三方平台(例如 GitHub issues)完成评论。其中比较有名的是 discus 和 utterances,以及我正在使用的 giscus

另外,一些小功能也可以基于 Serverless 自行开发。

一些最终没有使用的技术框架

11ty + Nunjucks

最开始,我使用了 11ty 作为静态网页生成器。它基于 Mozilla Nunjucks 语法实现模板网页渲染。你需要写好一个 HTML 模板,在里面留空(告诉 11ty “这里填入标题”,“这里填入文章内容”)。11ty 会按照你指定的规则填空,生成最后的网页。这类静态网页生成器最适合用来写博客网站。

但是生成器也有缺点:

  • 可以抽象组件,但很难抽象逻辑
  • 无法控制渲染顺序,复杂功能实现困难

在我加入了更多页面之后,复用代码成了大问题。所以最终我不再使用 11ty。

Headless CMS

Headless 指 “无头的”,一般来说就是 “无界面的”“无系统的”。Headless CMS 是目前国外比较火的博客内容管理方案,通过 GraphQL(图数据库)获取数据。本质上是博客后端服务器。这里我推荐这篇文章 Headless CMS explained in 1 minute 了解 Headless CMS 的概念。

从前我们把文章存储在本地的 Markdown 文件里,然后让生成器生成静态网站。每次发新博客都需要 rebuild 一遍网站,再上传到服务器。服务端渲染允许你在 CMS 更新后(例如发布新文章,或修改旧文章)触发服务端重渲染,用户可以在网页的「注水阶段」获得更新。

使用 Headless CMS 的一般是团队经营的大型博客网站,例如一个实验室或一个小型公司。作为个人博客,Headless CMS 属于杀鸡用牛刀。所以我试了一阵子就退回到传统 git 管理博客更新的方案上。

技术总结

目前博客使用的技术栈是:

  • React 用于封装组件、绘制页面
  • Next.js 用于服务端渲染
  • MDX 用于拓展 Markdown 的能力
  • Tailwind CSS 用于样式设计

Part 3. 分发

提升博客质量,不断获取新的读者,提升自己在社区的声量,最终以达到和更多人交流,互相促进提升的效果。博客的分发是一个重要的部分。

SEO 收录

SEO 收录表征博客文章在搜索引擎的收录情况。提高 SEO 的最好方法是让别的网站多多收录本网站的链接(例如交换友链、发布广告等等)。提升页面的 Metric 也有助于提升 SEO。

Metric

Metric 用于评估网页在不同设备上的性能表现。来自谷歌团队的 Lighthouse 是一个著名的测量工具。使用 Chromium 内核的浏览器都在开发者工具里附带了这个功能。它测量的指标包括 Performance, Accessibility, Best Practices 等。你可以获得 First Contentful Paint, Max Potential First Input Delay 等等很细致的数据,用于优化网站的体验。

CDN

博客的加载速度和阅读量正相关。为了最快速地加载网页内容,需要使用 CDN 将网页内容从中心服务器推向边缘服务器。我的网站部署在 Vercel 上,并用 Azure CDN 加速。这里我推荐这篇文章:《个人博客 CDN 选型和进阶玩法指北》,文章里比对了各种个人博客 CDN 方案的优劣。

订阅

忠实读者可以通过订阅 RSS 的方式第一时间获取博客最新内容。本博客提供一个 RSS 非全文源。RSS2.0

为什么不是全文源?

RSS 分发不支持 CSS,如果是全文源,我们的自定义组件就无法正常显示了。

关于 RSS 阅读器,我推荐 iOS/macOS 平台的 Reeder。你也可以自架一个 RSS 订阅中心用于同步各设备之间的阅读进度,例如开源的 FreshRSS

总结

这篇《构建自己的博客系统》就到这里结束了。我从三个角度出发介绍了构建自己博客系统时的构思和工作。这篇文章同时鼓励你自己搭建一套博客系统,因为这很有意思。就和阅读、看电影、玩游戏一样,配置和优化博客系统成了我业余时间的乐趣之一。

如果你喜欢这篇博客,或想第一时间获得通知,可以通过 RSS 订阅更新。也欢迎关注我的 GitHub 和推特账号。我平时是主要分享关于计算机系统安全的内容。如果你对这篇文章有任何疑问或建议,请在下方留言。再会~

博客搭建手记

© LICENSED UNDER CC BY-NC-SA 4.0