当我开始构建 Jamstack 网站时,我不得不弄清楚的一件事是,您的网站会经历不同的阶段,您可以在其中放置逻辑。
让我们来看一个特殊的例子,以便您了解我的意思。假设您正在为一个音乐场所制作一个网站。网站最重要的部分是活动列表,有些是过去的,有些是即将到来的。您需要确保将它们标记为这样,或者设计得非常清晰。这是基于日期的逻辑。您如何做到这一点?该逻辑在哪里存在?
对于 Jamstack,至少有四个地方需要考虑。
选项 1:自己将其写入 HTML
从字面上讲,坐下来编写一个 HTML 文件来表示所有事件。我们会查看事件的日期,确定它是过去还是未来,并为每种情况编写不同的内容。提交并部署该文件。
<h1>Upcoming Event: Bill's Banjo Night</h1>
<h1>Past Event: 70s Classics with Jill</h1>
这完全可以!但缺点是我们必须一直更新该 HTML 文件——一旦比尔的班卓琴之夜结束,我们就必须打开代码编辑器,将“即将到来”更改为“过去”,然后重新上传文件。
选项 2:编写结构化数据并在构建时执行逻辑
与其手动编写所有 HTML,不如创建一个 Markdown 文件来表示每个事件。日期和标题等重要信息以结构化数据的形式包含在其中。这只是一个选项。关键是我们可以直接访问此数据。它也可以是无头 CMS 或类似的东西。
然后,我们设置一个静态站点生成器,例如 Eleventy,它读取所有 Markdown 文件(或从您的 CMS 中提取信息)并将它们构建成 HTML 文件。最棒的是,我们可以在构建过程中运行我们想要的任何逻辑。执行复杂的数学运算、访问 API、运行拼写检查……一切皆有可能。
对于我们的音乐场所网站,我们可以将事件表示为如下所示的 Markdown 文件
---
title: Bill's Banjo Night
date: 2020-09-02
---
The event description goes here!
然后,我们通过编写如下所示的模板在构建过程中运行一些逻辑
{% if event.date > now %}
<h1>Upcoming Event: {{event.title}}</h1>
{% else %}
<h1>Past Event: {{event.title}}</h1>
{% endif %}
现在,每次构建过程运行时,它都会查看事件的日期,确定它是在过去还是将来,并根据该信息生成不同的 HTML。无需再手动更改 HTML 了!
这种方法的问题在于,日期比较只发生一次,在构建过程中。上面示例中的now
变量将引用构建恰好运行的日期和时间。并且一旦我们上传了构建生成的 HTML 文件,这些文件就不会更改,直到我们再次运行构建。这意味着,一旦我们音乐场所的某个活动结束,我们就必须重新运行构建以确保网站反映这一点。
现在,我们可以自动化重建,使其每天发生一次,或者,甚至每小时发生一次。这实际上就是 CSS-Tricks 会议网站 通过 Zapier 做的事情。

但这可能会累积构建分钟数,如果您使用的是 Netlify 等服务,并且可能仍然存在某些边缘情况,用户会获得过时的网站版本。
选项 3:在边缘执行逻辑
边缘工作者是在每次收到请求时在 CDN 级别运行代码的一种方法。在撰写本文时,它们尚未得到广泛应用,但一旦它们可用,我们就可以这样编写日期比较代码
// THIS DOES NOT WORK
import eventsList from "./eventsList.json"
function onRequest(request) {
const now = new Date();
eventList.forEach(event => {
if (event.date > now) {
event.upcoming = true;
}
})
const props = {
events: events,
}
request.respondWith(200, render(props), {})
}
render()
函数将获取我们处理过的事件列表并将其转换为 HTML,也许是将其注入预渲染的模板中。边缘工作者的最大优势在于它们速度极快,因此我们可以在服务器端运行此逻辑,同时仍然享受 CDN 的性能优势。
并且由于边缘工作者在每次有人请求网站时都会运行,因此我们可以确保他们会获得最新的版本。
选项 4:在运行时执行逻辑
最后,我们可以将结构化数据直接传递到前端,例如,以数据属性的形式。然后,我们编写 JavaScript 代码,在用户的设备上执行我们需要的任何逻辑,并动态操作 DOM。
对于我们的音乐场所网站,我们可能会编写如下所示的模板
<h1 data-date="{{event.date}}">{{event.title}}</h1>
然后,我们在页面加载后在 JavaScript 中进行日期比较
function processEvents(){
const now = new Date()
events.forEach(event => {
const eventDate = new Date(event.getAttribute('data-date'))
if (eventDate > now){
event.classList.add('upcoming')
} else {
event.classList.add('past')
}
})
}
now
变量反映用户设备上的时间,因此我们可以非常确定事件列表是最新的。因为我们是在用户的设备上运行此代码,所以我们甚至可以变得更花哨,例如根据用户的语言或时区调整日期的显示方式。
与生命周期中的前几个点不同,运行时持续时间与用户打开我们网站的时间一样长。因此,如果我们愿意,我们可以每隔几秒运行一次processEvents()
,并且我们的列表将保持完美更新,而无需刷新页面。对于我们音乐场所的网站,这可能没有必要,但如果我们想在建筑物外的广告牌上显示这些事件,它可能派上用场。
您将把逻辑放在哪里?
虽然 Jamstack 的核心概念之一是我们在构建时尽可能多地工作并提供静态 HTML,但我们仍然可以决定将逻辑放在哪里。
您将把它放在哪里?
这实际上取决于您要做什么。您网站中几乎从未更改的部分完全可以在编辑时完成。当您发现自己一次又一次地更改某个信息时,可能是将其移动到 CMS 中并在构建时提取它的好时机。对时间敏感的功能(例如我们在此处使用的事件示例),或依赖于用户信息的功能,可能需要在生命周期的后期在边缘甚至在运行时发生。
您忘记了 JAM 中的 A。您的 API 端点应该执行您的逻辑。您的标记应该加载应用程序框架,然后 JavaScript 应该从 API 请求即将发生的和过去的事件,并且 API 应该执行确定哪些是过去哪些是即将发生的逻辑并相应地做出响应。
说得对
选项 4 干净且无忧无虑,但它有一个问题。
它是时区。事件可能已经发生,但用户将其视为即将发生。
也许有一种方法可以将用户和事件时间转换为 UTC 或任何标准,然后根据状态进行比较。
您可以处理 UTC 日期,并在计算后仅格式化成本地时区以进行显示。