4148 字
21 分钟
Fuwari博客搭建和添加Twikoo评论

博客#

前提:

1.git

2.github帐号

3.cloudflare帐号

折腾了这么久,博客和该死的评论终于弄完了.

Fuwari博客还算简单.

参考链接

二叉树树的fuwari搭建教程

saicaca
/
fuwari
Waiting for api.github.com...
00K
0K
0K
Waiting...

这是fuwari的github仓库,点击fork(分叉),Repository name(仓库名)随便,Description(介绍)随便,取消勾选Copy the main branch only(仅复制主分支),点击Create fork.

在命令行中执行以下命令:

Terminal window
git clone <你分叉的仓库地址>
npm install -g pnpm
cd 你分叉的仓库的仓库名
pnpm install #不要运行pnpm add sharp

改写Fuwari的基本信息#

我们先来改src/config.ts文件,这个文件算是基础设置吧

src/config.ts
import type {
ExpressiveCodeConfig,
LicenseConfig,
NavBarConfig,
ProfileConfig,
SiteConfig,
} from "./types/config";
import { LinkPreset } from "./types/config";
export const siteConfig: SiteConfig = {
title: "Fuwari",//你的博客主标题
subtitle: "Demo Site",//你的博客副标题.可选,在浏览器标签那里会显示为"主标题 - 副标题"
lang: "en", // 博客显示语言 'en', 'zh_CN', 'ja','ko'.
themeColor: {
hue: 250, //你的博客主题色,可以在你的博客右上角的画板图标确定喜欢的颜色再填写
fixed: false, // 为访客隐藏主题颜色选择器
},
banner: {
enable: false,//是否开启,如果要开启,请设置为true
src: "assets/images/demo-banner.png", //即banner图片,支持http/https URL,相对于/src目录。如果以'/'开头,相对于/public目录
position: "center", // 图片位置,仅支持 'top'、'center'、'bottom'。默认为 'center'
credit: {
enable: false, // 显示图片的版权文本
text: "", // 要显示的文本
url: "", // (可选)指向原始艺术品或艺术家页面的链接
},
},
toc: {
enable: true, // 在文章右侧显示大纲
depth: 2, // Maximum heading depth to show in the table, from 1 to 3
},
favicon: [//即网站图标,支持http/https URL
// 留空此数组以使用默认的favicon
// {
// src: '/favicon/icon.png', // favicon的路径,相对于/public目录
// theme: 'light', // (可选)'light' or 'dark'(“亮”或“暗”),仅当您对亮模式和暗模式有不同的收藏夹图标时才设置
// sizes: '32x32', // (可选)favicon的大小,仅当您有不同大小的favicon时才设置
// }
],
};
export const navBarConfig: NavBarConfig = {//导航栏设置的超链接
links: [//这些链接在导航栏上,在关于右边
LinkPreset.Home,
LinkPreset.Archive,
LinkPreset.About,
{
name: "GitHub",
url: "https://github.com/saicaca/fuwari", // 内部链接不应包含基本路径,因为它会自动添加
external: true, // 显示外部链接图标,并将在新选项卡中打开
},
],
};
export const profileConfig: ProfileConfig = {//你的用户的超链接
avatar: "assets/images/demo-avatar.png", // 相对于/src目录。如果以'/'开头,相对于/public目录
name: "Lorem Ipsum",//你的名字
bio: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",//即个性签名,会显示在头像和名字下面
links: [
{
name: "Twitter",
icon: "fa6-brands:twitter", // 访问 https://icones.js.org/ 获取图标代码
// 如果尚未包含,您需要安装相应的图标集
// `pnpm add @iconify-json/<图标集名称>`
//比如QQ,则填写 fa6-brands:qq ,如图。Fuwari默认支持这几种类型:fa6-brands, fa6-regular, fa6-solid, material-symbols。可以在 astro.config.mjs 中搜索关键字进行配置
url: "https://twitter.com",
},
{
name: "Steam",
icon: "fa6-brands:steam",
url: "https://store.steampowered.com",
},
{
name: "GitHub",
icon: "fa6-brands:github",
url: "https://github.com/saicaca/fuwari",
},
],
};
export const licenseConfig: LicenseConfig = {
enable: true,
name: "CC BY-NC-SA 4.0",
url: "https://creativecommons.org/licenses/by-nc-sa/4.0/",
};
export const expressiveCodeConfig: ExpressiveCodeConfig = {
// Note: Some styles (such as background color) are being overridden, see the astro.config.mjs file.
// Please select a dark theme, as this blog theme currently only supports dark background color
theme: "github-dark",
};

写作#

清理src/content/posts目录下的多余文件

首先,在项目根目录执行:

Terminal window
pnpm new-post <这里填写你的文章标题>

然后,在根目录下的src/content/posts文件夹中会多出一个 xxx.md文件

我们使用MarkText打开这个文件,你可以看到一些基本信息,我们只需要关注几个重要的信息

xxx.md
---
title: xxx
published: 2024-10-14
description: ''
image: ''
tags: []
categories: ''
draft: false
lang: ''
---
title:文章标题
published:文章创建时间
description:文章描述,正常会显示在文章标题下面
image:文章封面图(同目录需要写 ./ 如:./https://xxxx/myblog/img/2024-10-14-11-33-28-image.webp)
tag:文章标签
categories:文章分类
draft: 是否为草稿
pinned: 是否置顶文章
lang: 文章语言,与src/config.ts中设置的不同的话要设置一下

更改仓库根目录下的astro.config.mjs。在第34行更改 stie: 为你的站点URL,如: site: “https://blog.com”

预览#

在项目根目录执行

Terminal window
pnpm dev

发布#

设置用户名和邮箱

Terminal window
git config --global user.name "你的Github用户名"
git config --global user.email "你的Github邮箱@example.com"

更改远程仓库为ssh*(如果是通过ssh克隆的不用改)

Terminal window
git remote set-url origin git@github.com:xxx/xxx

提交所有文件

Terminal window
git add .

让我们发布一个本地提交

Terminal window
git commit -m "项目初始化"

让我们将本地更改提交到远程仓库

Terminal window
git push

接下来我们用大善人Cloudflare来构建你的博客

打开Cloudflare,点击计算和 AI,点击 Workers 和 Pages,点击创建应用程序

点击想要部署 Pages后的开始使用

然后选择连接Git存储库,连接你的Github,随后设置

  • 构建命令:pnpm build
  • 输出目录:dist

绑定自定义域,访问自定义域即可访问你的博客!

之后只需要写文章推送至github就行,大善人会帮你自动构建

cloudflare部署玄学问题#

前面写道大善人会帮你自动构建,但是会遇到玄学问题,构建会概率性出现问题,日志如下

错误日志

正常日志

Build fails on [vite] [postcss]参考一下

这个要么重复构建,要么在src/styles/markdown.css的最顶部添加以下行,让构建通过

@import './main.css';

当然这或许并不是一个完美的解决办法.

添加Twikoo评论支持#

关于评论这里有两套方案

一个是Giscus,一个是我使用的Twikoo,giscus有个巨大的特点是使用它的博客评论的时候需要登陆github,而twikoo不要.

接下来介绍使用twikoo给fuwari提供评论支持.

参考链接

云函数部署

这是官方的教程,可以理解成twikoo存储数据的方式.

大概分为docker和后端加数据库两种.

docker可以使用Zeabur或者私有部署.#

Docker

Terminal window
docker run --name twikoo -e TWIKOO_THROTTLE=1000 -p 8080:8080 -v ${PWD}/data:/app/data -d imaegoo/twikoo

Docker Compose

version: '3'
services:
twikoo:
image: imaegoo/twikoo
container_name: twikoo
restart: unless-stopped
ports:
- 8080:8080
environment:
TWIKOO_THROTTLE: 1000
volumes:
- ./data:/app/data

zeabur

镜像:imaegoo/twikoo
环境变量:TWIKOO_THROTTLE: 1000
端口:8080
卷:/app/data
(资源限制 内存 (Mi)64)

docker部署完了之后,zeabur设置域名访问即可.

私有部署#

Terminal window
cd /opt
mkdir twikoo
cd twikoo
nano docker-compose.yml

docker-compose.yml内容如下:

docker-compose.yml
version: '3'
services:
twikoo:
image: imaegoo/twikoo
container_name: twikoo
restart: unless-stopped
ports:
- 8080:8080
environment:
TWIKOO_THROTTLE: 1000
volumes:
- ./data:/app/data

这里参考Astro | 为 Fuwari 主题添加 Twikoo 评论,我没用这种方法就不写了.

Vercel 部署#

我用的方案是官方方案中的vercel方案,使用vercel作后端,MongoDB作数据库.

Vercel 部署

1.准备 MongoDB 数据库#

参考链接:

申请 MongoDB Atlas 账号,获取 MongoDB 连接字符串

MongoDB Atlas 是 MongoDB Inc 提供的 MongoDB 数据库托管服务。免费账户可以永久使用 500 MiB 的数据库,足够存储 Twikoo 评论使用。

  1. 申请 MongoDB AtLas 账号
  2. 创建免费 MongoDB 数据库,区域推荐选择离 Twikoo 后端(Vercel / Netlify / AWS Lambda / VPS)地理位置较近的数据中心以获得更低的数据库连接延迟。如果不清楚自己的后端在哪个区域,也可选择 AWS / Oregon (us-west-2),该数据中心基建成熟,故障率低,且使用 Oregon 州的清洁能源,较为环保
  3. 在 Database Access(数据库访问) 页面点击 Add New Database User(添加新数据库用户) 创建数据库用户,Authentication Method(认证方式) 选 Password(密码),在 Password Authentication(密码验证) 下设置数据库用户名和密码,建议点击 Auto Generate(自动生成) 自动生成一个不含特殊符号的强壮密码并妥善保存。点击 Database User Privileges(数据库用户权限) 下方的 Add Built In Role(添加内置角色),Select Role (选择角色)选择 Read and write to any database(读取和写入任何数据库),也可以试试Specific Privileges(特定权限),最后点击 Add User(添加用户).
  4. 在 Network Access(网络访问) 页面点击 Add IP Address(添加 IP 地址) 添加网络白名单。因为 Vercel / Netlify / Lambda 的出口地址不固定,因此在 Access List Entry(访问列表条目) 输入 0.0.0.0/0(允许所有 IP 地址的连接)即可。如果 Twikoo 部署在自己的服务器上,这里可以填入固定 IP 地址。点击 Confirm 保存
  5. 获取连接字符串,格式如下:
mongodb+srv://<用户名>:<密码>@cluster0.xxxx.mongodb.net/?retryWrites=true&w=majority&appName=Cluster0

2. 部署到 Vercel#

  1. 申请 Vercel 账号
  2. 点击按钮将 Twikoo 一键部署到 Vercel Deploy
  3. 进入 Settings(设置) - Environment Variables(环境变量),添加环境变量 MONGODB_URI,值为前面记录的数据库连接字符串

默认的连接字符串没有指定数据库名称,Twikoo 会连接到默认的名为 test 的数据库.如果需要在同一个 MongoDB 里运行其他业务或供多个 Twikoo 实例使用,建立加入数据库名称并配置对应的 ACL.

mongodb+srv://<db_username>:<db_password>@cluster0.xxxx.mongodb.net/?retryWrites=true&w=majority&appName=Cluster0)

上面这个就是默认的.

如果要设置数据库名称则在cluster0.xxxx.mongodb.net/后面指定数据库名称,如下面的.

mongodb+srv://<db_username>:<db_password>@cluster0.xxxx.mongodb.net/twikoo_fuwari?retryWrites=true&w=majority&appName=Cluster0
  1. 进入 Settings(设置) - Deployment Protection(部署保护),设置 Vercel Authentication(Vercel身份验证) 为 Disabled(禁用),并 Save(保存)
  2. 进入 Deployments(部署) , 然后在任意一项后面点击更多(三个点) , 然后点击 Redeploy(重新部署) , 最后点击下面的 Redeploy(重新部署)
  3. 进入 Overview(概览),点击 Domains(域名) 下方的链接,如果环境配置正确,可以看到 “Twikoo 云函数运行正常” 的提示

Vercel Domains(域名)(包含 https:// 前缀,例如 https://xxx.vercel.app)即为您的环境 id

这里提醒一下vercel的域名在国内无法访问,记得把域名换掉.

vercel是后端,vercel的项目域名是后面的envId,MongoDB是数据库,数据库连接字符串能让后端访问数据库.

vercel弄完了之后,我们来为Fuwari添加Twikoo评论支持

参考链接

Fuwari 主题魔改 - 给你的Fuwari添加Twikoo评论支持

Astro | 为 Fuwari 主题添加 Twikoo 评论

在项目目录src/components/comment下创建Twikoo.astro文件

twikoo官方文档中cdn引入方式文本

<div id="tcomment"></div>
<script src="https://cdn.jsdelivr.net/npm/twikoo@1.6.44/dist/twikoo.min.js"></script>
<script>
twikoo.init({
envId: '您的环境id', // 腾讯云环境填 envId;Vercel 环境填地址(https://xxx.vercel.app)
el: '#tcomment', // 容器元素
// region: 'ap-guangzhou', // 环境地域,默认为 ap-shanghai,腾讯云环境填 ap-shanghai 或 ap-guangzhou;Vercel 环境不填
// path: location.pathname, // 用于区分不同文章的自定义 js 路径,如果您的文章路径不是 location.pathname,需传此参数
// lang: 'zh-CN', // 用于手动设定评论区语言,支持的语言列表 https://github.com/twikoojs/twikoo/blob/main/src/client/utils/i18n/index.js
})
</script>

这个官方引入方式修改如下写入Twikoo.astro文件

src/components/comment/Twikoo.astro
---
import { commentConfig } from "@/config";
interface Props {
path: string;
}
const config = {
...commentConfig.twikoo,
el: "#tcomment",
path: Astro.props.path,
};
---
<div id="tcomment"></div>
<script is:inline src="https://cdn.staticfile.org/twikoo/1.6.44/twikoo.all.min.js"></script><!--这个链接里的版本号要改成你后端的版本号-->
<script is:inline define:vars={{ config }}>
twikoo.init(config)
</script>

在src/components/comment路径下新建index.astro

src/components/comment/index.astro
---
import type { CollectionEntry } from "astro:content";
import { commentConfig } from "@/config";
import Twikoo from "./Twikoo.astro";
interface Props {
post: CollectionEntry<"posts">;
}
const { id, data, slug } = Astro.props.post;
const path = `/posts/${slug}`;
const url = `${Astro.site?.href}${path}`;
let commentService = "";
if (commentConfig?.twikoo) {
commentService = "twikoo";
}
---
<div class="card-base p-6 mb-4">
{commentService === 'twikoo' && <Twikoo path={path} />}
{commentService === '' && null}
</div>

然后在 Fuwari 主题的配置文件src/config.ts中的最后添加下述代码来引入commentConfig加载配置:

src/config.ts
export const expressiveCodeConfig: ExpressiveCodeConfig = {
// Note: Some styles (such as background color) are being overridden, see the astro.config.mjs file.
// Please select a dark theme, as this blog theme currently only supports dark background color
theme: "github-dark",
};
export const commentConfig: CommentConfig = {
twikoo: {
envId: "https://xxx",//您的环境id
lang: "zh-CN",
},
};

envId这一项填写你的envId,非腾讯云环境的话就是 twikoo 后端服务的域名.

修改src/pages/posts/[…slug].astro将 Twikoo 使用的页面路径引入.在顶部也要引入一下index.astro来加载 twikoo 等评论系统可显示的页面配置.

[...slug].astro
---
import path from "node:path";
import Comment from "@components/comment/index.astro"; // twikoo评论
import License from "@components/misc/License.astro";
import Markdown from "@components/misc/Markdown.astro";
import I18nKey from "@i18n/i18nKey";
.....
</div>
</div>
<Comment post={entry}></Comment>
<!-- twikoo评论 -->
<div class="flex flex-col md:flex-row justify-between mb-4 gap-4 overflow-hidden w-full">
<a href={entry.data.nextSlug ? getPostUrlBySlug(entry.data.nextSlug) : "#"}
class:list={["w-full font-bold overflow-hidden active:scale-95", {"pointer-events-none": !entry.data.nextSlug}]}>

最后src/types/config.ts中加入

config.ts
export type LIGHT_DARK_MODE =
| typeof LIGHT_MODE
| typeof DARK_MODE
| typeof AUTO_MODE;
// twikoo评论
export type CommentConfig = {
twikoo?: TwikooConfig;
};
type TwikooConfig = {
envId: string;
region?: string;
lang?: string;
};
// twikoo评论结束

保存更改之后本地运行pnpm dev应该能看到posts页面下的文章都能够显示评论了,点击小齿轮图标可以进入管理页面.

这时试着发下评论,如果没问题的话,后端会在数据库里创建数据库.

魔改 Twikoo 样式#

新建src/styles/twikoo.css

src/styles/twikoo.css
:root {
--tk-text: black;
}
html.dark {
--tk-text: #d1d5db;
}
.tk-comments {
@apply text-[var(--tk-text)];
}
.tk-submit {
.tk-avatar {
134 collapsed lines
@apply hidden;
}
}
/* Text Area */
.tk-row {
.tk-col {
@apply flex-col-reverse;
.tk-input {
textarea {
@apply rounded-[var(--radius-large)] py-4 px-6 !min-h-[150px] focus:border-[var(--primary)];
}
}
}
}
/* meta input style */
.tk-meta-input {
@apply relative mt-3;
div {
@apply min-h-10;
.el-input-group__prepend {
@apply !bg-inherit rounded-l-lg;
min-height: inherit;
}
input {
@apply px-4 rounded-r-lg focus:!border-[var(--primary)];
min-height: inherit;
}
}
}
/* Button */
.tk-row.actions {
@apply w-full !ml-0 !mt-0;
.__markdown {
@apply !hidden;
}
.tk-preview,
.tk-send,
.tk-cancel {
@apply border-none rounded-lg px-3 py-0 h-8
!bg-[var(--btn-regular-bg-active)] disabled:!bg-[var(--btn-regular-bg)]
!text-[var(--btn-content)] disabled:!text-[#ffffffa1];
}
}
/* Comment title */
.tk-comments-title {
.__comments svg {
@apply fill-[var(--primary)];
}
}
.tk-comment {
@apply border-[1px] border-[rgba(144,147,153,0.31)] p-4 rounded-2xl hover:shadow-md transition-all;
.tk-action-icon svg {
@apply fill-[var(--primary)];
}
}
.tk-action {
.tk-action-count {
@apply text-[var(--btn-content)];
}
}
.tk-meta {
.tk-tag {
@apply border-none rounded-lg text-[var(--btn-content)];
}
.tk-tag-green {
@apply bg-[var(--btn-regular-bg)] dark:bg-[var(--primary)] dark:text-[var(--deep-text)];
}
}
.tk-content,
.tk-preview-container {
a {
@apply link link-underline text-[var(--primary)] font-medium;
}
.tk-ruser {
@apply no-underline;
}
:not(pre) > code {
@apply bg-[var(--inline-code-bg)] rounded-md text-[--inline-code-color] px-1 py-0.5 font-semibold;
}
li{
@apply before:content-['•'] before:text-[var(--primary)];
}
}
/* Replies */
.tk-replies {
.tk-comment {
@apply bg-[var(--page-bg)];
.tk-content {
> span:first-of-type {
@apply text-xs;
}
}
}
}
.twikoo .code-block {
pre {
@apply !rounded-xl;
}
.copy-btn-icon {
width: inherit !important;
height: inherit !important;
}
}
.tk-expand-wrap .tk-expand,
.tk-collapse-wrap .tk-expand {
@apply hover:rounded-lg mt-1 hover:bg-[var(--btn-plain-bg-hover)];
}
/*让表情组件在表情包数量太多的时候正确显示*/
.card-base {
overflow: visible;
}
/* 让图片类型表情不换行显示 */
.tk-content img, .tk-preview-container img {
display: inline;
vertical-align: bottom !important;
}

解决首次加载不显示#

修改src/layouts/Layout.astro

src/layouts/Layout.astro
// 专用于创建loadComment的事件,即通知评论组件进行一次加载
function initCommentComponent() {
const event = new Event("loadComment");
document.dispatchEvent(event);
}
// twikoo评论
function init() {
// disableAnimation()() // TODO
loadTheme();
loadHue();
initCustomScrollbar();
showBanner();
initCommentComponent(); // 调用initCommentComponent()函数,twikoo
}
....
window.swup.hooks.on('content:replace', initCustomScrollbar)
// window.swup.hooks.on('content:replace', initCustomScrollbar)
window.swup.hooks.on("content:replace", () => {
initCustomScrollbar();
initCommentComponent(); // 添加代码调用
});
window.swup.hooks.on('visit:start', (visit: {to: {url: string}}) => {
// change banner height immediately when a link is clicked
const bodyElement = document.querySelector('body')

为评论区添加小标题#

修改src/components/comment/Twikoo.astro

HyperCherry的代码我不知道为什么会报错,import WidgetLayout from “@components/widget/WidgetLayout.astro”;的引用没有效果.可能我是个废物吧.

src/components/comment/Twikoo.astro
---
import WidgetLayout from "@components/widget/WidgetLayout.astro";
import { commentConfig } from "@/config";
import I18nKey from "@i18n/i18nKey";
import { i18n } from "@i18n/translation";
interface Props {
...
path: Astro.props.path,
};
---
<WidgetLayout name={i18n(I18nKey.comments)} id="comments">
<div id="tcomment"></div>
</WidgetLayout>
<script is:inline src="https://cdn.staticfile.org/twikoo/1.6.44/twikoo.all.min.js"></script>
<script is:inline define:vars={{ config }}>
twikoo.init(config)

解决首次加载不显示,解决评论操作会回到文章顶部#

见 issue #721,Twikoo 加了一个href=”#“,这是为了防止回复按钮在某些主题(如 VuePress)下被识别为不可点击的链接而加的,不能被官方仓库删除.但是 Fuwari 正是这个href=”#“导致了问题.

解:重新编译.

Fork官方项目

仓库 Clone 到本地

全局删除href=”#”

编译出 js 文件(或vercel部署)

编译流程:

Terminal window
# 安装 yarn(如未安装)
sudo npm install -g yarn
# 安装依赖
yarn install
# 搜索并删除 twikoo 源码中的 href="#"(建议用编辑器全局替换)
# 比如 VS Code 搜索全项目替换为 "javascript:void(0)"或删除,我是都删除了
# 编译(非腾讯云用 twikoo.min.js,全部文件输出在./dist)
yarn build

vercel部署

输出目录:./dist

js文件: 域名/twikoo.min.js

修改src/components/comment/Twikoo.astro

src/components/comment/Twikoo.astro
---
import WidgetLayout from "@components/widget/WidgetLayout.astro";
import I18nKey from "@i18n/i18nKey";
import { i18n } from "@i18n/translation";
import { commentConfig } from "@/config";
interface Props {
path: string;
}
const config = {
...commentConfig.twikoo,
el: "#tcomment",
path: Astro.props.path,
};
---
<WidgetLayout name={i18n(I18nKey.comments)} id="comments">
<div id="tcomment"></div>
</WidgetLayout>
<script is:inline src="https://cdn.staticfile.org/twikoo/1.6.44/twikoo.all.min.js"></script>
<script is:inline define:vars={{ config }}>
twikoo.init(config)
<script define:vars={{ config }}>
function loadTwikoo() {
// 动态加载 Twikoo 脚本,并在加载完成后执行 twikoo.init
const script = document.createElement('script');
script.src = 'https://xxx/twikoo.min.js';//js文件
script.defer = true;
script.onload = () => {
if (window.twikoo) {
window.twikoo.init(config);
}
};
// script.onerror = () => {
// console.error('Twikoo script load failed');
// };
document.body.appendChild(script);
}
document.addEventListener("loadComment", loadTwikoo, { once: true }); // 监听加载评论事件,但是我们只能监听一次,从而避免多次触发.
</script>

Twikoo使用#

昵称:显示的名称

邮箱:去Gravatar.com注册账号绑定自己的域名邮箱,在这里填Gravatar的邮箱后会显示Gravatar的头像

网址:点击之后会跳转的网址

Twikoo 管理面板#

自定义通知邮件模板,自定义博主通知邮件模板用的Zokiio的.

Fuwari博客搭建和添加Twikoo评论
https://blog.speechess.dpdns.org/posts/build-fuwari-blog-and-twikoo-comments/
作者
speechess
发布于
2025-10-07
许可协议
CC BY-NC-SA 4.0
评论