返回

文章详情

展示 HN: Frond - 你应用依赖图的前端运行时

Hacker News2026年7月1日 12:10

React 不是你的运行时,Frond 才是。你的应用已经有了运行时,它分散在提供者、效果和清理脚本中。Frond 将其转变为一个图。Effect 运行它,而 React 仍然是一个渲染器。每个不断成长的前端应用都会遇到相同的问题:服务之间是如何相互依赖的,以及在当前用户改变时需要清理什么。大多数不断成长的前端应用都会碰到相似的形状。实现是一个你手动维护的清单。没有 Frond 的情况如下: /sign-out 清单 async function signOut () { await session.end(); // ↓ 手动列出每个用户作用域的东西。 localStorage.removeItem("token"); queryClient.clear(); // 缓存查询 abortInFlightRequests(); // 打开的请求 presenceChannel.leave(); // 实时存在 socket.disconnect(); // 实时传输 billingStore.reset(); // 域存储 navigate("/login"); // 新添加的用户作用域服务? // 记得在这里添加一行。 } 手动记忆 每新增一个用户作用域服务就要增加一行来记住。漏掉一行,旧用户可能会通过存储、套接字、分析身份、过时的更新或正在进行的请求泄露进来。使用 Frond: frond / auth 操作类型 SessionSpec = Frond.NodeSpec<{ readonly args: Frond.Args.None; readonly key: Frond.Key.Singleton; readonly result: Session; }>; export class SessionNode extends Frond.NodeBase<SessionSpec> { static readonly spec = Frond.serviceSpec<SessionSpec>({ tag: Frond.tag("app/session"), key: () => Frond.Key.singleton(), driver: Frond.Driver.Async<SessionSpec>({ acquire: Frond.Driver.Acquire(({ signal }) => restoreSession(signal)), }), }); } // 一次调用——每个依赖节点都被驱逐、中断和释放。 function useSignOut () { const controls = FrondReact.useNodeControls(SessionNode, {}); return () => controls.evict("selfAndDependents", "sign-out"); } frond / 用户作用域资源类型 PresenceSpec = Frond.NodeSpec<{ readonly args: Frond.Args.None; readonly key: Frond.Key.Singleton; readonly deps: { readonly socket: Frond.Dep<typeof SocketNode>; readonly session: Frond.Dep<typeof SessionNode>; }; readonly result: PresenceChannel; }>; export class PresenceNode extends Frond.NodeBase<PresenceSpec> { static readonly spec = Frond.resourceSpec<PresenceSpec>({ tag: Frond.tag("app/presence"), key: () => Frond.Key.singleton(), dependencies: Frond.dependencies(() => ({ socket: Frond.dep(SocketNode, Frond.Args.none), session: Frond.dep(SessionNode, Frond.Args.none), })), driver: Frond.Driver.Async<PresenceSpec>({ // 在获取时加入用户的存在通道—— // 套接字独自进行心跳。 acquire: Frond.Driver.Acquire(({ deps }) => deps.socket.result.join("presence", { userId: deps.session.result.userId, heartbeat: 5_000, })), // 释放与获取对应—— // signOut() 永远不必了解存在。 release: Frond.Driver.Release(({ node }) => node.result.leave({ reason: "sign-out" })), }), }); } 运行时边界 清理属于获取资源的节点。驱逐会运行释放,取消正在进行的工作,清除准备状态,并拒绝驱逐图记录的过时提交。 关于驱逐和释放的阅读 状态工具 回答 值在哪里存活? Redux / Zustand 值,变更,选择器 React Query 服务器缓存,无效,重试 MobX 可观察域状态 通过 React 连接的上下文值 依然在模型之外 谁拥有生命周期? 在这个值加载之前必须准备好什么? 哪个键身份与此状态关联? 当依赖性变化时,什么取消了正在进行的工作? 谁在驱逐后拒绝过时的提交? 释放、遥测和重置存在哪里? Frond 回答 状态何时被允许存在? 身份 可观察状态 依赖性 准备状态 操作 范围 释放 驱逐 可见状态 缓存结果、可观察字段和计算的 getter 是可见的。Frond 保持这些人性化设计,然后将它们附加到图形身份、准备状态、取消、释放和驱逐上。 运行时生命周期 React 读取一个节点。 MobX 使其可观察。 效果运行工作。 Frond 拥有节点的生存、准备、过时、释放或死亡。 运行时及其图形后端架构驱动返回 node.result deps.x.result useNode() 定义/类型化驱动 类型 ProfileSpec = Frond.NodeSpec<{ readonly args: Frond.Args.None; readonly key: Frond.Key.Singleton; readonly deps: { readonly auth: Frond.Dep<typeof AuthNode>; readonly api: Frond.Dep<typeof ApiNode>; }; readonly result: Profile; }>; export class ProfileNode extends Frond.NodeBase<ProfileSpec> { static readonly spec = Frond.resourceSpec<ProfileSpec>({ tag: Frond.tag("app/profile"), key: () => Frond.Key.singleton(), dependencies: Frond.dependencies(() => ({ auth: Frond.dep(AuthNode, Frond.Args.none), api: Frond.dep(ApiNode, Frond.Args.none), })),

赞助内容

NordVPN Next-gen Antivirus

本站免费、广告极少。如果觉得有帮助,可以请我们喝杯咖啡 —— 任何金额都对持续运营有实际帮助。

请我喝杯咖啡