文章摘要
加载中...|
此内容根据文章生成,并经过人工审核,仅用于文章内容的解释与总结

Twikoo 评论区深度定制实录:从默认布局到和参考站一模一样

博客评论区用的是 Twikoo,默认界面其实已经很简洁,但对比参考站(同款 Curve 主题的站点)总觉得差一口气:发送按钮位置不对、没有字数统计、没有评论数量角标、表情包加载不出来。这篇记录我踩过的坑和最终实现。

一、布局对齐:发送按钮该在哪?

Twikoo 默认的提交区结构是:输入框 → 操作行(表情/图片图标 + 发送按钮)→ 昵称/邮箱/网址表单。

参考站的布局是:输入框 → 操作行(图标在左、发送按钮在右)→ 表单,发送按钮和图标在同一行右侧。

一开始我尝试用 JS 把发送按钮挪到表单行右侧:

js
// 错误示范:发送按钮被塞进表单行,3 个输入框把按钮挤到中间错位
const send = document.querySelector(".tk-submit .tk-send");
const meta = document.querySelector(".tk-submit .tk-meta-input");
meta.after(send);

结果表单行有昵称/邮箱/网址三个输入框(都是 flex:1),发送按钮被挤到下一行中间,非常难看。正确做法是保持 Twikoo 默认结构,只用 CSS 控制:

scss
.tk-submit .tk-row.actions {
  display: flex !important;
  align-items: center !important;
  .tk-row-actions-start { flex: 1; display: flex; align-items: center; gap: 4px; }
}
.tk-submit .tk-send {
  margin-left: auto;              // 靠右
  background: #f2f3f5 !important; // 灰色圆角
  border-radius: 8px !important;
}

手机端再把表单竖排、发送按钮保持右侧,两端都正常。

二、字数统计 0/10000 的坑

参考站输入框右下角显示 0/10000,而 Twikoo 默认只显示 0/500LIMIT_LENGTH 未配置时的默认值)。

这个默认值藏在 Twikoo 的源码里:

js
maxLength() {
  let e = parseInt(this.config.LIMIT_LENGTH);
  return Number.isNaN(e) && (e = 500), e > 0 ? e : null;
}

我在前端给 textarea 设 maxlength=10000 不管用——Twikoo 是 Vue 渲染的,属性会被组件状态覆盖。最彻底的办法:因为我把 Twikoo 的 JS 本地化到了站点(/js/twikoo.min.js,不依赖官方 CDN),直接改本地文件里的默认值 500 → 10000。构建部署后所有页面统一显示 0/10000

提示:前端 0/10000 是显示上限,如果后端 LIMIT_LENGTH 配置了更小的值,提交超长仍会被后端拒绝,需要时去管理面板同步配置。

三、评论数量角标:两个隐蔽的坑

标题栏要显示「评论 N」的深色角标,用 Twikoo 的 getCommentsCount 接口。踩了两个坑:

坑 1:返回结构是数组不是对象。

js
// 错误:以为返回 { url: count }
const count = res[path];

// 正确:返回的是数组 [{ url, count }]
const list = Array.isArray(res) ? res : [];
const hit = list.find((i) => candidates.includes(i.url));

坑 2:存储的 URL 和页面地址可能差一个 .html 后缀。

Twikoo 存评论时 URL 是 /pages/link,但页面地址是 /pages/link.html,直接匹配永远查不到。解决办法是多候选匹配:

js
const candidates = [path];
if (path.endsWith(".html")) candidates.push(path.slice(0, -5));
else candidates.push(path + ".html");

角标样式用深色圆角矩形,数字用 line-height 撑满 + 微调才真正做到垂直居中(默认 line-height:1 会让数字视觉偏上)。

四、匿名评论:随机昵称 + 匿名邮箱

参考站有「匿名评论」开关,点击后弹确认框,开启后自动填入随机昵称和匿名邮箱(形如 海盐79 / anon9386751@proton.me)。

实现要点:

  1. 确认弹窗用 Teleport 挂到 body,避免组件 scoped 样式失效;
  2. 昵称从词库里随机组合(形容词 + 数字),邮箱用固定的匿名域名;
  3. 输入框填充要轮询等待——评论区是异步渲染的,输入框可能还没出现,直接赋值会丢;
  4. 不要autofocus 聚焦输入框,手机端聚焦会触发页面放大,体验很差。

五、Bilibili 表情包加载失败:本地化解决

表情面板有三个标签:颜文字(文本)、Emoji(Unicode)、Bilibili(图片)。前两个正常,Bilibili 全是「友」字占位图——表情图片挂在 owo.imaegoo.com,国内直连不稳定。

排查过程:

bash
# 表情配置文件本身在 owo.imaegoo.com/owo.json
# 里面的图片 URL 是 https://owo.imaegoo.com/bilibili/xxx.png

解决办法:全部本地化。 下载 owo.json 和 21 张表情图到站内 public/emoji/,把 json 里的图片 URL 改成站内路径 /emoji/bilibili/xxx.png,再在 Twikoo 初始化时指定:

js
Twikoo.init({
  envId: "https://你的域名/",
  emoji: "/emoji/owo.json",   // 表情数据走站内,不再依赖外部 CDN
});

图片全站打包部署到 GitHub Pages,国内访问秒开,彻底告别「友」字占位。

小结

评论区定制的核心思路:能不改源码就不改,CSS 优先;必须改默认值就改本地化的 JS。遇到问题先看元素结构(F12 找真实 class),别凭感觉写选择器。整套流程跑下来,评论区从"能用"变成"和参考站一模一样"。

评论 匿名评论 隐私政策