追求正确性的前端框架:基于Effect构建,架构类似Elm
摘要
Foldkit 是一个基于 Effect-TS 构建、受 Elm 架构启发的新型 TypeScript 前端框架,提供可预测的状态管理、显式副作用,以及内置路由、UI 组件和订阅功能。
暂无内容
查看缓存全文
缓存时间: 2026/07/24 17:02
# Foldkit - 基于 Effect-TS 构建的 TypeScript 前端框架 | Elm 架构
来源:https://foldkit.dev/
FoldkitBeta
## 为**正确性**而生的前端框架。
基于 Effect 构建。架构如同 Elm。使用 TypeScript 编写。
``
npx create-foldkit-app@latest
``
## 声明行为。交付。重复。
React、Vue、Svelte 和 Solid 解决了渲染问题,却把架构留给了你。Foldkit 提供了架构,让你专注于业务领域。
### 可预测的状态
一个不可变的 model 承载着整个应用状态。每一次变化都经过单一的 update 函数。没有隐藏的变异,没有过时的闭包,没有意外。
### 显式副作用
副作用是你从 update 中返回的值,而不是隐藏在处理器中的命令式调用。Command 描述了应该发生什么,运行时则处理何时以及如何发生。
### 优雅地扩展
复杂性呈线性增长,而非指数级。一个 50 个文件的应用程序遵循的模式与 5 个文件的应用程序完全相同,因此每个新功能都会增加结构,而不是与现有代码纠缠在一起。新团队成员阅读代码即可理解。
## 构建于 [Effect](https://effect.website/) 之上。由内而外。
如果你已经了解 Effect,那么 Foldkit 会让你感到自然。如果你刚接触 Effect,Foldkit 是学习它的绝佳途径。
- 每个 Foldkit 应用程序都是一个 Effect
- 所有状态都是一个单一的 Schema
- 副作用被建模为永不失败的 Effect
## 看看它的运行效果。
观察一条消息如何通过 update 流入 model。代码会实时高亮,向你展示每一步正在发生什么。
``
// MODEL
const Model = S.Struct({
count: S.Number,
isResetting: S.Boolean,
resetDuration: S.Number,
})
// MESSAGE
const ClickedIncrement = m('ClickedIncrement')
const ChangedResetDuration = m('ChangedResetDuration', {
seconds: S.Number,
})
const ClickedResetAfterDelay = m('ClickedResetAfterDelay')
const CompletedDelayReset = m('CompletedDelayReset')
// COMMAND
const DelayReset = Command.define(
'DelayReset',
{ seconds: S.Number },
CompletedDelayReset,
)(({ seconds }) =>
Effect.sleep(`${seconds} seconds`).pipe(
Effect.as(CompletedDelayReset()),
),
)
// UPDATE
M.tagsExhaustive({
ClickedIncrement: () => [
evo(model, { count: count => count + 1 }),
[],
],
ChangedResetDuration: ({ seconds }) => [
evo(model, { resetDuration: () => seconds }),
[],
],
ClickedResetAfterDelay: () => [
evo(model, { isResetting: () => true }),
[DelayReset({ seconds: model.resetDuration })],
],
CompletedDelayReset: () => [
evo(model, { count: () => 0, isResetting: () => false }),
[],
],
})
``
Model 状态
count:0
isResetting:false
resetDuration:2
## 电池已包含。
大多数框架要求你自己引入路由库、状态管理器、UI 组件库和表单验证器。Foldkit 将它们作为一个连贯的系统一并提供。
### 路由
类型安全的双向路由。URL 解析为类型化的路由,路由也可以构建回 URL。无需字符串匹配,解析和构建之间不会出现不匹配。
探索路由 (https://foldkit.dev/core/routing-and-navigation)
### UI 组件
为 Elm 架构构建的可访问组件(对话框、菜单、选项卡、列表框、折叠面板等)。易于样式化和定制。
浏览组件 (https://foldkit.dev/ui/overview)
### 子模型
一个自包含的 Model、Messages、update 和 view,嵌入在更大的程序中。子节点将领域事实作为类型化的 OutMessage 暴露,父节点在 update 中处理它们。每个有状态的 Foldkit UI 组件都以 Submodel 形式提供。
探索子模型 (https://foldkit.dev/core/submodel)
### 订阅
将 Model 的一个切片绑定到一个可能发出 Messages 的作用域 Stream 上。当切片持有其值时,运行时打开作用域;当切片发生变化时,运行时关闭作用域。
探索订阅 (https://foldkit.dev/core/subscriptions)
### 托管资源
为浏览器中长期存在的资源(如 WebSocket、AudioContext、RTCPeerConnection)提供 Model 驱动的生命周期。当 Model 需要资源时,运行时获取它;当不再需要时,运行时释放它。
探索托管资源 (https://foldkit.dev/core/managed-resources)
### 字段验证
支持同步和异步的按字段验证。将规则定义为断言,在 update 中应用它们,Model 会跟踪每个字段的状态。
探索字段验证 (https://foldkit.dev/core/field-validation)
### 测试
两个测试原语。Story 通过 update 发送 Messages 并断言生成的 Model 和 Commands。Scene 通过可访问的定位器驱动渲染后的视图,并断言重新渲染的 HTML。
探索测试 (https://foldkit.dev/testing)
### 开发者工具
在应用运行时检查 Messages、Model 状态和 Commands。时间旅行模式可以将你的 UI 回滚到任何过去的 Model。AI 代理可以通过 MCP 连接到相同的数据。
探索开发者工具 (https://foldkit.dev/core/devtools)
### 嵌入
使用 Runtime.embed 在任何宿主应用程序中运行 Foldkit 小部件。宿主通过 Schema 类型化的 Ports 推入数据并接收输出值,并通过 dispose 销毁小部件。
探索嵌入 (https://foldkit.dev/core/embedding)
## 测试读起来就像**故事和场景**。
纯粹的 update 函数意味着纯粹的测试。Story 测试状态机。Scene 通过视图(点击按钮、输入文本)结合可访问的定位器测试功能。无需 DOM,无需模拟。
了解测试 (https://foldkit.dev/testing)
``
import { Scene, Story } from 'foldkit'
import { expect, test } from 'vitest'
// Story — 测试状态机
test('fetch weather updates the model', () => {
Story.story(
update,
Story.with(model),
Story.message(SubmittedWeatherForm()),
Story.model(model => {
expect(model.weather._tag).toBe('WeatherLoading')
}),
Story.Command.expectExact(FetchWeather),
Story.Command.resolve(FetchWeather, SucceededFetchWeather({ weather })),
Story.model(model => {
expect(model.weather._tag).toBe('WeatherSuccess')
}),
)
})
// Scene — 通过视图测试
test('type a zip code, click get weather, see the forecast', () => {
Scene.scene(
{ update, view },
Scene.with(model),
Scene.type(Scene.label('Zip code'), '90210'),
Scene.click(Scene.role('button', { name: 'Get Weather' })),
Scene.expect(Scene.role('button', { name: 'Loading...' })).toExist(),
Scene.Command.expectExact(FetchWeather),
Scene.Command.resolve(FetchWeather, SucceededFetchWeather({ weather })),
Scene.inside(
Scene.role('article'),
Scene.expect(Scene.text('Beverly Hills, California')).toExist(),
Scene.expect(Scene.text('72°F')).toExist(),
),
)
})
``
## 观看你的程序**思考**。
当每一次状态变更都通过 Messages 和单一的 Model 流动时,你就能获得在可变状态框架中不可能实现的开发者工具。每条 Message 都会被记录。每个 Model 状态都是可检查的。点击任意一行,就能看到具体发生了什么变化。
此外,AI 代理可以通过 MCP 连接。它们读取当前的 Model,遍历 Message 历史,并将 UI 回滚到过去的状态。以编程方式访问开发者工具向你展示的相同数据。
该站点运行在 Foldkit 上。请查看本页面右下角的标签,尝试实时开发者工具。
了解开发者工具 (https://foldkit.dev/core/devtools)
Foldkit 开发者工具叠层,正在检查 Foldkit 网站:左侧是编号的 Message 时间线,包含 ClickedLink、ChangedUrl 和 CompletedScrollToTop 等条目;右侧是可展开的 Model 状态树,显示 route、url 和 theme 字段。
## 为**AI**而构建。
Foldkit 应用程序是显式的且可预测的。这使得 LLM 特别擅长生成 Foldkit 代码。同时,也让生成的 Foldkit 代码对人类来说异常容易审查。
AI 代理还可以直接通过模型上下文协议连接到正在运行的 Foldkit 应用。它们读取当前的 Model,检查 Message 历史,将 UI 回滚到过去的状态,并发送 Messages。
设置 AI 辅助开发 (https://foldkit.dev/ai/overview)
## 有什么代价?
Foldkit 要求你以不同的方式思考前端开发。它使用 [Elm 架构](https://guide.elm-lang.org/architecture/),因此没有组件、没有 hooks、没有局部状态。一切都是声明式且结构化的。你需要转变对状态、副作用和视图的思考方式。
这是一种纪律。它会有回报,但确实是一个实质性的要求。
了解它的工作原理 (https://foldkit.dev/core/architecture)
## 它与其他框架相比如何?
Foldkit 是一种不同类型的前端框架。如果你将其与 React、Vue、Svelte 或 Solid 比较,关键区别不在于语法或性能。而在于 Foldkit 规定了架构,而不是将其留给你。
与 React 比较 (https://foldkit.dev/react/coming-from-react)
## 它适合谁
- ### 需要前端的 Effect 开发者 你的后端已经使用了 Effect。Foldkit 是缺失的前端部分:相同的生态系统,相同的模式,无需上下文切换。
- ### 重视正确性的开发者 你希望你的架构能预防 bug,而不仅仅是捕获它们。
- ### 需要保持一致性的团队 一个适用于状态、副作用和视图的模式意味着更少的分歧和更快的上手速度。
- ### 具有复杂状态的项目 认证流程、实时数据、多步骤表单。它们都变成了显式的状态和转换,因此困难的部分仍然可读,而不是隐藏在副作用和 refs 中。
## 它不适合谁
- ### 大型现有 React 代码库 Foldkit 不是一个渐进式采用的方案。它是一种不同的架构,迁移意味着重写。中间路线是嵌入:Runtime.embed 在现有应用中运行 Foldkit 小部件。
- ### 尚未准备好投入 Effect 的团队 Foldkit 大量使用 pipe、可区分联合和 Effect。没有逃生舱。你要么完全投入,要么不。
- ### 需要 React 生态系统的项目 没有 React 组件库,没有 Next.js,没有现有中间件。你是在不同的基础上构建。
- ### 需要服务器端渲染的团队 Foldkit 是一个客户端 SPA 框架。静态生成是可能的,但你需要自己实现(就像我们为此网站所做的那样)。
## 保持更新。
新版本发布、模式分享以及偶尔的深度文章。
相似文章
宣布 Topcoat: 一个使用 Rust 构建全栈响应式 Web 应用程序的框架
Tokio 宣布 Topcoat,一个模块化、开箱即用的 Rust 框架,用于构建全栈响应式 Web 应用程序,完全服务器端渲染,具有类似于 HTMX 的响应式指令。
@cnakazawa: fate 1.0:首个完整的异步 React 元框架 1.0 新特性:* 基于 SSE 的零配置实时视图 * Drizzle 支持 * "Na…
fate 1.0 是一个全新的完整异步 React 元框架,具备基于 SSE 的零配置实时视图、Drizzle 支持、原生 HTTP、Void Router、Vite 插件以及客户端垃圾回收等功能。它旨在通过规范化缓存和组合视图来简化数据获取。
tokio-rs/topcoat
Topcoat 是一个模块化、开箱即用的 Rust 框架,用于构建全栈应用,支持服务端渲染和客户端响应式,无需独立的 API 层。
Prism:一种带类型效应的非纯函数式语言
Prism 是一种新型函数式语言,它结合了代数效应与类型系统,允许在没有单子的情况下使用可变状态及其他效应,同时从外部保持纯函数性。其目标是让效应成为类型系统的一等公民,从而实现优化和安全使用。
Show HN: Frond – 一款管理应用依赖图的前端运行时
Frond 是一款前端运行时,可管理 React 应用的依赖图,并自动处理服务的生命周期与清理工作。