Introduction
从进入大学学习程序开发到现在,搭建的博客系统大大小小也有四五个了,断断续续尝试过 Hexo、Workpress、Hugo,博客资源有时部署在云服务器上,有时使用免费的 Github Pages 托管,或者直接放在 CDN 上面。
一次偶然的机会见到 spencerwoo 大佬的博客,觉得程序架构简洁流畅,页面样式也很美观,深得我心,于是就直接 fork 了 fat-bamboo/portfolio-react(应该是他的小号)。不过目前他似乎又开始折腾新的博客框架了。不得不说,不管是前端还是后端,项目若是能呈现良好的设计美感和丰富的想象力,就已经成功了一半。spencerwoo 之前设计并开发的多个博客/个人主页看起来都很赏心悦目,包括 spencerwoo/portfolio 和 spencerwoo/blog。
当然,作为一个可以深入研究、长期发展而不是交差了事的项目,这个博客系统最好还要能够拥抱一些比较前沿的开发技术,偏向轻量化而不是企业级,便于修改维护,少一些 CRUD,若是做成类似于“xxx 商城”这样纯堆砌工作量的项目就太无聊了。
本文的贡献如下:
- 概述了本博客的系统架构,它包含两大核心组件:NextJS 以及 Notion。
- 介绍了新兴的基于 React 的服务端渲染(SSR)前端框架——NextJS 的特点,以及将它与 Vercel 结合所产生的奇妙的化学反应。
- 分析了将 Notion 作为 CMS 在进行博客页面渲染时可能存在的问题,以及我给出的解决方案。
- 记录了本博客功能上的演进历程。
注:虽然有时称作博客(blog),但其实是一个整合了博客、友链、publication 等一系列子功能的组合体,因此我在建立仓库以及 vercel 项目的时候定义为 portfolio,也是沿用了 spencerwoo 命名。说到底,叫 blog 还是 portfolio 只是一个小问题。
Overview
本博客的架构不同于 Hexo 等一众静态内容生成框架,也不同于 WordPress 等纯动态框架。本博客采用的是 NextJS + Notion 的组合,从动态性来说,应该处于 Hexo 和 Wordpress 之间:用户请求时从(托管)服务端获取到的是高度静态化的页面,不需要频繁交互 JSON 格式的业务数据;作者写作时也只需要通过 CMS 即时修改博文内容,不需要手动操作服务端生成新的静态页面。
- NextJS:一个基于 React 的前后端一体框架,其最核心的功能是服务端渲染(SSR)——在用户请求到来时根据请求的路径和参数决定应该渲染出的页面内容,在服务端完成渲染后再返回给用户。我觉得和尘封已久的 JSP 有点像。
- Notion:一个以层级数据库为核心,类似于 Markdown 编辑器且支持多种内容块类型的笔记工具,官方提供了可用于读写笔记内容的 API,用作 CMS 十分合适。
因此,本博客基于 NextJS 框架开发,并接入了 Notion 的 API,最终服务端程序部署在了 Vercel 上,目前的架构如下图所示。

大致的数据流是这样的:
- 用户带着(路由)参数请求某个地址。
- NextJS 收到请求,会检查这个地址和参数对应的页面是否最近已经生成过并且有缓存,如果有的话就直接返回网页内容。
- 如果该网页没有生成过,NextJS 会生成这个页面。生成页面的过程可能需要请求 Notion API 接口以获得博客和文章的数据,在得到数据响应后填充到页面中,最后返回给用户。这个就是 SSR 的含义:在服务端查询后台数据,渲染得到静态资源文件,而不是让客户端(浏览器)去查询后台数据。
- 客户端收到静态资源文件后呈现给用户。
Vercel + NextJS

Vercel 是一个专门提供网站托管服务的 Serverless 平台。当用户想要部署自己的网站服务端程序但是却不想要受到配置服务器和运行环境的诸多繁琐事务影响的时候,就可以考虑 Vercel,只需要将代码上传到平台上,Vercel 就会将代码跑起来,并且分配合适的算力、内存和存储资源。实际使用时,可以将 Vercel 项目与 Github 项目绑定起来,这样开发人员只需要 Push 一份最新版本的代码到仓库就可以完成部署。
当然,和 Github Page 一样,Vercel 也可以部署纯静态网站。
NextJS 是一个基于 React 的开发框架,较为轻量级,并没有提供大量可复用的组件和工具方法,着重于简化 React 网站前后端的开发、整合和部署工作。NextJS 官网罗列出的特色功能有:
- 基于页面的路由系统(动态/静态)。目录结构和文件名构成了路由,不仅支持静态路由,也支持由文件名占位符捕获的动态路由。
- 预渲染:页面级的静态生成(SSG)和服务端渲染(SSR)。
- 自动代码拆分和优化。 对于 SSR 页面,客户端 React 代码和服务端代码被放在同一个代码文件的不同函数中。服务端代码运行由 Vercel 运行,客户端是看不到的。
- 利用 Serverless 函数构建 API。
虽然在这些功能中,服务端渲染(SSR)只是围绕着 NextJS 框架核心流程的诸多功能中的一个,但是 NextJS 核心流程所提供的优势却能够通过 SSR 窥探到大半,包括:
- 更好的 SEO:搜索引擎(百度、Google 等)的爬虫在索引 SSR 网页时,能够直接爬取有完整页面内容的网页,而在动态网页中,站点的链接往往只包含一个尚未填充网页内容的网页框架。
- 更快的首屏加载:博客的内容的静态性较强,一般只有少数时间会发生更新事件,在其余时间则保持不变,那么先渲染后缓存的模式就能够节约服务端的计算资源。如果用户访问的是已经渲染完成的页面,那么访问体验会更加流畅。
- 统一的开发模式:开发人员不再需要游走于 HTTP 通信的两端,而是可以在项目中的同一处,用相同的语言,相同的编程范式来同时完成前后端功能的编码。
Page Rendering
Features
2023.2.9
- 借助于html中的picture标签,响应式地提供图片资源:根据页面宽度加载不同尺寸的图片,根据浏览器支持情况加载更优化的图片编码格式(webp和avif)。
2023.1.18
- 参考 SpeedUp!使用黑科技为你的网站提速 这篇文章,将静态资源尽可能多地存放在 NPM 的 CDN 中,前端通过 ServiceWorker 调控下的竞速方式加速资源获取。因此基本上完全放弃了 NextJS 提供的 SSR,转而寻求网页的静态化。
- 引入 styled component,实践 CSS in JS。
2023.1.5
- NextJS 版本更新为 13。
2022.12.31
- Blog 页面支持按照文章类别进行选择过滤。
2022.11.17
- Friends 页面添加了一张引导图(不是。
- 添加 tweet 列表。
2022.11.5
- 使用 html 5 的 audio 标签,兼容了 Notion 中的内嵌音频文件在网页播放。
- 将所有文章根据是否 private 分为 2 类,默认不显示 private 文章。添加了一个不起眼的按钮作为(秘密)开关。这样当有人只是随意点进来看两眼的时候,就不会发现 private 文章,自己的稳重成熟的形象得以维护。
- 支持了文本中的内联超链接,就像这样。