世界上许多大型软件系统都是用 Python 构建的,例如 YouTube、Instagram、Dropbox 等众多公司的大部分代码。2019年,Dropbox 宣布 Python 是他们“在后端服务和桌面客户端应用中都使用最广泛的语言”,代码量高达400万行。
如果你已经是一名 Python 程序员,那么你就会知道用这门语言将想法转化为代码是多么容易。除了用于大型系统,Python 在科学家、工程师、数据分析师等人士的日常工作中也很受欢迎。
Python 程序员面临的一个特殊挑战是,要创建一个现代化的 Web 应用程序,他们还必须学习 JavaScript,以及像 React、Angular 或 Vue 这样的框架。即使学会了所有这些,他们仍然需要处理编写、调试和维护一个多语言系统的复杂性,这个系统在两种语言之间以及客户端-服务器边界上存在复杂的交互。
使用 FastHTML,你常常会发现根本不需要编写任何 JavaScript。这不仅让开发和调试变得更加容易,许多功能的实现也突然变得简单了。例如,当我们想添加缓存来加速我们的主页时,我们只需在创建主页的函数上添加一个标准的装饰器即可。不需要特殊的基础设施,因为实现都在一个地方。ASGI 使这一点特别强大——它可以在一个地方处理缓存、会话、认证、基于主机的重定向、子路由等等。所有这些都可以从 FastHTML 直接访问。
如今,大多数 Web 应用程序都是使用后端系统构建的,这些系统通过 HTTP 返回 JSON 和 HTML 数据的组合。JavaScript,通常使用 React、Angular 或 Vue 等框架,将 JSON 和 HTML 结合起来以便在浏览器中显示。这是一种“基于 API”的 Web 开发方法。
HTMX 使用的另一种“基于超媒体”的方法,通过只返回 HTML 极大地简化了事情。FastHTML 就是为创建超媒体应用程序而设计的。使用这种方法,客户端-服务器编程的几乎所有复杂性都消失了。当直接访问一个页面时,服务器会响应一个标准的 HTML 网页。
<html>
<head><title>FastHTML Page</title></head>
<body>
<p id="greet" hx-get="/change">Hello World!</p>
</body>
</html>
这可以使用以下 FastHTML 代码生成:
@rt('/')
def get(): return Div(P('Hello World!'), hx_get="/change")
当点击这个链接时,服务器会响应一个“HTML 片段”——也就是一小段 HTML,它将被插入到现有页面中。
<p>Nice to be here!</p>
在这种情况下,返回的元素将替换原来的 P 元素(因为这是 HTMX 的默认行为)。我们创建这个 /change 处理程序的代码是:
@rt('/change')
def get(): return P('Nice to be here!')
正如我们在HTMX 基础部分讨论的,HTMX 消除了 HTML 的四个关键限制。它允许任何 DOM 元素上的任何事件在任何路径上调用任何 HTTP 方法,并将响应放置在 DOM 的任何位置。如果你以前没有编写过基于超媒体的应用程序,我们强烈建议你阅读《Hypermedia Systems》这本书。它解释了如何使用 HTMX 构建超媒体应用程序;你在那里学到的技术将直接适用于 FastHTML。
Uvicorn 根据其官网的说法,是一个“ASGI Web 服务器”。这到底是什么意思?正如我们已经讨论过的,ASGI 是一个 Python API,它将 HTTP 请求和响应转换为 Python 函数调用。Uvicorn 是一个 Web 服务器,Web 浏览器可以与之通信,而它又与 ASGI 应用程序通信,将其结果返回给浏览器。
大多数时候,你只需在你的 main.py 文件末尾添加一行代码即可运行你的 FastHTML 应用程序:serve()。这样做之后,会打印一条消息,告诉你现在你的计算机上有一个 Web 服务器正在运行,如果你点击提供的链接,你将看到你的应用程序正在运行。如果你查看 main.py 的源代码,你会看到实际运行服务器的那行代码正是调用 Uvicorn 来完成工作的。
uvicorn.run(f"{fname}:{app}", host=host, port=port, reload=reload)
当你部署应用程序时,通常会使用像 Railway 或 Vercel 这样的服务提供商。我们提供的一键部署功能只是为你调用 python main.py,而提供商负责将 Uvicorn 运行的端口连接到一个公共 IP 地址。你也可以在像 VPS 这样的服务器上运行你的应用程序,可以设置 PORT 环境变量为 80 使其直接可用,或者添加一个像 nginx 或 caddy 这样的前端服务器来将请求转发到 Uvicorn 运行的端口。
因为 ASGI 是一个如此简单的 API(它实际上只是一个接受三个参数的 Python 函数),反直觉的是,这实际上使得它使用起来相当复杂。它为你做的事情不多,所以为了直接用 Uvicorn 创建一个 ASGI 应用程序,需要编写相当多的样板代码。
Starlette 使得创建 ASGI 应用程序变得容易得多。它通过提供一些简单的抽象,如 Request、Response 和 Route,消除了大量的样板代码。阅读 Starlette 的源代码非常有启发性,因为你会意识到实际涉及的代码量非常少;它只是将最小化的 ASGI API 转换成了一套更方便的类和函数。
Starlette 对于你如何创建 Web 应用程序完全没有强制性规定。因此,其他库介入提供了更具体的功能。例如,FastAPI 提供了一个构建在 Starlette 之上的框架,为创建 JSON API 添加了大量功能。
当 Jeremy Howard 决定要创建一个库来简化构建超媒体应用程序时,他以 FastAPI 为榜样。实际上,他逐页阅读了 FastAPI 的教程,并试图尽可能多地复制其功能,但目标是超媒体应用程序而不是 JSON API。FastAPI 的创建者 Sebastián Ramírez 非常慷慨地向 Jeremy 提供了时间和建议,帮助解释了 FastAPI 设计背后的思路。
主要的 FastHTML 类实际上是作为 Starlette 的 Application 类的子类实现的。这意味着你可以使用任何与 Starlette 兼容的中间件、路由和其他功能。(不过,你通常会发现 FastHTML 提供了更方便的方式来做这些事。)
尽管 FastAPI 和 FastHTML 都建立在 Starlette 之上,并且 FastHTML 受 FastAPI 的启发,但它们之间存在很多差异,因为它们有不同的目的。所以,如果你以前用过 FastAPI,不要想当然地认为所有东西都会完全一样!
FastHTML 开箱即用地通过 Fastlite 库支持 SQLite。SQLite 是 Python 内置的,所以你不需要安装任何额外的东西。因为它使用一个文件直接从 Python 存储和访问数据库,所以访问速度极快,而且使用起来非常简单。Fastlite 为数据库访问提供了一个极其简单的 API,并允许你使用标准 Python 内置功能,如数据类(dataclasses)和字典(dicts)来读写数据。
旧版本的 SQLite 不具可扩展性,因为它们不支持并发读写。然而,这个限制在几年前通过添加预写日志(WAL)得到了解决,FastHTML 默认使用该模式。有了 WAL 以及现代的多核计算机和高速 SSD,SQLite 可以支持大型且受欢迎的网站。像 Litestream 这样的系统可以用来将数据库复制到远程服务器。
除了 Fastlite 和 SQLite,你也可以使用 SQLModel、SQLAlchemy、Redis 或任何其他数据库服务器或数据存储系统。我们(以及 FastHTML 社区)将持续为 FastHTML 添加更多的数据存储选项。