对于后端开发者来说,修改/扩展 Discourse 有多“困难”

关于此事的几点思考。

开发付费 Discourse 课程

在 Udemy 或 Coursera 等平台上开设与 Discourse 开发相关的课程的想法很有趣。我怀疑主要问题在于,除了 Discourse 团队之外,没有多少人有资格创建 Discourse 开发课程,而且课程的潜在学生数量相当少。话虽如此,我相信一门与开发 Discourse 主题或插件相关的优秀课程,随着时间的推移,可以获得足够多的学生,使其值得投入精力来创建。

另外一个可能的问题是,对 Discourse 课程收费可能违背本论坛的精神。不过我只是在猜测。

Discourse 开发教程

我认为关于许多 Discourse 插件、主题和主题组件的实际实现相当简单的观察是正确的。与其他平台的开发一样,问题在于如何克服不理解各部分如何协同工作的初始障碍。Discourse 团队(或其他合格的开发人员)可以通过编写一些教程来帮助解决这个问题,这些教程将引导你完成创建功能性主题、主题组件和插件的整个过程。例如,一个关于创建 Discourse 占位符主题组件的分步指南。

我意识到这需要大量的时间投入,因此需要与 Discourse 的业务目标保持一致。

当前可用资源

通过一点搜索,可以在本论坛上找到大量关于 Discourse 开发的优质信息。查看 Discourse GitHub 仓库中的代码示例也是一种很好的学习方式。

最近一个不应被忽视的工具是 GitHub Copilot。如果你以正确的方式构建开发环境,它可以帮助你了解如何开发 Discourse 主题和插件。对于开发 Discourse 主题,我发现的最佳方法是在本地 Discourse 安装的根目录中开发主题(使用 Discourse 主题 CLI)。这样构建可以使 Copilot 轻松访问 Discourse 代码库。(请注意,对于插件开发,plugins 文件夹已在根目录 /discourse 中。)(另外请注意,我可能错了,Copilot 实际上并没有访问我在 IDE 中加载的 Discourse 代码库。无论如何,核心 Discourse 代码可供我自己参考是有帮助的。)

为了避免分心,并专注于将 Copilot 用作学习工具,我在我的 IDE(VS Code)中关闭了 Copilot 的自动补全功能,只通过聊天与 Copilot 进行交互。以下是一个示例,说明它可以提供什么样的帮助。(shopify.js 是 Theme CLI 在我的主题的 /javascripts/discourse/api-initializers/ 目录中创建的文件。)

与 Copilot 的对话

scossar: 你能解释一下 shopify.js 中的默认导出在做什么吗?它基本上是使用 discourse_theme gem 创建主题时 Discourse 提供的样板代码。我已经修改了样板代码,以便它会记录每次页面更改。我理解 api.onPageChange 的调用,但我并不清楚 api 对象是如何被注入到我的代码中的。

GitHub Copilot: api 对象通过 apiInitializer 返回的对象中的 initialize 方法被注入到你的代码中。

以下是简化版的操作:

  1. 当 Discourse 启动时,它会运行所有初始化器。

  2. apiInitializer 函数创建一个在 "inject-objects" 初始化器之后运行的初始化器。

  3. 当此初始化器运行时,它会调用 withPluginApi 函数,并将 versioncbopts 参数传递进去。

  4. withPluginApi 函数会检查当前的 Discourse 版本是否与指定的版本兼容。如果兼容,它会调用回调函数 (cb) 并将 PluginApi 实例作为参数传递。这个 PluginApi 实例就是你在代码中看到的 api 对象。

因此,api 对象是 PluginApi 的一个实例,它通过 withPluginApi 函数传递到你的代码中。这个对象提供了许多允许你与 Discourse 交互的方法,例如你已经在使用的 onPageChange

scossar: 那么 shopify.js 中默认导出的第二个参数函数就是 apiInitializer 接受的回调函数吗?

GitHub Copilot: 是的,没错。apiInitializer 的第二个参数是一个回调函数。当初始化器运行时(假设当前的 Discourse 版本与指定的版本兼容),这个函数会以 PluginApi 实例作为参数被调用。这就是 api 对象在你的 shopify.js 文件中可用的方式。

在我弄清楚之后……

scossar: ……还有另一个想法,api.decorateCooked 是否可以用来注入一个托管在外部应用程序上的评分 iframe(例如,由我正在开发的 Shopify 应用托管)?

GitHub Copilot: 是的,你可以使用 api.decorateCooked 将 iframe 注入到帖子中。以下是一个大致的示例,说明你如何做到这一点:

api.decorateCooked($elem => {
  const postId = $elem.data('post-id');
  const iframe = document.createElement('iframe');
  iframe.src = `https://your-app.com/ratings?post_id=${postId}`;
  iframe.width = '100%';
  iframe.height = '200'; // 根据需要调整
  $elem.append(iframe);
}, { id: 'rating-iframe' });

在这个示例中,api.decorateCooked 用于将一个 iframe 追加到每个帖子。iframe 的源被设置为你的外部应用程序上的一个 URL,并将帖子 ID 作为查询参数包含在内。你需要将 'https://your-app.com/ratings' 替换为你评分页面的实际 URL。

请注意,这是一个简化的示例。你可能需要根据你的需求进行调整,并且绝对应该添加错误处理和其他必要的预防措施,以确保它能够正确且安全地工作。

目前还不能指望 Copilot 能够写出你所有的代码,但它是一个学习如何编写 Discourse 代码的绝佳工具。