Vue-Python

快速应用程序开发框架(FastAPI)全面转型,致力于打造全栈式开发平台

关于Vue如何看到那条蛇并坠入爱河的故事

FastAPI 是那种让我心存疑虑的软件之一,因为 Python 后端的开发体验实在太顺畅了。Flask/Express 风格的处理器中的类型提示,在 FastAPI 中直接演变成了解析和验证功能。无需费力配置奇怪的框架配置,就能实现带有自动重载功能的异步处理。再加上其 OpenAPI 模式和自动重载功能,你甚至能在咖啡机完成启动流程之前,就快速搭建出一个体面可用的 API 接口 令人愉悦。Flask/Express风格的处理器中的类型提示功能,转变为解析和验证功能。无需手动配置繁琐的框架配置,即可实现异步加载和自动重载。再加上其OpenAPI模式和自动重载功能,你甚至能在咖啡机完成启动序列之前,就将API快速开发成一个体面的版本 git init 在咖啡机完成启动序列之前,就能开发出一个体面的API。

然后你需要一个前端界面。

历史上,这里曾是霓虹灯照亮的高速公路突然戛然而止的地方,尽头是一堵混凝土墙。

FastAPI FastAPI在API开发方面表现出色,但除非你刻意将所有请求路由至特定目录,否则它无法真正在站点根目录下为前端提供服务。此外,FastAPI完全不支持单页应用(SPA)。虽然你可以通过FastAPI来服务文件、渲染模板或集成任意后端框架,但如何在现代JavaScript前端与Python应用之间实现无缝衔接,却始终是个留给读者自行探索的难题 总之,FastAPI在API开发方面表现出色,但它并不能真正为你的前端提供站点根目录的服务,除非你希望所有请求都被路由到该目录。此外,它对单页应用(SPA)的支持更是闻所未闻。虽然你可以提供文件服务、渲染模板或集成任意你需要的框架,但如何在现代JavaScript前端与Python应用之间实现无缝衔接,却始终像是留给读者去探索的难题 你的请求被路由到 StaticFiles另外,SPA支持功能更是闻所未闻。你可以提供文件服务、渲染模板,或者接入任何你想要的组件,但现代JavaScript前端与Python应用之间的最后一公里连接,基本上还是留给读者自己去探索的。

而那个读者越来越像我了。

与此同时,在前端领域

我用 Vue 来完成这项工作。Vue 与 React 和 Svelte 处于大致相同的应用领域:基于组件的界面、响应式状态、客户端路由,以及将一堆源文件转换为浏览器可执行代码的常见现代技术栈。

然而,这个软件包也可以与其他软件配合使用,所以我会简要介绍一下它的主要竞争对手 fastapi-vue 然而,该软件包也可以与其他软件配合使用,因此我将简要介绍一下主要的竞争对手。

反应式编程框架 React

React 堪称行业巨头。其生态系统规模庞大,几乎能解决所有问题,而且通常还会出现三个相互矛盾的解决方案来挑战最初的方案。

它最大的实用优势在于其天然的适配性:许多开发者对它了如指掌,众多库都以它为核心目标,许多示例代码也以它为起点。相较于 Vue,它的受欢迎程度要高得多,这也恰恰说明了 React 与 FastAPI 之间集成程度相对较低这一点颇具启示意义 npm install react它比 Vue 受欢迎得多,这本身就充分说明了 React 与 FastAPI 之间集成程度较低的问题。

就连FastAPI官方推出的全栈解决方案(几周前才正式发布),其核心本质上也是一个模板库:你可以基于其中预先选定的React技术栈进行开发,而非通过某种工具直接接入现有Python应用程序,并将前端界面集成到其中。

苗条的

Svelte采用了一种更依赖编译器的开发方式。它不会将大量框架代码直接加载到浏览器中,而是会在构建阶段对组件进行转换。这样一来,即使在生产环境中没有 Node 环境支持,代码仍然能够正常运行。

若想实现正确的响应机制,需要稍作技巧处理。在其自定义语言中,用 $ 符号来标示需要更新的内容。另一方面,这种自定义语言相较于竞争对手的产品,编写代码时更简洁高效。

Vue

Vue 的位置恰到好处:既提供了构建大型应用所需的足够结构,又不会让 HTML 被层层叠叠的 JavaScript 代码所掩盖。其响应式机制搭配 Pinia 存储库,运行顺畅且不显山露水。当你需要极致的性能时,它还能与非响应式架构无缝集成,无需复杂的封装或冗余代码 useState 无需繁琐的模板代码。

而且它的运行速度很快,这一点与大多数 React 应用的情况不同。

官方工具在这里为我提供了特别实用的功能:我无需自己构想 Vue 项目应该是什么样子了。它能互动式地询问应用是否应该使用 TypeScript、Vue Router、Pinia、Vitest、Playwright/Cypress、ESLint、Prettier 以及其他常见的开发工具 create-vue 这款工具在这里为我提供了特别实用的功能:我无需自己构想 我的 它让我无需自己构想 Vue 项目应有的样子——它能以交互式方式询问应用是否应使用 TypeScript、Vue Router、Pinia、Vitest、Playwright/Cypress、ESLint、Prettier 等常用工具包。

这一点在之后会变得很重要。

Vue、Svelte、React
解决一个在应用中频繁出现的简约网页表单:一个下拉菜单,用于选择要编辑的记录,并为其各个字段提供输入。Svelte 的代码最简洁,但 Vue 只需编写一个普通的浏览器入口页面 index.html,实际长度几乎相同。再看 React,代码量太大,根本无法完整截图。所有工作都是用 React 完成的,这张图就很好地说明了原因。

我们到底该怎么部署这个东西啊?

假设我一方面使用FastAPI,另一方面使用Vue、React或Svelte,那么将最终的应用部署到服务器上,有几种截然不同的方法可供选择。

选项 1:向 Node 投降

显而易见的 JavaScript 开发者会给出的解决方案,就是将 JavaScript 服务器也投入生产环境中运行。

如果我确实需要一个 Node 后端,这么做就非常合理了。在应用链路的两端使用同一种语言和紧密相关的工具,确实有其优势。如果我的团队希望在整个项目中都使用 TypeScript,那这无疑是一个非常连贯的架构设计。

我不这么认为。

我选择了FastAPI,因为我想用Python编写后端。另外再安装一个运行环境来交付前端资产,就好比请了第二个厨师专门负责把菜从厨房端到餐厅一样——既费时又费力。

图片
我想在我的生产服务器上装满数GB的node_modules文件夹和这些依赖项吗?当然不想啊。

还有一个小问题,那就是 JavaScript 依赖库的生态系统。它体积过大,兼容性差,经常出现崩溃问题。虽然可以轻松安装正确的 Node 版本,但这仍会占用大量空间,而托管服务器往往没有足够的存储空间来容纳这些依赖库 nvm 尽管可以轻松安装正确的 Node 版本,但这仍会占用大量空间,而托管服务器通常没有足够的存储空间。

节点(Node)在我的开发机器上非常实用,Vite 更是出色至极。 create-vue 太棒了。构建生态系统的工作做得非常出色。

这并不意味着我希望这些东西出现在生产环境的机器上。

选项 2:在服务器上渲染所有内容

相反地,我也可以完全跳过SPA,直接用Python生成HTML页面。

这项技术本应得到比它目前所获得的更多尊重。并非每款应用都需要客户端应用运行环境、路由器以及数兆字节的配套支持代码。

像htmx这样的工具能够通过在HTML属性中直接添加HTTP请求、交换操作、过渡效果、WebSocket和服务器推送事件,将服务器渲染的HTML页面提升到令人惊叹的水平。再结合html5tagger在服务器上生成这些文档和HTML片段,你就能打造出一个在何设备上都能正常运行,并且能被社交媒体、搜索引擎和代理程序等平台无缝解析的网站。

对于那些以文档、表单和相对简单的交互为主的网站,我很喜欢这种架构。浏览器发出请求,Python生成HTML页面,无需构建一个能发射火星探测器的构建管道。

Vasanko.com正是基于这种方法构建的,只是在需要更强交互性的管理界面中引入了Vue框架。该内容管理系统基于FastAPI-Vue架构运行,但所有页面都是在Python后端进行渲染的。

选项 3:运行两台服务器

因此,常见的折中方案是:

  • FastAPI 负责运行该 API。
  • Vite 或其他 JavaScript 服务器负责运行前端部分
  • 反向代理将两者都置于同一个主机名之下。
  • 如今,生产环境中存在两个应用程序、两个依赖关系堆栈、两个进程,以及一个用于整合各部分的配置文件。

这种方法虽然能行,但效果并不理想。

浏览器并不在意代码是由 Python、Node、Caddy、nginx 还是一台性能足够强大的烤面包机生成的。一旦前端构建完成,最终呈现的将是静态的 HTML、CSS、JavaScript 代码,以及字体和图片资源 app.js 无论是Python、Node、Caddy、nginx,还是一台决心十足的烤面包机生成的内容,浏览器都无所谓。前端构建完成后,最终呈现的都是静态的HTML、CSS、JavaScript、字体和图片。

既然产品已经下线了,为什么还要让工厂继续运转呢?

用 JavaScript 进行开发,用 Python 进行运行

这便成了核心理念的核心所在 fastapi-vue-setup.

我希望 JavaScript 开发工具能真正发挥作用,让开发过程更加高效便捷。

  • create-vue 来创建前端界面
  • 用于开发服务器的 Vite 工具
  • 更改内容后可立即进行热重载
  • 在 Node、Bun 或 Deno 上进行常规的 Vue 构建

然后我希望它消

生产环境下的输出成果应当是一个 Python 包,内含一个已构建完成的前端界面。安装该包时,不应需要 Node、npm、Vite、Vue 源代码,也无需访问传统上存储在某个目录下的 ”引力异常”(gravitational anomaly)文件 node_modules.

如今开发项目正是如此操作的:先运行前端构建流程,将生成的资源文件整合至 Python 包内。生成的 Hatch 配置会将这些资源视为构建产物,并与源代码分发构建流程相衔接,同时将包内容严格限制在 Python 包本身范围内 fastapi-vue-setup 如今构建项目的方式如下: uv build 运行前端构建,并将生成的资产包含在 Python 包中。生成的 Hatch 配置将其视为一个构件,并挂钩源代码分发的构建过程,同时将包内容限制在 Python 包本身内。这正是当今构建项目的标准流程:运行前端构建,并将生成的资产包含在 Python 包中。生成的 Hatch 配置将其视为一个构件,并挂钩源代码分发的构建过程,同时将包内容限制在 Python 包本身内 frontend-build 它将其视为一个构件,并挂钩源代码分发构建流程,同时将包内容限制在 Python 包本身范围内。

这种区分对软件开发者来说尤为重要。我不希望源代码分发包给用户时意味着:“这里有一些Python代码,还有一整套Vue开发环境,现在请你自己安装Node并重建实际应用。” 在分发包离开我的开发环境之前,前端部分就已经完成了编译工作。

该软件包包含了其运行所需的全部组件,但不包括我为了编译它而临时需要的所有依赖项。

这样一来,部署工作就显得格外枯燥乏味了:

uvx my-app

目标机器上没有 Node 安装仪式。没有 npm install无前端服务器。无需担心三个月后某个JavaScript依赖树突然活跃起来并要求被加载。

该程序会启动 FastAPI,而 FastAPI 会负责为应用程序提供服务。

另一个令人头疼的问题

搭建前端界面只是整个项目的一半工作。

StaticFiles 严格来说不算是一个前端服务器

FastAPI 暴露了 Starlette 的相关功能,针对普通静态资产,它的功能完全符合其宣传承诺:即提供无缝的静态资源加载服务 StaticFiles,对于普通的静态资产,它的功能正如其名所示:

app.mount("/static", StaticFiles(directory="static"))

问题往往出在前端是网站本身的时候

一个 Vue 应用通常需要 /它可能还需要 /login, /settings, /dashboard/coffee-reactor/7以及客户端路由器所拥有的其他任何资源。

但进行挂载操作时,实际上会将整个剩余的 URL 空间分配给挂载的应用程序。FastAPI 的官方维护者多年前就解释过这个问题:如果将其挂载到根目录,静态应用程序就会接管整个目录,导致其下方的常规路径操作无法正常运行 StaticFiles 然而,将应用程序挂载到根目录实际上会占用整个剩余的 URL 空间。FastAPI 的官方维护者多年前就解释过这个问题:如果将应用挂载到根目录,静态应用就会接管整个目录,导致其下方的常规路径操作无法正常运行 / 这样做实际上等于将剩余的整个 URL 空间交给了已安装的应用程序。FastAPI 的官方维护者多年前就解释过这个问题:如果将其挂载到根目录,静态应用程序就会接管整个路径空间,导致其下方的常规路径操作无法正常运行。

并且我们还需要:

  • 前端构建在 /*
  • FastAPI 仍然会处理
    • /api/...
    • /openapi.json
    • 任何其他任意路线

这和提供服务不是同一个问题 /static/logo.svg.

FastAPI最近大幅提升了其自身的前端支持能力,目前的文档明确建议前端应用使用FastAPI提供的API接口,而非直接使用原始的API接口。不过,FastAPI早在此之前就已推出前端支持方案,但两者的功能略有不同:FastAPI是整个打包系统的轻量级运行时组件,专注于提供高效的API调用服务 app.frontend() 相较于普通的 StaticFiles然而,FastAPI最近大幅提升了其前端支持能力,目前的文档明确建议前端应用使用 格式而非纯文本格式。但 早于该解决方案问世,且其功能略有不同:它是这套完整打包系统的轻量级运行时组件 fastapi-vue 它早于该解决方案问世,并且承担着略有不同的职责:它是这个完整打包系统的轻量级运行时组件。

运行时:FastAPI-Vue

伴随组件 fastapi-vue 该配套包提供了一个自定义的处理程序,而非直接挂载到应用程序中 Frontend 使用自定义处理程序而非直接挂载 StaticFiles 关于应用程序。

在SPA模式下,它可以返回 index.html 针对属于客户端路由器的路径,无需使用同名物理文件即可访问。

Note

由于FastAPI路由的工作机制,应用程序会接管该路径下的所有请求。剩余的路由会按顺序尝试,其中第一个匹配成功的路由将被优先执行。因此,在SPA模式下,我们必须将通用路由(catch-all)放在应用模块的最后位置。

禁用SPA模式后,它只会绑定到实际文件的路径,因此即使在禁用SPA模式后,您的路由仍能捕获所有未被处理的请求。

它还能处理那些不那么吸引人的细节,我可不想每周二都重新去实现这些功能:

  • ETag 还有 Last-Modified
  • 针对已构建资产的不可变缓存
  • 采用 zstd 压缩技术的随机存取存储器(RAM)缓存功能
  • SPA 路由回退机制,以及 /favicon.ico 必要时
  • 切勿在开发模式下意外提供过时的构建版本

这是生产环境中的运行时依赖项。它体积很小,且仅适用于 Python 环境。

用于构建和开发 Vue 应用的所有工具都保存在源代码项目中。

输入 fastapi-vue-setup

把这些部件安装到位后, fastapi-vue-setup 主要任务是去除重复的布线部分。

基本命令故意设计得比较平淡:将其指向你的应用程序文件夹,或者 . 如果你已经在那里了。

uvx fastapi-vue-setup my-app

需要更改你的生产环境、Vite 以及开发后端默认使用的端口号吗?使用以下命令运行 --ports若不启用该选项,系统将保留您之前配置的端口。您随时可以通过命令行界面(CLI)或在运行时调整设置,修改您的生产环境、Vite 以及开发后端默认使用的端口号 --listen 在你的命令行界面(CLI)上或者 scripts/devserver.py 若想在运行时更改相关设置,

但这背后隐藏着两个截然不同的情况。

它为你的应用程序所获取的 CLI 入口点提供了支持工具,这些工具还能处理你自定义的命令行选项,而这正是其他工具无法做到的 fastapi run 无法提供。

创建一个新的应用程序

针对一个新项目,我不想 fastapi-vue-setup 我不想强制使用自己制作的固定 Vue 模板。我希望它能创建一个 Python 项目,项目名称由我指定,并采用我选择的 Vue 配置方案:

  • JavaScript or TypeScript
  • Vue Router or no router
  • Pinia or not
  • Testing, linting and formatting choices
  • The other options supported by the current create-vue

随后,该工具会围绕开发者实际选择的应用程序,构建FastAPI集成功能。

这很重要,因为模板不可避免地会固化某人的偏好。六个月后,他们眼中所谓的现代全栈架构可能就已经过时了,而你只能被迫接受现有的局面。

我宁愿能自己做选择。

安装完成

已经有应用程序了?

到这里,可能我已经有了一个真实的项目,其中包含一些我不希望被删除的代码。它可以用同一个脚本创建,或者作为独立项目存在,而脚本会在需要时对其进行修补。

安装脚本会检测 Python 项目和后端模块,检测或创建 Vue 前端,并对可以安全集成的部分进行修补,只需进行最少的更改即可。

Note

将现有的 Vue 完全移至 frontend/ 首先,我们将 Vue 放置在那里,以避免让根目录被 Node 相关的代码污染。将其置于此处,是为了让脚本能够对现有应用进行修补,而非创建一个全新的应用。

需要升级到最新版本吗?只需运行一次即可 fastapi-vue-setup 再次运行,它会自动更新并添加新功能。

这是它与模板库的主要区别之一 。

A template says:
Fork this repository to start your project

I needed tooling that can also say:
Fine, you’re already 30,000 lines in. Show me where the patient is.

你的应用程序已经可以运行了

uv run scripts/devserver.py

vite.config.js

配置哪些路径被代理到后端。默认情况下,仅有 /api这种配置仅影响开发环境,在生产环境中,所有请求都会直接转发到FastAPI服务器。

JS_RUNTIME

Environment with value bun/deno/node or path to one of them (otherwise we find one).

这样一来,Vue/Vite开发服务器和支持重载功能的FastAPI就启动了。生成的Vite集成代理会将后端请求转发到Python开发服务器。你的浏览器会连接到Vite服务器。

因此,在开发过程中,我仍然能享受到JavaScript生态系统中那些真正令我喜爱的特点。

大家好,我正在连接FastAPI服务

开发工作完成后:

uv build  # Installs and (re)builds everything
my-app    # CLI entry point provided (in .venv)

Vue 已完成编译,其编译结果已被整合到 Python 发行版中。构建引擎已完成任务。若您愿意,可以将您的包打包发布,或者直接从开发环境复制到生产环境,然后进行安装并运行 uv publish 如果你愿意,可以将你的包打包发布,或者直接从开发环境复制到生产环境中 dist/然后,进行安装并运行:

uv tool install my-app-0.1.0.tar.gz
my-app

安装前我只需要安装 紫外线(UV) 还有在开发环境中 Node 它本身就没问题,我根本不需要去动它 npm 根本不需要做任何额外操作,现在我可以在其他地方安装或运行它,而无需把 JavaScript 工作室也一并带过去。

终于只需提交一份申请了

实际上,我想要的并不算什么特别稀奇的东西。

  • 我本想用 Python 来开发后端部分。
  • 我本想用 Vue 来开发前端部分。
  • 在开发Vite的过程中,我一直希望能用上它。

并且我希望能快速搭建并部署一个东西。

这不是一个 Python 应用程序加一个 JavaScript 应用程序,也不是两个容器通过 Nginx 连接在一起,彼此互相猜忌。

A <1MB Python package that Just Works™.

它包含前端部分。FastAPI 会从站点根目录提供服务,而不会将应用程序的其他部分一并处理。当我需要构建单页应用(SPA)时,Vue 路由机制就能派上用场;而普通文件路由则能让后端服务器专注于处理通用请求,例如处理带有服务器端渲染内容的漂亮网址(就像本站点一样)。反过来,后端也可以在需要的地方使用 Vue,两者完美协同,相得益彰。

The JavaScript toolchain does what a toolchain is supposed to do:

它先开发出软件,然后就退居幕后,不再插手后续事务

接下来,您的全栈应用需要一个数据库,而 Kanta 能为您提供全面支持。借助这套工具链,您的应用很快就能上线运行了。