当尝试在不提供服务器端渲染页面的单页应用程序 (SPA) 中实现 [1] 时,您最终会遇到问题,请参阅:[2]
因此,经过一些调整,我想提出以下方法。示例使用 Vue.js,但可以轻松改编到其他框架/库。
注意:我将使用术语 博客文章 来指代应嵌入 Discourse 评论的部分。当然,这也可以指您网站上的各个页面。
1. [1] 中的问题
1.1. javascripts/embed.js 无法与客户端渲染内容配合使用
因此,您在 [1] 中被告知插入到 HTML 中的 <script>...</script> 代码段将不会包含在我们正在处理的实现中。我们将利用 Discourse 实例提供的 javascripts/embed.js 的一些部分作为我们 SPA 中的函数。
1.2. Discourse 无法抓取客户端渲染内容
Discourse 会为每篇博客文章自动创建主题,并尝试访问(博客文章的)原始 URL 来确定标题和内容。这在 SPA 中会失败,因为 Discourse 将获取其非 JavaScript 部分,例如 We're sorry but this site doesn't work properly without JavaScript enabled. Please enable it to continue.
我们将使用 RSS 轮询插件来提供必要的数据并为我们创建主题。
2. 实现
2.1 RSS 轮询和 RSS/Atom feed
在您的网站上创建一个提供 RSS 或 Atom feed 的端点,供 RSS 轮询插件使用。此端点可以是静态 XML 格式文件,也可以是提供 XML 格式内容的服务器端函数,例如:
URL:https://mysite.com/blog.atom
内容:
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>My Site Blog Posts</title>
<link href="https://mysite.com/blog/"/>
<updated>2022-07-03T09:02:48.721Z</updated>
<id>urn:uuid:790c1857-b968-49cc-9fbd-bf7afe3552c2</id>
<entry>
<title>An Article about Technology</title>
<author>
<name>Your Name Here</name>
</author>
<link href="https://mysite.com/blog/an-article-about-technology"/>
<id>urn:uuid:f6cc13e4-d2eb-4385-af28-c867a94f48dc</id>
<published>2022-07-03T00:00:00Z</published>
<updated>2022-07-03T00:00:00Z</updated>
<summary>Let's discuss some technology in this article.</summary>
</entry>
</feed>
根据 [3] (Discourse 托管) 或 [4] (自托管) 安装 Discourse 的 RSS 轮询插件。
在 Admin → Settings → Plugins 中,RSS 轮询的推荐设置:
| 键 (Key) | 值 (Value) |
|---|---|
| rss polling enabled | true |
| rss polling frequency | 10 (即 10 分钟) |
在 Admin → Plugins → RSS Polling 的 RSS 轮询插件配置中添加一个新 feed。
根据 [3] 进行配置:
| 键 (Key) | 值 (Value) |
|---|---|
| URL | https://mysite.com/blog.atom |
| Category Filter | <此为可选> |
| Author | <定义自动生成主题的作者> |
| Category | <定义自动生成主题的分类/类别> |
| Tags | <此为可选> |
2.2 SPA 路由配置
Discourse 使用 URL 路径的最后一部分作为单个博客文章的标识符。
示例:
https://mysite.com/blog/an-article-about-technology
https://mysite.com/blog/another-article-about-cats
相应地配置您的 SPA 路由,以便单个博客文章对应于单个 URL。
另外:Discourse 会提供一个指向单个博客文章的链接,因此当用户点击时,您的网站会显示实际文章,这是一种良好的用户体验。
2.3 文章组件
如前所述,我们无法使用 [1] 中的 <script></script> 方法。因此,我们在组件中实现了 iframe 和 javascripts/embed.js 的一些函数:
Article.vue
您应该编辑以下内容:
| 项目 (item) | 描述 (description) |
|---|---|
| #YOUR-DISCOURSE-URL# | 您的 Discourse 实例的 URL,例如 discourse.mysite.com) |
| #YOUR-SITE-URL# | 您的网站的 URL,例如 mysite.com,如果您的博客文章路径不是 /blog/,可能还需要 %2Fblog%2F |
<template>
<div id="article">
<!-- 您的格式化文章在此处 -->
<iframe
v-if="slug"
v-bind:src="`https://#YOUR-DISCOURSE-URL#/embed/comments?embed_url=https%3A%2F%2F#YOUR-SITE-URL#%2Fblog%2F${slug}%2F`"
id="discourse-embed-frame"
width="100%"
v-bind:height="`${iframeHeight}px`"
frameborder="0"
scrolling="no"
referrerpolicy="no-referrer-when-downgrade"
/>
</div>
</template>
<script>
export default {
data: () => ({
slug: null, // 博客文章的 slug,例如,“an-article-about-technology”,而路由是“https://mysite.com/blog/an-article-about-technology”
iframeHeight: 0 // Discourse 将告诉我们确切的 iframe 高度(参见:receiveMessage 方法)
}),
methods: {
// iframe 通信
receiveMessage(event) {
if (!event) {
return;
}
if (!(event.origin || "").includes("#YOUR-DISCOURSE-URL#")) {
return;
}
if (event.data) {
if (event.data.type === "discourse-resize" && event.data.height) {
this.iframeHeight = +event.data.height;
}
if (event.data.type === "discourse-scroll" && event.data.top) {
// 查找 iframe 偏移量
const destY = this.findPosY(this.$refs["discourse-embed-frame"]) + event.data.top;
window.scrollTo(0, destY);
}
}
},
// 感谢 http://amendsoft-javascript.blogspot.ca/2010/04/find-x-and-y-coordinate-of-html-control.html
findPosY(obj) {
var top = 0;
if (obj.offsetParent) {
while (1) {
top += obj.offsetTop;
if (!obj.offsetParent) break;
obj = obj.offsetParent;
}
} else if (obj.y) {
top += obj.y;
}
return top;
}
},
async created() {
this.slug = this.$router.currentRoute.path.split("/")[2];
},
mounted() {
window.addEventListener("message", this.receiveMessage);
},
beforeDestroy() {
window.removeEventListener("message", this.receiveMessage);
}
}
</script>
2.4 Discourse 嵌入配置
现在已经部署了 RSS/Atom Feed 轮询并在您的网站上实现了相关功能,我们终于可以配置 Discourse 实例上的嵌入了。
转到 Admin → Customize → Embedding 并添加一个主机:
| 键 (Key) | 值 (Value) |
|---|---|
| Allowed Hosts | 您的网站基础 URL,例如 “mysite.com” |
| Class Name | 可选的样式类名 |
| Path Allowlist | 例如 “/blog/.*” |
| Post to Category | 与 RSS 轮询 Category 中配置的相同分类 |
3. 最后说明
RSS/Atom feed 轮询以及 Discourse 本身都会在单个博客文章不存在时创建一个新主题。确保 RSS/Atom feed 轮询先执行(即,在您访问网站上的博客文章之前等待主题创建)。
原因:Discourse 无法抓取标题和摘要,因此主题将是 mysite.com,摘要为 We are sorry, but this site does not work without javascript. ![]()
如果由于某种原因 Discourse 先执行了,您可以删除该主题并等待 RSS/Atom feed 运行。
祝好
– MK2k