如何在没有服务器的情况下为静态网站添加动态功能
静态网站目前正受到人们的青睐,这是有原因的。一个包含HTML、CSS和JavaScript文件的文件夹,加载速度很快,托管成本也很低,而且几乎不可能出现故障。 像 Astro 、 Eleventy 和 Hugo 这样的工具,能够利用Markdown文件和模板帮您生成这样的网站结构。而Netlify、Vercel以及Cloudflare Pages等托管服务,则可以通过内容分发网络来提供这些生成的网站内容,而且通常还是免费的。 不过,您的网站还需要具备实际的功能。读者可能想要留下评论,或者有人想通过电子邮件与您联系。也许您还想实时显示价格信息,需要用户登录后才能查看某些页面,或者在网站发布之前收
静态网站目前正受到人们的青睐,这是有原因的。一个包含HTML、CSS和JavaScript文件的文件夹,加载速度很快,托管成本也很低,而且几乎不可能出现故障。
像Astro、Eleventy和Hugo这样的工具,能够利用Markdown文件和模板帮您生成这样的网站结构。而Netlify、Vercel以及Cloudflare Pages等托管服务,则可以通过内容分发网络来提供这些生成的网站内容,而且通常还是免费的。
不过,您的网站还需要具备实际的功能。读者可能想要留下评论,或者有人想通过电子邮件与您联系。也许您还想实时显示价格信息,需要用户登录后才能查看某些页面,或者在网站发布之前收集用户的电子邮件地址。
大多数开发者在这种情况下都会选择使用后端技术。他们会搭建一个Express应用程序,添加数据库,并选择合适的托管方案。这样一来,他们就拥有了自己的服务器,而服务器是需要长期维护的。
不过好消息是,很多时候您其实并不需要这样的服务器。对于小型网站来说,大多数动态功能即便没有自己的服务器也能正常运行。因此,下面我们来探讨一下如何解决这些问题。
目录
静态并不意味着无法实现动态功能
“静态”这个词描述的是您的HTML代码是如何传输到浏览器的,而不是这些代码在浏览器中被执行后会发生什么。一个静态网站仍然可以运行您需要的所有JavaScript代码,它可以获取数据、重新生成页面内容、响应用户的点击操作,也可以与任何公共API进行交互。
但它有一件事是无法做到的,那就是在页面被发送给用户之前先运行您的代码。这意味着它无法隐藏某些信息、查询私有数据库,也无法根据请求者的身份来做出不同的处理。
正是这个限制决定了静态网站的设计方式。一旦您清楚地了解了这一点,剩下的问题就都很容易解决了:有些功能应该由浏览器本身来完成,有些则可以通过租用的服务来实现,而还有一些功能则需要在一个私密的环境中运行。
从浏览器已有的功能入手
在添加任何新的技术组件之前,请先了解当前所使用的平台——因为现代浏览器的功能远比我们大多数人所意识到的要强大得多。
fetch API可以用来从任何公共接口获取JSON数据。元素可以让您无需使用任何第三方库就能创建可交互的模态窗口,而元素则可以实现折叠式菜单的功能。如今,CSS已经能够处理固定标题、滚动动画以及容器查询等功能,而这些功能在过去都是需要借助JavaScript来实现的。
很多看似需要后端处理的功能,其实只需要进行一次数据获取请求,并使用模板字符串即可实现。天气插件、GitHub活动动态、货币转换器、实时比分显示工具,或是Mastodon的时间线功能:所有这些都可以通过浏览器顺利运行,因为相关数据都是公开的,而且调用这些接口也不需要任何密钥。只要编写好数据获取代码,妥善处理加载过程及可能出现的错误情况,问题就能迎刃而解。
处理表单提交
对于大多数静态网站来说,表单处理往往会成为第一个遇到的障碍,因为用户提交的表单数据必须被存储在访客无法看到的地方。你不能在客户端代码中存放数据库密码,也不能直接通过浏览器发送电子邮件。
幸运的是,这个问题已经被多次解决过了,大多数静态网站托管服务都会帮你处理表单相关事务。使用Netlify Forms时,只需要在表单标签中添加一个额外的属性即可。之后,Netlify会捕获每一份提交的表单数据,将其存储在控制面板中,并要么通过电子邮件通知你,要么触发Webhook接口。
不过有时候,你可能需要更多的功能。垃圾邮件过滤、文件上传、条件逻辑处理、多步骤流程设计、表单内集成支付功能,或者需要将数据直接导入电子表格……这些需求往往意味着你需要使用专门的工具来满足它们。通过查看这些优秀的表单构建工具,你可以对比各种选项,因为真正的区别在于这些工具所提供的逻辑处理能力、集成功能,以及你对自己编写的前端代码能拥有多少控制权。
无论如何,最终的选择还是取决于你是否愿意自己负责相关功能的实现。如果你自己编写表单处理的后端代码,那么你就能够完全掌控数据验证、请求速率限制、垃圾邮件过滤机制、数据存储方式、警报通知功能,以及符合GDPR规定的数据删除流程。不过,对于一个简单的联系表单来说,这样做未免有些大费周章。只有当表单成为你产品的重要组成部分时,才值得自己动手开发;而如果表单只是人们用来联系你的工具,那么租用现成的服务才是更合理的选择。
其余场景下的无服务器函数
迟早你会需要运行一些代码来处理某些特定任务——比如在数据发送给浏览器之前对其进行修改,或者隐藏API密钥。而无服务器函数正是为了解决这类问题而存在的,它们使你能够以最简单的方式让静态网站具备更多的动态功能。
你只需要将代码文件放入指定的文件夹中,托管服务就会在下次部署时将其转换成一个URL地址。这样的设置方式在Netlify Functions、Vercel Functions以及Cloudflare Pages Functions中都是通用的。代码量也会保持得很少:
export default async function handler(request) {
const res = await fetch("https://api.example.com/data", {
headers: { Authorization: `Bearer ${process.env.API_KEY}` },
});
const data = await res.json();
return Response.json({ total: data.items.length });
}
你的密钥存储在主机上的环境变量中,因此浏览器在调用 `/api/handler` 时根本看不到这个密钥。既不需要维护任何服务器,也不需要更新操作系统,更不会在平时产生任何费用。
对于任何需要使用凭证的功能来说,这种架构都是理想的选择。你可以利用它通过 Resend 或 Postmark 发送邮件,调用 AI 模型,验证 Webhook 签名,或者代理那些密钥否则会在一天内就被泄露的付费 API。
可租赁的数据库与登录服务
人们通常认为状态管理功能需要服务器来支持,但实际上并非如此。Supabase 通过 REST API 和行级安全机制来实现 Postgres 的状态管理。这些规则直接存储在数据库中,因此你可以通过浏览器进行查询而无需担心安全性问题。Firebase 采用不同的数据模型实现了类似的功能,而 Cloudflare D1 和 Turso 则在边缘节点提供了 SQLite 支持。
登录功能也遵循同样的模式。Clerk、Auth0 和 Supabase Auth 都通过客户端 SDK 来处理会话管理、密码重置、社交登录以及双因素认证等功能。认证机制的实现确实非常复杂,因此使用经过审计的服务来处理这些功能并不是偷懒,而是明智的风险管理措施。
评论、搜索与支付功能
对于最后这几类常见的功能,也有简单的解决方案。Giscus 只需要使用一个脚本标签,就能将 GitHub 的讨论区转换为评论框,而且完全不需要数据库。Pagefind 会在你的网站构建过程中生成搜索索引,并将其作为静态文件发布,因此运行时进行全文本搜索不会产生任何费用。
Stripe 可以处理所有的支付事务。通过 Payment Links 和 Checkout 功能,你可以使用简单的链接来销售商品,而 Stripe 会负责处理信用卡信息、税费以及开具收据等工作。你的网站根本不需要接触任何信用卡数据。
了解何时该升级系统
这种架构确实存在上限,你应该清楚这一点,而不是试图强行突破它。当出现以下情况时,你就应该考虑升级了:你的业务逻辑分散在六种不同的服务中;每月的 SaaS 费用已经超过了购买小型服务器的成本;系统的冷启动时间过长,影响了用户的体验;或者你需要执行耗时较长的任务、使用队列系统,或者需要保持连接数小时以上的socket。到了那个时候,使用传统的应用服务器会更加方便,所以应该及时进行升级。
在之前,这种架构确实能为你带来很多实际的好处。你的网站默认情况下就能保持较高的运行速度,安全风险也会降到最低,你可以将更多的精力投入到产品本身的开发中,而不是系统基础设施的维护上。先从浏览器开始使用这个架构,当需要使用密钥时再添加相应的功能,而对于那些复杂的部分,则可以选择租赁服务来使用。
希望你喜欢这篇文章。你可以通过 在 LinkedIn 上与我联系 。
相关文章
Flutter前端系统设计:在人工智能时代,如何像资深工程师一样思考
系统设计长期以来一直被视为后端领域的问题。 如果你问一群Flutter工程师“系统设计到底意味着什么”,他们中的大多数人会提到服务器架构:负载均衡器、数据库以及微服务。 但如果你让他们设计一个分布式缓存系统或画出一个消息队列的示意图,他们会犹豫不决。而当你要求他们为社交Feed应用开发Flutter客户端时,他们就会立刻打开新文件开始编写组件代码。 这种差距确实存在,不过正在迅速缩小。 随着Flutter应用程序变得越来越复杂——它们具备了实时功能、离线支持、多平台兼容性,同时还包含需要维护的人工智能生成代码——在编写任何一个组件之前所做出的架构决策,其重要性已经与后端架构相当了。 在那些以产
阅读全文
从项目到产品:将平台转化为人们真正会使用的成品
仅仅拥有一个平台是远远不够的;真正的挑战在于确保该平台能够被用户理解、使用,并真正被他们采纳。只有当某种功能能够被他人可靠地使用时,它才算真正实现了其价值。为了评估开发工作的进展,你可以问自己:“这个功能真的被人们在使用吗?”“它是否减轻了用户的使用难度?”这样的思考有助于让开发工作始终与实际的用户需求保持一致,而不仅仅是追求功能的完成。 作者:本·林德斯
阅读全文
GPS在可穿戴设备中的工作原理——以及为什么它会耗费大量的电池电量
几个月前,我在跑步时同时使用了三款不同的设备。为什么呢?这又是一个关于截止日期以及我自己的拖延习惯的长故事…… 当我跑完步回到家后,那款使用GPS功能的智能手表的电池电量已经只剩下15%左右了;而那些依靠手机GPS功能运行的智能手环和智能戒指的电池电量却几乎没有减少。就在那时,我才真正意识到:GPS功能实际上会消耗设备多少电量,以及大多数人在看到手表提供导航信息时,其实并不了解其中的具体原理。 那么,让我们来详细了解一下吧:可穿戴设备中的GPS功能究竟是用来做什么的?为什么它会如此耗电?在长途徒步过程中,又该如何节省电池电量呢? 目录 当你的手表显示“正在获取GPS信号”时,实际上发生了什么?
阅读全文
在生产环境中进行部署时会发生什么?一场幕后揭秘之旅
你提交代码后,几分钟后,这些代码就会真正开始为用户提供服务。 在这两个时刻之间,有一系列复杂的流程在运转:代码构建、生成相应的成果文件、数据库迁移、健康检查以及流量分配调整。每一位生产工程师都依赖这一整套流程,而许多团队至今仍然亲自负责这些工作的执行。 部署基础设施如今已经悄然成为一种运营负担。它最初只是出于技术上的必要性而存在的——因为当时没有其他可行的解决方案,所以每个团队都必须自行搭建这套系统。 如今,这已经成为另一个需要工程师维护的系统,它会消耗大量的值班时间、开发资源,甚至还会在凌晨两点占用本该用于更重要工作的精力。 在这篇文章中,我们将详细探讨一次实际的生产环境部署过程:从代码构建
阅读全文