Wasp 的技术愿景:用声明式 Spec 描述整座 Web 应用
2026/9/15 15:20:04 网站建设 项目流程

Wasp 的技术愿景:用声明式 Spec 描述整座 Web 应用

【免费下载链接】waspThe batteries-included full-stack framework for the AI era. Develop JS/TS web apps (React, Node.js, and Prisma) using declarative code that abstracts away complex full-stack features like auth, background jobs, RPC, email sending, end-to-end type safety, single-command deployment, and more.项目地址: https://gitcode.com/GitHub_Trending/wa/wasp

导读

本文以 web/versioned_docs/version-0.15/vision.md 为骨架,解读 Wasp 的核心设计哲学:它不是又一个 Web 框架库,而是一门面向 Web 应用的声明式描述语言(DSL / Spec)。文章将逐一拆解愿景中"Entity 一等公民、开箱即用 CRUD、智能查询与操作、逃生舱机制、由框架托管基础设施"等构想,并用当前仓库中的@wasp.sh/spec构造器实现与main.wasp.ts示例逐一印证。读完你既能理解 Wasp 为什么这样设计,也能看到这些理念在今天的代码库中是如何落地的。

出发点:让开发 Web 应用像写需求文档一样简单

愿景文档的第一句话就定下了基调:"With Wasp, we want to make developing web apps easy and enjoyable, for novices and experts in web development alike."——无论是新手还是专家,用 Wasp 开发 Web 应用都应当既简单又愉快。

理想的编程体验是:用 Wasp 写应用,感觉像是在用人类语言描述一个应用,像撰写一份规格说明文档——你主要描述"需求"(what you want),而不是"实现细节"(how it's done)。同时,从零创建一个可以上生产的 Web 应用应当容易,把它部署到生产环境应当顺畅直接。

在 Wasp 0.15 时代的愿景中,这个目标的载体被明确为"一门编程语言(DSL)";而在当前仓库的 web/docs/vision.md(及version-0.25版本)中,这一表述演进为 "spec-driven framework"、"Declarative, statically typed spec"。语言从"DSL"转向"静态类型 Spec",但内核一致:用户声明需求,Wasp 负责实现

为什么必须是"语言/Spec"而不是库

愿景给出了一个关键论断:"we believe Wasp needs to be a programming language (DSL) and not a library - we want to capture all parts of the web app into one integrated system"

理由是:Web 应用横跨前端、后端、数据模型、认证、任务调度等多个层面,一个库只能寄生在某一层生态里;而 Wasp 想把 Web 应用的所有部分捕获进一个为它量身定制的、集成的系统。只有让 Wasp 处于"语言层",它才能统一描述路由、页面、数据操作、认证这些跨层概念。

今天的仓库正是这样实现的:waspc/data/packages/spec/src/spec/publicApi/constructors.ts定义了apppageroutequeryactioncrudjobapi等一套构造器。以 examples/ask-the-documents/main.wasp.ts 为例,整个应用的骨架(名称、认证方式、路由、操作)浓缩在一个声明式文件中:

import { action, app, page, query, route } from "@wasp.sh/spec"; export default app({ name: "askTheDocuments", wasp: { version: "0.26.0" }, title: "PG Vector Example", auth: { userEntity: "User", methods: { google: { userSignupFields, configFn: getGoogleAuthConfig } }, onAuthFailedRedirectTo: "/", }, spec: [ route("RootRoute", "/", page(Main), { prerender: true }), query(getDocuments, { entities: ["Document"] }), action(askDocuments, { entities: ["Document"] }), action(deleteDocument, { entities: ["Document"] }), ], });

这正是"用声明式规格描述应用"的直观体现——没有一行 Express 路由代码,也没有手工搭建认证中间件。

声明式"胶水"代码:不替代、只粘合

愿景同时强调:"trying to capture every single detail in one language would not be reasonable"。React 之于组件、CSS/HTML 之于样式与标记、JS/TS 之于逻辑,这些专用方案已经解决得很好了,Wasp 不打算用自己替代它们。

Wasp 的定位是声明式"胶水"(declarative glue):它把这些专用方案粘合成一个整体,并在它们之上提供更高层次的 Web 应用抽象。

这一点在仓库中随处可见。constructors.ts中的page()直接接收一个 React 组件引用:

export function page(component: Page["component"], config?: PageConfig): Page { return { kind: "page", component, ...config }; }

main.wasp.ts中通过with { type: "ref" }引入外部源码文件:

import { Layout } from "./src/Layout" with { type: "ref" }; import { Main } from "./src/pages/MainPage" with { type: "ref" };

也就是说,复杂业务逻辑仍用 JS/TS 和 React 编写(通过ref引用),Wasp Spec 只负责描述"它们如何被组织进应用"。愿景中的"可内联混用,也可通过外部文件提供"在 examples/kitchen-sink/main.wasp.ts 中得到了双重体现——既有直接写在main.wasp.ts里的emailSenderwebSocket配置,也有通过import { crudSpec } from "./src/features/crud/crud.wasp"拆分到多个 Spec 文件/模块的做法。

"横向语言":理解 Web 应用概念的多文件 Spec

愿景设想 Wasp 是一门"horizontal language":规则简单,却理解大量 Web 应用概念(路由、认证、数据操作、定时任务……),并支持多文件/模块与库。

当前仓库的@wasp.sh/spec正是对"横向"的兑现:constructors.ts中的构造器覆盖了 App、Page、Route、Query、Action、Api、ApiNamespace、Job、Crud 等全部应用概念,并且每个都是"简单的基本规则"——以job为例:

export function job(fn: Job["fn"], config: JobConfig): Job { return { kind: "job", fn, ...config }; }

而 kitchen-sink 示例把这一理念发挥到极致:main.wasp.ts只做汇总,认证、CRUD、任务、流式输出等各自定义在独立的.wasp.ts模块中(如src/features/jobs/jobs.waspsrc/features/crud/crud.wasp),再统一装配进spec数组——这就是愿景所说的"多文件/模块、库"支持。

Entity:一等公民的数据模型

愿景将数据模型列为一等公民(first-class citizen):Entity 用 Wasp 自身语法定义,与其余功能紧密集成,是一切围绕的中心概念。

在今天的仓库中,数据模型由 Prisma 定义(schema.prisma),并通过 Wasp 与之深度绑定。以 examples/ask-the-documents/schema.prisma 为例:

model User { id Int @id @default(autoincrement()) email String? } model Document { id String @id @default(uuid()) title String url String @unique content String embedding Unsupported("vector(1536)") createdAt DateTime @default(now()) updatedAt DateTime @updatedAt }

注意两点:其一,askTheDocuments应用是PG Vector 向量检索示例——embedding字段直接声明为Unsupported("vector(1536)"),并在datasource中启用了pgvector扩展,展示 Wasp 应用同样能承载 AI 时代的向量数据;其二,Wasp 的每个操作都通过entities: ["Document"]声明其访问的数据模型,让 Entity 成为连接 Spec 与业务逻辑的枢纽。

开箱即用的 CRUD

愿景承诺:基于 Entity 提供开箱即用(out of the box)的 CRUD UI,让应用快速跑起来,同时保持一定程度的可定制性。

constructors.ts中的crud()构造器是这一承诺的实现:为某个 Prisma Entity 自动生成查询与操作(查询/动作),每个操作既可启用默认实现、可设为公开(isPublic),也可用overrideFn替换为自定义实现:

export function crud(name, entity, operations): Crud { return { kind: "crud", name, entity, operations }; }

kitchen-sink 示例 examples/kitchen-sink/src/features/crud/crud.wasp.ts 展示了实际用法:

crud("tasks", "Task", { getAll: { isPublic: true }, get: {}, create: { overrideFn: createTaskOverride }, update: {}, });

"可定制到一定程度"体现在两个维度:isPublic控制是否免认证访问,overrideFn则允许注入自定义的创建/更新逻辑——这正是愿景中"defaults 加 customization"模式的落地。

"智能"操作:Query 与 Action,弥合客户端-服务端鸿沟

愿景描述了一个理想状态:"Smart" operations (queries and actions)在大多数情况下自动判断何时需要更新,即便不满足也很容易通过自定义逻辑补偿;用户尽可能少地为客户端-服务端之间的鸿沟操心。

constructors.tsquery()action()的 JSDoc 恰好把"自动"部分讲清楚了:

  • Query是服务端只读操作,客户端通过useQuery调用并缓存;在config.entities中列出其读取的 Entity 后,Wasp 会把对应的 Prisma 委托注入context.entities并在相关 action 修改这些数据时自动使客户端缓存失效(cache invalidation)。
  • Action是服务端写操作,同样在entities里声明后,运行时会触发相关 Query 缓存的失效。

也就是说,"操作声明了它动哪些数据"这一条信息,被 Wasp 用来完成自动的数据同步,让客户端代码无需手动刷新。以 ask-the-documents 为例,前端只需useQuery(getDocuments),当embedDocument等 action 写入Document后,列表会自动更新——这正是"智能操作"愿景在源码层面的直接证据。

逃生舱(Hatches):需要时才出现的定制入口

愿景提出:Wasp 应在所有正确的位置提供逃生舱(escape mechanisms/hatches),允许定制应用,但这些机制平时保持隐藏,直到你需要它们。

仓库中的典型逃生舱包括:

  • overrideFn:CRUD 自动生成的实现可被自定义函数整体替换(见上文 crud 示例),不必推翻整个 Spec。
  • 自定义 API 端点api(method, path, fn)用于 Webhook、文件上传等 query/action 模型容纳不了的 HTTP 交互,apiNamespace还能为某路径前缀批量挂中间件。
  • 服务端/客户端 Setup 与中间件:examples/kitchen-sink/main.wasp.ts 中通过server.setupFnserver.middlewareConfigFnclient.setupFn挂入自己的初始化逻辑与中间件配置。
  • ref导入:用with { type: "ref" }引入任意外部 JS/TS 代码,这是最大的逃生舱——任何 Wasp 没覆盖的逻辑都能用原生代码写。

这些机制平时不占用心智,需要时随时打开,正是"hidden until you need them"。

由框架托管的基础设施关注点

愿景明确:服务端渲染(SSR)、缓存、打包、安全等一律由 Wasp 负责——"你告诉 Wasp 你想要什么,Wasp 自己想办法做到"。

仓库中的落地证据包括:

  • 预渲染(prerendering)route("RootRoute", "/", page(Main), { prerender: true })中的prerender选项让路由在构建期渲染为静态 HTML,对应 web/docs/advanced/prerendering.md;constructors.tsroute()JSDoc 还说明可传入具体路径数组来预渲染动态路由的特定实例。
  • 懒加载route()支持lazy配置决定是否关闭页面 bundle 的懒加载。
  • 环境变量校验server.envValidationSchema/client.envValidationSchema让配置在启动时即被 Zod 之类的 schema 校验(见 examples/kitchen-sink/main.wasp.ts)。
  • 认证安全auth配置块统一声明用户实体、登录方式与失败跳转路径,认证基础设施由 Wasp 生成。

部署:尽最大可能地简单

愿景要求"as simple deployment to production/staging as it gets"。仓库中每个示例(如examples/ask-the-documents/fly-server.tomlexamples/ask-the-documents/fly-client.tomlexamples/waspello/fly-server.toml)都预置了面向 Fly.io 的部署配置,examples/kitchen-sink/Dockerfile则提供了容器化路径;scripts/get-wasp-database-provider.sh与 web/docs/deployment/intro.md 也都在支撑"一条命令部署"的体验。部署时 Wasp 负责构建产物、数据库迁移与静态资源托管,用户只需声明目标环境。

不绑定单一实现:开放的语言层

愿景的最后一条是极具前瞻性的设计约束:"the Wasp Spec will not be coupled with the single implementation"——官方提供参考实现,但他人可以提供编译到不同 Web 技术栈的其他实现

这一条意味着 Wasp 的"语言/规格"与其生成器(编译器)是分离的。从仓库结构看:waspc/src/Wasp/Generator/是官方编译器(Haskell 实现),waspc/data/packages/spec/定义 Spec 的构造器与类型,examples/下的各示例通过@wasp.sh/spec描述应用。Spec 层保持技术栈无关,理论上允许第三方实现面向别的技术栈的编译器——这也是"Wasp 是语言而不是库"这一判断的必然推论。

从愿景到现实:一句话总结

0.15 时代的愿景文档描述的是"我们想象中 Wasp 的样子",而当前仓库已经把它变成了"Wasp 现在就是这个样子":@wasp.sh/spec提供了一套理解 Web 应用概念的声明式构造器,Entity 是一等公民,CRUD 开箱即用,query/action 自动管理缓存同步,hatches 在需要时随时打开,SSR/预渲染/安全/部署由框架托管。开发者要做的,仍然是愿景开篇那句话——像写规格说明一样描述你的应用,剩下的交给 Wasp。

想深入验证这些理念,可以从这几个文件开始:examples/ask-the-documents/main.wasp.ts(最小而完整的 Spec)、examples/kitchen-sink/main.wasp.ts(全功能示例的装配)、waspc/data/packages/spec/src/spec/publicApi/constructors.ts(构造器的权威定义),以及最新愿景文档 web/docs/vision.md。

【免费下载链接】waspThe batteries-included full-stack framework for the AI era. Develop JS/TS web apps (React, Node.js, and Prisma) using declarative code that abstracts away complex full-stack features like auth, background jobs, RPC, email sending, end-to-end type safety, single-command deployment, and more.项目地址: https://gitcode.com/GitHub_Trending/wa/wasp

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询