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
评论