在 VitePress 中实现一个动态说说功能

在构建动态博客时,添加说说功能能显著提升用户体验。相比静态博客的繁琐流程,这种功能允许用户随时随地分享短小的想法,降低了发文的心理负担。通过利用 CloudFlare Workers 实现后端逻辑,并结合 KV 存储,开发者能够轻松管理说说内容。前端则通过 VitePress 框架和 Vue 组件的嵌入,快捷地展示这些动态信息,为博客增添了生动的交互性。

前言

很多动态博客中都有一个说说的功能,本质就是一种特殊的博文,借助动态博客的实时性,可以做到随写随发

静态博客由于是在本地或服务器上静态编译成 html 后再部署,实时性比较差。写一篇博文长篇大论自然可以在电脑前,走 git 推送部署也不算麻烦,但是发一篇说说还要打开电脑,心智负担就有些重了,手机上操作 git 也比较麻烦,不是很优雅,干脆一想就不发算了

于是实现了一套说说系统的前后端,效果就是本博客的 碎碎念。后端使用 CloudFlare Workers 实现,存储当然也就近存储在大善人的 KV 里,简单写了个管理页面。博客框架是 VitePress,前端也就做成了个 Vue 组件,直接嵌入一个页面作为说说页

前端效果不再多说,后端管理页面效果 Memo 管理页面

后端 CloudFlare Workers + KV

基本概述

后端包含以下功能:

  • 支持说说的增删改(基本功能)
  • 页面和所有写接口都有鉴权,足够安全
  • Markdown 格式实时预览(by marked)

KV 中存储一个 index key,value 是一个 uid 的数组,作为全部说说的索引。其他所有说说都存储在以 uid 为 key 的条目中,value 格式如

js
{
    "uid":"唯一 id",
    "createTime":"发布时间",
    "content":"说说内容",
}

实现

首先要创建一个 CloudFlare 的 KV Space,专门存储说说相关的 KV 对。位置在账户首页 - 存储和数据库 - KV,点击创建,名字不太重要,记住就行了,我这里简单命名为 memos

接着就是创建 CloudFlare Workers,用于逻辑处理。位置在账户首页 - 计算(Workers)- Workers 和 Pages,点击创建,名字依然不太重要,我简单命名为 memos-api。创建完成后,点击 Workers 名称进入 Workers 详情,在设置 - 绑定中添加一个绑定关系,选择绑定 KV 命名空间,变量名称为 KV,KV 命名空间选择刚刚创建的 KV Space 名称,我的是 memos。这样绑定完成后,就可以在代码中直接使用 env.KV 操作 memos 这个 KV 空间了。最后点击顶栏右侧的 编辑代码 按钮

下面就是 Code Time!

首先创建一个 index.html,用来存放管理页面的 html、css 和 js

从 JS 代码中即可看出,后端包含如下两个端点

  • POST /api/auth:页面鉴权
  • GET /api/memos:获取说说详情,支持分页
  • POST /api/memos: 发布新说说
  • PUT /api/memos/{uid}: 更新说说
  • DELETE /api/memos/{uid}:删除说说

随后编辑 worker.js 实现这些端点即可

最顶上三个常量需要配置:

  • CORRECT_PASSWORD,页面密码
  • CALLBACK_URL,发布新说说或更新/删除说说后触发的回调地址
  • ALLOWED_ORIGINS,跨域处理,允许访问的域名列表,至少两个:你的博客域名和管理页面域名

配置完成后点击发布

由于墙的原因,默认的 workers.dev 域名很难访问,最好为 worker 配置一个新的域名。在 memos 详情页面 - 设置 - 域和路由,添加一个自定义域,填入一个在 Cloudflare 上托管的域名即可。注意这个域名也要添加到 worker.jsALLOWED_ORIGINS

完成后就可以使用这个管理页面了,管理页面的 URL 为 https://{你的域名}/manage,进入页面需要输入密码,then enjoy!

前端

Thanks to VitePress,我们可以很方便地通过 Vue 组件的方式,编写说说前端并嵌入博客

首先安装 markedjs 依赖,pnpm 可使用如下命令:

shell
pnpm add marked

在你的博客的主题配置文件(通常为 docs/.vitepress/theme/index.ts,文件路径和拓展名也许会有区别)的同级目录下,新建一个 components 文件夹(已有则无需新建),在其中新建 memos.vue

注意将 {你的域名} 替换为 CloudFlare Worker 的域名

眼尖的同学可能注意到了,这个组件初始化加载的内容不是通过请求 Worker 接口获取到的,而是从一个 json 文件获取的(import memosRaw from '../../../../memos.json')。只有点击加载更多,才会通过 Worker 接口获取更多内容。这是为什么呢?

  • 从体验上来说,进入说说页面时,如果初始数据从接口获取,那么这时页面在获取到数据之前会空白一会儿,体验不佳
  • 省钱的角度上来说,CloudFlare Worker 免费版是限制请求次数的,初始化数据静态获取可以极大地降低请求次数

这个 memos.json,则是在项目编译时,从接口获取到的前十条说说。这也就是为什么,Worker 代码中会添加一个 CALLBACK_URL,这个 URL 是在你发布新说说,或者删改前十条说说时重新触发编译使用的,具体 URL 可以根据你的部署平台自行搜索。如果完全动态获取说说内容的话,这里可以不用设置这么一个回调

下面的代码用于在编译时生成 memos.json,在主题配置文件(通常为 docs/.vitepress/theme/index.ts,文件路径和拓展名也许会有区别)的同级目录下,新建一个 utils 文件夹(已有则无需新建),在其中新建 memos.js

接着编辑博客根目录下的 package.json,在 dev 和 build 的命令前都添加 node docs/.vitepress/theme/utils/memos.js。这里的添加位置可能因人而异,以我为例:

json
{
  ...
  "scripts": {
    "dev": "node docs/.vitepress/theme/utils/memos.js && vitepress dev docs",
    "build": "node docs/.vitepress/theme/utils/memos.js && vitepress build docs",
    "serve": "vitepress serve docs"
  },
  ...
}

这样在 dev 阶段和 build 阶段都会首先调用 memos.js,在博客根目录下生成 memos.json。注意根据目录层级调整 memos.vue 中 import 的路径

这样组件和数据都准备好了,下面这个组件注册为全局组件

在主题配置文件(通常为 docs/.vitepress/theme/index.ts,文件路径和拓展名也许会有区别)中引入这个组件,并注册

js
...
import Memos from './components/memos.vue'
...
export default {
    ...
    enhanceApp({ app }) {
        ...
        app.component('Memos', Memos);// [!code highlight]
    }
} satisfies Theme

这样在博客的任何地方,都可以通过 <Memos /> 直接引入这个组件了

最后就是创建一个单页,专门用于放置这个组件

什么?你说你从来没有在 vitepress 中使用过单页?

这样,你先在根目录下新建一个 pages 文件夹,再在 VitePress 核心配置文件中(注意不是主题配置文件,通常为 docs/.vitepress/config.ts,文件路径和拓展名也许会有区别)中新增一个 rewrites 规则 'pages/:file.md': ':file.md',这样 pages 下的内容都可以直接通过 /文件名 访问了。关于 rewrites,见 官方文档

pages 文件夹下新建 balabala.md,内容为

markdown
---
title: 碎碎念
hidden: true
comment: false
sidebar: false
aside: false
readingTime: false
showMeta: false
---

<Memos />

完事收工

VPS 套 Warp 分流指定出口 IPv6
实现 OPNsense 透明代理+分流
读完了?给这篇文章一个反馈

评论区

还没有人说话来做第一个