开发者适配

评论组件 Next 支持完整评论区,也支持只调用评论框,由主题或其他插件自行实现评论列表。独立评论框继续处理登录状态、匿名信息、编辑器、图片上传、验证码和提交,接入方只需关联内容、设置回复目标并刷新自己的列表。

版本与资源

运行环境为 Halo 2.27.0 及以上,站点需安装并启用评论组件 Next。

本页的 show-comments="false" 字符串处理、replyTarget、replyTo()、cancelReply() 和 created 事件从 1.1.1 开始提供。接入时安装 1.1.1 或更高的兼容版本,并保持插件与前端资源版本一致;仅清理浏览器缓存不会给旧版安装包增加这些能力。

完整评论区

普通主题推荐沿用 Halo 的 <halo:comment /> 扩展点,遵守站点和内容的评论开关。主题切换评论组件后,无需修改输入框或列表实现。

文章页:

<div th:if="${haloCommentEnabled}">
  <halo:comment
    group="content.halo.run"
    kind="Post"
    th:attr="name=${post.metadata.name}"
  />
</div>

独立页面:

<div th:if="${haloCommentEnabled}">
  <halo:comment
    group="content.halo.run"
    kind="SinglePage"
    th:attr="name=${singlePage.metadata.name}"
  />
</div>

name 使用内容的 metadata.name,不是标题、访问路径或数据库编号。其他插件的内容类型应传入其真实的 group、kind、name,并先接入 Halo 评论支持。

主题需保留 Halo 头部资源处理流程。插件会加载 comment-next.css 与 comment-next.iife.js,并将标准评论位渲染为 <comment-widget>。标准标签只透传当前实现支持的内容参数;下文的独立模式直接使用 Web Component,不能简单地将 show-comments 加在 <halo:comment /> 上。

独立评论框

通过 show-comments="false" 关闭内置列表:

<comment-widget
  id="custom-comment-composer"
  group="content.halo.run"
  kind="Post"
  version="v1alpha1"
  name="post-xxx"
  show-comments="false"
></comment-widget>

此时只显示评论框,不渲染或请求内置评论列表,也不启用文章互动条和片段评论入口。登录、上传、表情、验证码、AI 和审核规则仍沿用插件及站点配置。独立页面改用 kind="SinglePage";调用方仍需遵守内容的评论开关。

<comment-widget> 与 <comment-next> 使用相同的参数和方法。同一内容应只放置自己需要的评论区,避免同时渲染标准完整评论区和独立评论框而出现重复入口。

参数

HTML 属性 / JS 属性默认值用途
group空字符串内容所属 API 组,文章和独立页面使用 content.halo.run
kind空字符串内容类型,例如 Post、SinglePage
name空字符串内容的 metadata.name
versionv1alpha1内容 API 版本
show-comments / showCommentstrue是否附带内置评论列表;设为 false 仅显示评论框
reply-target / replyTarget未设置回复目标对象,未设置时发表主评论
base-url / baseUrl当前站点请求 Halo API 的站点根地址,通常保持默认
placeholder写下你的评论...默认输入提示,插件设置中的占位文字优先
auto-focus / autoFocusfalse初始化时尝试聚焦,异步加载后也可调用 setFocus()

可以用 composer.showComments = false 动态关闭列表,设回 true 或移除 show-comments 属性后恢复。HTML 的 show-comments="false" 与 JS 的布尔值 false 均有效。

其他布尔参数沿用 Web Component 的布尔属性语义:写出属性即表示开启,字符串 "false" 仍可能表示开启。需要关闭 showAccountFields、showFooter、showSubmitArea 或 allowImages 等参数时,使用 JS 属性赋值 false,不要据此类推其他 HTML 属性的字符串行为。

加载资源

Halo 主题页由插件自动注入资源。独立前端和 Vue 页面则需要加载与服务端配套的前端构建。例如,在同源页面显式引入已安装插件的资源:

<link
  rel="stylesheet"
  href="/plugins/PluginCommentNext/assets/static/comment-next.css"
/>
<script
  defer
  src="/plugins/PluginCommentNext/assets/static/comment-next.iife.js"
></script>

若前端工程已经依赖配套构建的 @xhhao/comment-next,可以沿用工程内的导入方式:

import '@xhhao/comment-next';
import '@xhhao/comment-next/comment-next.css';

两种加载方式选择一种。资源发布与缓存策略由接入工程维护,不能假定 npm 上任意版本都包含本页介绍的能力。

设置与取消回复

Halo 评论分为主评论与回复。回复某条回复时,仍归属于原主评论,通过 replyName 表明被回复的目标,不创建无限层级的新主评论。

replyTarget 字段必填说明
commentName是所属主评论的 metadata.name
replyName否被回复的回复的 metadata.name;回复主评论时省略,提交时作为 quoteReply
displayName否“回复 @昵称”的展示文案,不作为身份依据

自定义列表点击回复时,向当前评论框实例传递目标即可,不必把评论内容复制进编辑器:

await customElements.whenDefined('comment-widget');
const composer = document.querySelector('#custom-comment-composer');

// 也可在组件挂载前通过属性指定初始目标。
composer.replyTarget = {
  commentName: 'comment-xxx',
  displayName: '主评论作者',
};

// 在自定义列表的点击事件中调用:回复某条回复。
function replyToNestedComment() {
  return composer.replyTo({
    commentName: 'comment-xxx',
    replyName: 'reply-yyy',
    displayName: '被回复者',
  });
}

function cancelReply() {
  return composer.cancelReply();
}
方法行为
replyTo(target)校验并切换回复目标,同时尝试聚焦;返回是否成功切换
cancelReply()清除回复目标,恢复发表主评论;返回是否成功操作
setFocus()聚焦当前评论框
reset()清空编辑内容和片段引用,保留回复目标;需要取消目标时单独调用 cancelReply()

replyTo() 和 cancelReply() 在组件未就绪、正在提交、上传或验证码校验期间返回 false,调用方可稍后由用户重试。customElements.whenDefined() 只保证标签已注册,不保证站点配置加载完成;初始目标用属性赋值,交互时优先调用方法。

切换或取消回复会保留已有草稿和匿名信息。提交失败保留草稿及目标,成功后清空内容并退出本次回复状态。回复模式沿用现有规则,不提供独立私密回复开关。

提交成功事件

在当前元素监听 created 事件,让自定义列表自己刷新:

composer.addEventListener('created', (event) => {
  const detail = event.detail;

  console.log(detail.type, detail.commentName, detail.result.id);
  // 在此重新请求自己的列表,按 detail.subject 定位内容。
  // detail.result.approved === false 时,显示“已提交,审核后可见”。
});
event.detail 字段含义
typecomment 或 reply
subject本次内容归属:{ group, kind, name, version }
commentName新主评论 ID,或本次回复所属的主评论 ID
replyTarget本次回复目标;发表主评论时未设置
result组件归一化后的评论或回复,包含 id、content、approved、author 等字段

事件从当前组件发出,bubbles 和 composed 都为 true。同一页面有多个实例时,应监听具体元素或按 subject 过滤,避免刷新其他内容的列表。result.id 是组件返回值;下文公开 API的原始 JSON 使用 metadata.name,不要混用字段。

提交成功表示服务端已接收,不保证立即公开。自定义列表应继续遵守审核与私密可见性规则;内容尚未出现在列表时,可稍后重新读取,不能把待审核内容直接当作对所有访客公开。

Vue 接入

Vue 页面可以直接使用上述 Web Component,不需要引入 Svelte 组件源码。先在 Vue 编译配置中声明自定义标签,例如在 Vite 工程的 @vitejs/plugin-vue 中配置:

vue({
  template: {
    compilerOptions: {
      isCustomElement: (tag) =>
        tag === 'comment-widget' || tag === 'comment-next',
    },
  },
});

在自己的页面中使用评论框。下面假定前述配套资源已加载;监听组件本身的事件即可刷新 Vue 列表:

<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref } from 'vue';

const composer = ref<HTMLElement>();

function handleCreated(event: Event) {
  const { subject, result } = (event as CustomEvent).detail;
  console.log('刷新对应内容的评论列表', subject, result.id);
}

onMounted(() => composer.value?.addEventListener('created', handleCreated));
onBeforeUnmount(() => composer.value?.removeEventListener('created', handleCreated));
</script>

<template>
  <comment-widget
    ref="composer"
    group="content.halo.run"
    kind="Post"
    name="post-xxx"
    show-comments="false"
  ></comment-widget>
</template>

后台纯编辑模式

后台需要自行处理保存时,可以沿用插件 Vue 后台的 editorOnly 模式:

editor.editorOnly = true;
editor.allowImages = true;
editor.showAccountFields = false;
editor.showFooter = true;
editor.showSubmitArea = false;
editor.allowAnonymous = false;
editor.showCaptcha = false;
editor.enablePrivate = false;

editor.addEventListener('update', (event) => {
  const { content, characterCount } = event.detail;
  // 将 content 保存到自己的表单,提交时由调用方处理。
});

这段配置应在元素初始化时应用;editor 指向 <comment-next> 或 <comment-widget> 元素。editorOnly 用于宿主自行保存内容,update 返回 content 与 characterCount。前台想保留完整提交流程时,使用 show-comments="false",不要同时开启 editorOnly。

配色与暗色模式

评论框使用 Shadow DOM,优先通过 CSS 变量调整外观。可在主题或当前组件上设置稳定变量:

#custom-comment-composer {
  --halo-cw-primary-1-color: #2563eb;
  --halo-cw-text-1-color: #172033;
  --halo-cw-base-rounded: 0.75rem;
  --halo-cw-avatar-rounded: 9999px;
  --halo-cw-avatar-size: 36px;
}

深色外观可跟随 .color-scheme-dark、.dark 或 [data-color-scheme="dark"] 容器。避免依赖内部 DOM 和类名覆盖样式,以便升级组件。

自定义评论列表与读者墙

  • 自己画评论列表:使用评论与回复查询,将原始数据的 metadata.name 传给 replyTarget。
  • 自己画读者墙:使用读者榜 API,读取头像、网站、获赞数、回复数和邮箱哈希。
  • 直接使用插件榜单:可以在主题放置 <comment-ranking> 或 <comment-ranking-sidebar>;参数见榜单组件接入。

同源部署通常无需指定 baseUrl。跨域接入还需要匹配的登录凭证、CORS 和 HTTPS 配置,单独设置 baseUrl 不会自动完成跨域授权或登录跳转适配。