有时我们的网站需要一些内容管理。 不总是。 不需要太多。 但需要一些。 CMS 市场 正在蓬勃发展,提供了负担得起、易于使用产品,因此我们不缺选择。 值得庆幸的是,这是一个与过去截然不同的世界,过去公司被迫为一个全能、全方位、全整合、全个性化、大企业认证™ 的 CMS 平台支付巨额费用(不是确切的费用:我四舍五入到最近的十亿)。
但有时,使用任何更新网站内容的人员都熟悉的一种非常简单的工具会很不错,而不是去掌握一个新的 CMS。
我非常喜欢 Trello 来管理想法和任务。 而且它有 一个 API。 为什么不把它用作网站的内容来源呢? 我的意思是,嘿,如果我们能用 Google Sheets 做到这一点,那还有什么能阻止我们尝试其他事情呢?
你好,Trello
这是一个简单的网站,可以探索。 它从 此 Trello 看板 获取其内容,这些内容以部分的形式显示。 每个部分都由我们 Trello 看板中卡片的标题和描述字段填充。

Trello 使用 Markdown,这在这里很有用。 任何在 Trello 卡片中编辑内容的人都可以应用基本文本格式,并使相同的 Markdown 流入网站,并通过构建过程转换为 HTML。
构建块
我非常喜欢这种运行构建的模型,它从各种提要和来源中提取内容,然后将它们与模板混合在一起,以生成网站的 HTML。 它将演示与内容管理分离(这就是现代流行的 CMS 产品中“分离”一词的来源)。 这意味着我们可以自由地以我们想要的方式设计网站,使用我们在这里在 CSS-Tricks 上学习的所有花哨技巧和技术。

由于我们在构建时引入内容,因此如果我们的网站变得流行并带来大量流量,我们就不必担心数据源的使用配额或性能。 为什么不呢? 看看我们把它们做得多么漂亮!
我想玩!
好的。 您可以获取此网站代码的副本,并尽情修改。 此版本包含有关如何创建自己的 Trello 看板并将其用作构建内容来源的信息。
如果您想先了解它是如何工作的,然后再自己动手,请继续阅读。
发现 API
Trello 有一个 文档齐全的 API 和一组 开发者资源。 还有一个 方便的 Node 模块,用于简化身份验证和与 API 交互的任务。 但您也可以在探索 Trello 看板时通过修改 URL 来探索 API。
例如,上面 Trello 看板的 URL 是
https://trello.com/b/Zzc0USwZ/hellotrello
如果我们在此 URL 中添加 .json
,Trello 会将内容以 JSON 的形式显示给我们。 看一下。我们可以使用此技术来检查 Trello 中的底层数据。 以下是特定卡片的 URL
https://trello.com/c/YVxlSEzy/4-sections-from-cards
如果我们使用这个小技巧并将 .json
添加到 URL 中,我们将会看到 描述该卡片的数据。
我们会发现有趣的事情 - 看板、列表和卡片的唯一 ID。 我们可以看到卡片的内容和大量元数据。
我喜欢这样做! 看看这些可爱的数据! 我们应该如何使用它?
决定如何使用看板
对于这个例子,让我们假设我们有一个网站,只有一页可管理的内容。 我们看板中的列表或列非常适合控制该页面上的部分。 编辑人员可以为它们提供标题和内容,并将它们拖放到他们想要的顺序。
我们需要列表的 ID,以便我们可以通过 API 访问它。 幸运的是,我们已经看到了如何发现它 - 看看 所讨论列表中任何卡片的数据。 每个都有一个 idBoard
属性。 bingo!
生成网站
计划是从 Trello 获取数据并将其应用于一些模板以填充我们的网站。 大多数 静态网站生成器 (SSG) 都可以完成这项工作。 这就是它们擅长的地方。 我将使用 Eleventy,因为我认为它具有最简单的概念易于理解。 此外,它在获取数据和使用 Nunjucks(一种流行的模板语言)生成干净的 HTML 方面非常高效。
我们希望能够使用表达式 lin 我们的模板,为在名为 trello
的 JavaScript 对象中找到的每个项目输出一个 section
元素
<!-- index.njk -->
{% for card in trello %}
<section>
<h2>{{ card.name }}</h2>
<div>
{% markdown %}
{{- card.desc | safe }}
{% endmarkdown %}
</div>
</section>
{% endfor %}
获取构建数据
像这样的 Jamstack 网站的一种流行技术是使用 Gulp、Grunt 或 [在此处插入最新的构建脚本热度] 运行构建,它从各种 API 和提要中获取数据,将数据存储在适合 SSG 的格式中,然后运行 SSG 生成 HTML。 这很有效。
Eleventy 通过支持在 其数据文件中执行 JavaScript 来简化此过程。 换句话说,它不仅可以利用以 JSON 或 YAML 格式存储的数据,还可以使用 JavaScript 返回的任何内容,从而为在 Eleventy 构建运行时直接向 API 发出请求打开大门。 我们不需要单独的构建步骤来先获取数据。 Eleventy 会为我们做。
让我们使用它来获取模板中 trello
对象的数据。
我们可以使用 Trello Node 客户端 查询 API,但事实证明,我们想要的所有数据都在 看板的 JSON 中。 所有内容! 一次请求! 我们可以一次性获取它!
// trello.js
module.exports = () => {
const TRELLO_JSON_URL='https://trello.com/b/Zzc0USwZ/hellotrello.json';
// Use node-fetch to get the JSON data about this board
const fetch = require('node-fetch');
return fetch(TRELLO_JSON_URL)
.then(res => res.json())
.then(json => console.log(json));
};
但是,我们不想显示看板中的所有数据。 它包括其他列表中的卡片、已关闭和删除的卡片等。 但是,我们可以使用 JavaScript 的 filter
方法 来过滤卡片,只包括我们感兴趣的卡片。
// trello.js
module.exports = () => {
const TRELLO_JSON_URL='https://trello.com/b/Zzc0USwZ/hellotrello.json'
const TRELLO_LIST_ID='5e98325d6d6bd120f2b7395f',
// Use node-fetch to get the JSON data about this board
const fetch = require('node-fetch');
return fetch(TRELLO_JSON_URL)
.then(res => res.json())
.then(json => {
// Just focus on the cards which are in the list we want
// and do not have a closed status
let contentCards = json.cards.filter(card => {
return card.idList == TRELLO_LIST_ID && !card.closed;
});
return contentCards;
});
};
就这样! 将其保存在名为 trello.js
的文件中,该文件位于 Eleventy 的数据目录中,我们将在名为 trello
的对象中准备好这些数据,以便在我们的模板中使用。
搞定! 🎉
但是我们可以做得更好。 让我们也处理附件图片,并添加一种方法,在内容上线之前将其暂存以供审核。
图片附件
可以将文件附加到 Trello 中的卡片。当您附加图像时,它会直接显示在卡片中,并包含在数据中描述的资源的源 URL。我们可以利用它!
如果一张卡片包含图像附件,我们将获取其源 URL,并将其作为图像标签添加到我们的模板在构建时插入页面的内容中。这意味着在 JSON 的描述属性(card.desc
)中添加图像的 Markdown 代码。
然后,我们可以让 Eleventy 将其与其他内容一起转换为 HTML。这段代码会在我们的 JSON 中查找卡片,并将数据整理成我们需要的形式。
// trello.js
// If a card has an attachment, add it as an image
// in the description markdown
contentCards.forEach(card => {
if(card.attachments.length) {
card.desc = card.desc + `\n`;
}
});
现在我们也可以在内容中移动图像了。非常方便!
暂存内容
让我们再添加一个功能,以展示如何使用 Trello 管理网站内容。
我们可能希望在将内容发布到世界之前进行预览,这有几种方法。我们的 Trello 看板可以包含一个用于暂存内容的列表,以及一个用于生产内容的列表。但这将难以直观地了解新内容是如何与已发布内容一起展示的。
更好的方法是使用 Trello 的标签来指示哪些卡片已发布,哪些卡片应该只包含在网站的暂存版本中。这将提供一个良好的工作流程。我们可以通过在正确位置添加新卡片来添加更多内容。使用“暂存”标签标记它,并将其从出现在生产分支的卡片中过滤掉。

需要对我们的 JavaScript 对象进行更多过滤
// trello.js
// only include cards labelled with "live" or with
// the name of the branch we are in
contentCards = contentCards.filter(card => {
return card.labels.filter(label => (
label.name.toLowerCase() == 'live' ||
label.name.toLowerCase() == BRANCH
)).length;
});
我们希望标记为“已发布”的内容出现在构建的每个版本中,无论是否暂存。此外,我们将尝试包含标签与名为“BRANCH”的变量匹配的卡片。
为什么?这是什么意思?
这里我们将发挥创意!我选择在Netlify(免责声明:我在那里工作)上托管此网站。这意味着我可以在Netlify 的 CI/CD 环境中运行构建。每当我将更改推送到其 Git 存储库时,它都会重新部署网站,还会提供一些对于此网站非常有用的功能。
其中之一是分支部署。如果您想要为网站创建新的环境,可以通过在 Git 存储库中创建新分支来创建它。构建将在该上下文中运行,并且您的网站将发布到包含分支名称的子域上。像这样。
查看一下,您将看到来自我们列表的所有卡片,包括带有橙色“暂存”标签的卡片。我们将其包含在此构建中,因为其标签与构建上下文的支部分支名称匹配。BRANCH
是一个环境变量,其中包含构建运行的支部分支名称。
label.name.toLowerCase() == BRANCH
理论上,我们可以创建任意数量的分支和标签,并且拥有各种暂存和测试环境。准备好将内容从“暂存”提升到“已发布”了吗?交换标签,就可以开始!
但是它如何更新呢?
在 Netlify 等 CI/CD 中运行网站构建时,我们获得的第二个好处是可以根据需要触发构建运行。Netlify 允许我们创建构建钩子。当您向其发送 HTTP POST 请求时,这些是会启动新部署的 Webhook。
如果 Trello 也支持 Webhook,那么我们可以将这些服务连接在一起,并在 Trello 看板发生更改时自动刷新网站。猜猜怎么着?它们确实支持!太好了!
要创建 Netlify 构建钩子,您需要访问您网站的管理面板。(如果您想尝试一下,可以通过点击几次来将此演示网站引导到新的 Netlify 网站中。)

现在,有了新的构建钩子 URL,我们需要注册一个新的 Trello Webhook,当内容发生更改时,它会调用该钩子。在 Trello 中创建 Webhook 的方法是通过 Trello API。
此网站的存储库包含一个用于调用 Trello API 并为您创建 Webhook 的小工具。但是您需要拥有 Trello 开发者令牌和密钥。幸运的是,您可以通过访问Trello 开发者门户网站并按照“授权客户端”下的说明进行操作来免费创建这些令牌和密钥。
获得了它们吗?太棒了!如果您将它们保存在项目中的.env
文件中,您可以运行此命令来设置 Trello Webhook
npm run hook --url https://api.netlify.com/build_hooks/XXXXX
这样,我们就创建了一个用于管理简单网站上的内容的不错的小流程。我们可以按照自己的意愿设计前端,并且内容更新会发生在 Trello 看板上,每当内容发生更改时,该看板都会自动更新网站。
我真的可以用它吗?
这是一个简单的示例。这是有意为之。我确实希望展示解耦的概念,以及使用外部服务的 API 来驱动网站内容的概念。
这不会取代更复杂项目的完整功能的解耦 CMS。但这些原理完全适用于更复杂的网站。
但是,此模型可能非常适合我们看到的独立商店、酒吧和餐厅等企业的网站类型。想象一下,一个 Trello 看板,其中一个列表用于管理餐厅的首页,一个列表用于管理餐厅的菜单项。餐厅员工可以非常轻松地管理这些内容,比每次菜单更改时都上传新的 PDF 格式的菜单要好得多。
准备探索示例并尝试使用您自己的看板和内容吗?试试这个
- 克隆并部署上面的示例,然后开始进行更改。
- 详细了解 Netlify 的Netlify 分支构建功能。
- 深入了解Trello 开发者资源。
很棒的文章,Phil!
关于您关于生产使用的最后一节
我们在构建它到我们的自定义平台之前,几年来一直使用Trello 作为 CMS来管理我们的每周通讯。
所以,是的!是可以做到的 :)
哦,不错,@Jerod!
我认为有很多类似的实现方法。很高兴听到您在构建原生功能之前也使用了类似的方法。
我希望我在我在代理机构工作时为许多项目探索了这种方法。即使是早期概念或内部工具,这也能让我们加快速度。
它也是使用无头 CMS 平台实现更复杂和更丰富功能的良好入门。
例如 Bludit、Nomad、Kirby、Batflat。所以:为什么?
因为提供和更新内容的用户已经使用 Trello 了?她熟悉它,有适用于移动设备的应用程序,并且此类用户的学习曲线为零?实际上不需要维护(更新等)?说真的,要对新想法持开放态度。我发现这种方法在某些情况下非常有用。
@Uwe
确实!而且列表还在不断增加。看看https://headlesscms.org上的所有很棒的无头 CMS。有很多功能强大的 CMS 产品可以通过它们的 content API 使用。
为什么发布这篇文章?嗯,我喜欢这个示例的原因是它不是一个完整的 CMS。相反,它使用的是许多个人使用的工具。它是免费的,熟悉的,并且足够直观,可以非常逻辑地映射到简单网站上的内容。
你不会用它来取代真正的 CMS。但我经常需要为我创建网站的人提供少量的 内容管理 能力。这以一种非常容易理解的方式,而且免费提供。无需在任何地方托管 CMS,也不需要教作者使用新工具。
不过,我最喜欢的是那些展示将内容源与用户界面解耦的模型如何良好工作的示例。尤其是在可以分层添加事件、自动构建和 webhooks 的情况下。我认为这是一个可以轻松理解这些概念的例子。
很棒的文章!我期待在个人项目中实现它。
通过一些自定义字段,这种方法对 MVP 很有帮助。
非常感谢 Phil 的启发。很棒的想法。
我使用了你的想法,并将其应用于我的 gohugo.io 网站和私人看板。非常感谢。
我非常喜欢它,所以我根据我的设置做了一个关于 Hugo 的教程:https://discourse.gohugo.io/t/deadly-simple-cms-with-trello-for-dummies/27281
太棒了!
是的,这种方法应该适用于任何使用的静态网站生成器。我依靠 Eleventy 中的一些机制来获取数据,但我喜欢你将它移植到 Hugo 的方式。
在写这篇文章之后,我意识到我可以抽象出从 Trello 获取数据的部分,并使其轻松添加到任何使用支持本地数据文件的静态网站生成器的构建中。所以,我做了一个 Netlify 构建插件,它获取所有公共看板上的列表数据,并将数据保存到您选择的 SSG 可以使用的位置。
该插件在 GitHub 上
这里有一个 演示
它对上面的示例略有改进,但包含所有相同的概念。
现在你可以
您好 Phil,很棒的工作
我尝试使用您的文件,但在 GitHub Pages 上只显示了 README 文件。在 Netlify 上,它只是运行,却没有与 Trello 同步。
我难以设置 webhooks 并调用它们。
嗨,Mateus,
这在 GitHub Pages 中不起作用。您不能在 GitHub Pages 中运行除了 Jekyll 构建之外的构建。如果您愿意,您可以本地运行构建,然后将结果推送到 GitHub Pages,但我认为这不是您要问的。
当我在 Netlify 上运行构建时,它确实成功地从示例 Trello 看板中提取了内容。(通过点击文章中的链接测试,或此链接)。如果您更改了它从中提取的 Trello 看板,则需要将该看板设置为在 Trello 中公开可见,因为此示例匿名获取数据(为了简单起见)。
在将 webhook 添加到 Trello 以便 Trello 中的更改触发 Netlify 中的构建时,您需要
一个 Trello API 密钥 添加到您的
.env
文件中一个 Trello API 令牌 添加到您的
.env
文件中在您的 Netlify 网站管理中创建的 Netlify 构建钩子
然后,您应该能够运行一次性帮助程序来向 Trello 注册 webhook,确保在该命令中指定您的 Netlify 构建钩子
npm run hook --url YOUR-NETLIFY-BUILD-HOOK-URL
希望这有帮助。
你真是个天才!:-)
喜欢你所有的文章,太棒了!