夏日幽灵 サマーゴースト (2021)
1004 字
5 分钟
fuwari(Astro) 添加评论功能实践记录
背景
Fuwari 是基于 Astro + Svelte 的博客主题,默认没有评论模块。为了保留静态站点的性能与部署体验,我选择集成自建的 Twikoo 服务。本篇记录最终方案、遇到的问题和改善建议。
目标与约束
- 运行环境:Astro + Fuwari(Svelte 渲染层),部署到 Cloudflare Pages。
- 评论服务:Twikoo(腾讯云云函数部署,兼容 LeanCloud)。
- 要求:
- 首次进入文章页即可渲染评论,无需刷新;
- 不影响静态渲染,尽量避免打破 Astro 的 islands 架构;
- 支持暗色模式、移动端适配;
- 保留无评论时的占位文案,避免布局突兀。
Twikoo 部署概要
- 准备数据库:我使用腾讯云云函数 + 云开发环境
env-xxx。 - 部署云函数:按照官方指引上传
twikoo,记录部署后的envId。 - 配置域名:自定义域名 + HTTPS,方便前端调用。
- 后台初始化:在云开发控制台中创建管理员用户、配置默认主题色。
如果不想维护服务,可选择第三方托管,但需关注隐私与速率限制。
在 Fuwari 中引入评论组件
Fuwari 的文章详情使用 Svelte 组件 Post.astro。为了保持 SSR 与事件绑定,我采用“前端懒加载”的做法:
- 新增 Twikoo 入口组件
src/components/post/TwikooComments.svelte:
<script lang="ts">
import { onMount } from 'svelte';
export let envId: string;
export let path: string;
export let darkSelector = 'html[data-theme="dark"]';
let container: HTMLDivElement | null = null;
let twikooLoaded = false;
const loadTwikoo = async () => {
if (twikooLoaded || !container) return;
twikooLoaded = true;
const { default: twikoo } = await import('twikoo');
await twikoo.init({
envId,
el: container,
path,
lang: 'zh-CN',
theme: matchMedia(`(prefers-color-scheme: dark)`).matches ? 'dark' : 'light',
});
};
onMount(() => {
loadTwikoo();
const observer = new MutationObserver(() => {
const isDark = document.querySelector(darkSelector);
container?.setAttribute('data-twikoo-theme', isDark ? 'dark' : 'light');
});
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ['data-theme'],
});
return () => observer.disconnect();
});
</script>
<div bind:this={container} class="tk-container">
<p class="tk-loading">评论加载中...</p>
</div>
<style>
.tk-container {
margin-top: 3rem;
}
.tk-loading {
text-align: center;
color: var(--muted-foreground, #888);
font-size: 14px;
}
</style>
- 在
Post.astro中注入组件:
---
import TwikooComments from '@/components/post/TwikooComments.svelte';
const { frontmatter } = Astro.props;
const twikooEnvId = Astro.site ? 'your-env-id' : import.meta.env.PUBLIC_TWIKOO_ENV_ID;
---
<article>
<!-- 文章内容 -->
</article>
<TwikooComments client:idle envId={twikooEnvId} path={Astro.url.pathname} />
client:idle确保组件在空闲时加载,避免阻塞首屏。path传入文章路径用于评论定位。envId可以通过环境变量注入。
解决“首次进入不渲染”的问题
一开始我把 Twikoo 的初始脚本直接写在 client:load 的组件里,结果在首次导航到文章页时不会执行,必须刷新才出现。这与 Astro 的 SPA 切换有关:页面通过 @astrojs/view-transitions 保留 DOM,导致 Twikoo 初始化被错过。
最终改进方案:
- 监听路由切换:Fuwari 使用客户端导航,需在
onMount中关心Astro.url.pathname。 - 使用
Astro.props动态变更path:当文章切换时,path随 props 更新,组件重新初始化。 - 自定义事件重载:如果妙用
<ViewTransitions />,可以在切换时派发事件:
document.addEventListener('astro:page-load', () => {
twikooLoaded = false;
loadTwikoo();
});
Fuwari 目前(2024.11)没有暴露事件,我通过在 src/layouts/Base.astro 添加:
<script>
document.addEventListener('astro:page-load', () => {
window.dispatchEvent(new CustomEvent('fuwari:page-loaded'));
});
</script>
然后在组件里监听:
onMount(() => {
const rerender = () => {
twikooLoaded = false;
loadTwikoo();
};
window.addEventListener('fuwari:page-loaded', rerender);
loadTwikoo();
return () => window.removeEventListener('fuwari:page-loaded', rerender);
});
这样任何一次页面跳转都会重新初始化 Twikoo,解决首屏不显示的问题。
样式与暗色模式
Twikoo 提供 data-theme 属性控制主题。Fuwari 使用 data-theme="dark" 切换,我在 MutationObserver 里同步这个状态:
- 切换时更新容器属性;
- 通过自定义 CSS 重写输入框、按钮的颜色;
- 在暗色模式下调低边框与阴影。
示例:
.tk-container[data-twikoo-theme='dark'] {
--twikoo-font-color: #d4d4d8;
--twikoo-card-bg: rgba(24, 24, 27, 0.6);
}
性能与懒加载
client:idle+await import('twikoo')组合确保在主线程空闲时加载脚本。- 可以进一步使用
IntersectionObserver,只有当评论区域进入视口时才加载。 - Twikoo 默认加载表情包等资源,必要时使用
twikoo.init({ reaction: false })关闭。 - 若担心 bundle 体积,考虑通过 CDN 注入
<script src=".../twikoo.min.js" defer>,再调用window.twikoo.init。
参考资料
fuwari(Astro) 添加评论功能实践记录
https://bangwu.top/posts/fuwari-comment/

