跳到主要内容

6 篇博文 含有标签「Schemas」

TypeSafe definition of a data model

查看所有标签

v0.19:批量 Controller.set(),更快的 TypeScript

· 阅读需 29 分钟
Nathaniel Tucker
Creator of Reactive Data Client
备注

这篇翻译之后英文原文有更新,内容可能已过时。

v0.19 让 Manager 可以在一次 store 更新中写入一整批流式到达的行,把 endpoint 代码的类型检查速度提高约 2 倍,并修复了一批 TypeScript 和 Vue 问题。编码智能体还获得了 Claude Code 插件、Vue skill 和 llms.txt;请参阅面向编码智能体的 Data Client。

新 API:

性能:

其他改进:

  • 修复 TypeScript 7 对包 exports 的模块解析;导入现在会解析到声明文件(#4019)
  • renderDataHook() 在首次渲染挂起时也会运行 provider 的挂载副作用(#4099)
  • Controller.set() 的值按 schema 推导类型,因此 ctrl.set(new schema.All(Todo), 42) 会是 TypeScript 错误(#4133)
  • Controller.set() 可用 Invalidate schema 删除单个 entity,不再需要包成单行批量的变通写法(#4230)
  • 在 new RestEndpoint()、.extend() 和 resource().extend() 中,process() 获得带类型的 params,因此写错路径参数会是 TypeScript 错误,而不是运行时的 undefined(#4183、#4184)
  • getOptimisticResponse() 的返回值按 schema 推导类型,因此字段类型写错会是 TypeScript 错误,而不是闪现一下错误数据(#4198)
  • Vue 的 useSuspense() 和 useLive() 在新参数加载期间保留之前的数据,而不是返回 undefined(#4131)
  • Vue 的 useSuspense() 和 useLive() 把参数变化后的获取错误发送给 onErrorCaptured(),而不是产生未处理的 promise rejection(#4135)
  • Vue 的 useSuspense()、useDLE() 和 useFetch() 不再在每次 store 更新时重新获取过时数据,因此 controller.set() 不会被覆盖(#4134)
  • Vue 的 useSuspense() 在挂载时显示过时数据并在后台重新获取,与 React 一致,而不是在重新获取完成前一直显示 <Suspense> 的 fallback(#4169)
  • Vue 的 useDLE() 和 useCache() 在无关的 store 更新中保留已过期的 invalidIfStale 数据,而不是卡在加载状态(#4142)
  • Vue 的 DataClientPlugin 可以安装在 3.5 之前的 Vue 版本上,而不是抛出 app.onUnmount is not a function(#4146)
  • Vue 的 DataClientPlugin 默认进行垃圾回收,与 React 的 DataProvider 一致,因此你不再需要传入 gcPolicy;它如何决定移除什么(#4148)
  • Vue 组合式函数接受像 () => ({ id: props.id }) 这样的 getter 参数;之前类型上允许,但却把函数本身传给了 endpoint(#4115)
  • 修复在关闭 skipLibCheck 的 TypeScript 4.x 上出现的 Cannot find name 'NoInfer' 和 export type 错误(#4138)
  • 修复在关闭 skipLibCheck 的 TypeScript 4.0–4.5 上 Entity、Endpoint、Union 和 RestEndpoint 的类型错误(#4140)
  • 修复在 TypeScript 4.0–4.4 上导入 @data-client/react/redux、@data-client/react/nextjs 或 @data-client/test 时的 ',' expected 错误(#4232)
  • 修复在 TypeScript 4.0–4.4 上 @data-client/test 的 act 和 renderHook 被推导为 any 的问题(#4251)
  • Entity 类可以用在期望 EntityInterface 的地方(#4149);为未来版本准备好 pk() 重写
  • useCache() 和 useDLE() 对已删除且重新获取失败的 entity 返回 undefined,而不是一个能绕过 if (!data) 检查的真值 Symbol;移除所有变通代码(#4150)
  • Vue 的 useFetch() 在挂载期间保护其数据不被垃圾回收,与 useSuspense() 一致,因此配置的 gcPolicy 不再会清除组件稍后要读取的预取数据(#4152)
  • 打开 Redux DevTools 的应用在开发模式下遇到大型 store 或频繁更新时不再卡顿;每次更新的序列化快 40-60 倍(#4163)
  • 传入 endpoint 覆盖对象的 resource().extend() 会在类型中保留之前用 .extend('name', options) 添加的 endpoint,因此使用它们不再是 TypeScript 错误(#4184)
  • 当 Query 或 Lazy 包装 Collection 时,RestEndpoint.push、unshift 和 remove 接受 Entity 的字段作为 body,而不是只接受 FormData(#4226)
  • Vue 的 useFetch() 的类型现在是它返回的只读 Ref,因此 promise.resolved(始终为 undefined)现在是 TypeScript 错误;请改为读取 promise.value.resolved(#4114)
  • 传给 DataProvider 或 DataClientPlugin 的 Controller 子类不再需要类型断言就能通过类型检查(#4228)
  • prepareStore() 的 store 可以不加类型断言直接传给 react-redux 的 <Provider>,并且其类型中包含 dispatch()(#4238)
  • resource().create 接受 FormData,无需类型断言,与 update、partialUpdate 和 getList.push 一致(#4235)
  • RestEndpoint.push、unshift 和 remove 以 JSON 形式发送数组 body,因此一次创建或移除多个条目不再向服务器发送 "[object Object]";resource().create 也接受数组(#4262)
  • Vue 的 createDataClient() 已弃用,因为它是 DataClientPlugin 的内部实现;请用 app.use(DataClientPlugin, options) 安装插件,而不是直接调用它(#4193)
  • Vue 的 renderDataCompose() 的 waitForNextUpdate() 已弃用,因为它会在 1 秒后静默放弃,让仍处于挂起状态的测试通过;请改为 await result,或在变化之后 await allSettled(),后者现在也会等待由 prop 变化触发的获取(组合式函数的单元测试、#4221)

破坏性变更:

为未来版本做好准备:

v0.18:Scalar、类型化文件下载、过滤感知的 Collection

· 阅读需 12 分钟
Nathaniel Tucker
Creator of Reactive Data Client

v0.18 聚焦于三件事:对随请求上下文变化的值进行更丰富的数据建模、更简单的类型化下载,以及更贴近真实 API 过滤条件的集合匹配。

新特性:

其他改进:

Fixtures
▶api/Company
import { Collection, Entity, RestEndpoint, Scalar } from '@data-client/rest';

export class Company extends Entity {
  id = '';
  name = '';
  price = 0;
  pct_equity = 0;
  shares = 0;
}

const PortfolioScalar = new Scalar({
  lens: args => args[0]?.portfolio,
  key: 'portfolio',
  entity: Company,
});
Company.schema = {
  pct_equity: PortfolioScalar,
  shares: PortfolioScalar,
};

export const getCompanies = new RestEndpoint({
  path: '/companies',
  searchParams: {} as { portfolio: string },
  // `portfolio` is a lens, not a filter — the returned Company list is the same
  // regardless of lens. Dropping it from `argsKey` collapses every portfolio to
  // one Collection pk, so `Collection.queryKey()` finds the list on every
  // switch and `useSuspense` reuses it without refetching.
  schema: new Collection([Company], { argsKey: () => ({}) }),
});

export const getPortfolioColumns = new RestEndpoint({
  path: '/companies/columns',
  searchParams: {} as { portfolio: string },
  schema: new Collection([PortfolioScalar], {
    argsKey: ({ portfolio }: { portfolio: string }) => ({ portfolio }),
  }),
});
▶CompanyGrid
▶PortfolioGrid
结果
Store▶

这个示例展示了同一批 Company 实体通过不同的投资组合视角进行渲染。稳定的 Entity 字段会被复用,而随视角变化的值则从独立的 Scalar 单元格中选取。

破坏性变更:

**在使用 AI 智能体?**安装迁移 skill——它会运行 codemod,为依赖参数的 schema 注册 argsKey,并处理 codemod 无法覆盖的情况(类字段、动态方法赋值、SFC <script> 块、自定义辅助函数):

npx skills add reactive/data-client --skill data-client-v0.18-migration

否则,直接运行 codemod——它覆盖了常见情况:

npx jscodeshift -t https://dataclient.io/codemods/v0.18.js --extensions=ts,tsx,js,jsx src/

v0.16:并行获取、Collection.move、Lazy

· 阅读需 13 分钟
Nathaniel Tucker
Creator of Reactive Data Client

新特性:

其他改进:

Collection.move 让你能够用一次操作轻松地在多个 Collection 之间移动实体:

import { useController } from '@data-client/react';
import { TaskResource, type Task } from './TaskResource';

export default function TaskCard({ task }: { task: Task }) {
  const handleMove = () => ctrl.fetch(
    TaskResource.getList.move,
    { id: task.id },
    { id: task.id, status: task.status === 'backlog' ? 'in-progress' : 'backlog' },
  );
  const ctrl = useController();
  return (
    <div className="listItem">
      <span style={{ flex: 1 }}>{task.title}</span>
      <button onClick={handleMove}>
        {task.status === 'backlog' ? '\u25bc' : '\u25b2'}
      </button>
    </div>
  );
}
结果

破坏性变更:

使用自动化 codemod 升级,它涵盖所有破坏性变更:

npx jscodeshift -t https://dataclient.io/codemods/v0.16.js --extensions=ts,tsx,js,jsx src/

v0.15:Vue 3、Collection.remove、useDebounce 升级

· 阅读需 10 分钟
Nathaniel Tucker
Creator of Reactive Data Client

新平台:

  • Vue 3,提供完整的组合式函数:useSuspense、useCache、useDLE、useController、useQuery

新特性:

性能:

破坏性变更:

使用自动化 codemod 完成常见破坏性变更的升级:

npx jscodeshift -t https://dataclient.io/codemods/v0.15.js --extensions=ts,tsx,js,jsx src/

v0.11 Query、Queryable 与 useQuery

· 阅读需 12 分钟
Nathaniel Tucker
Creator of Reactive Data Client

除了规范化状态带来的性能和数据完整性收益之外,我们还获得了一项额外的好处:能够安全地直接访问并查询 store。

在本次发布中,我们围绕 Queryable Schema 的概念统一并简化了这一功能。它们包括 Entity、All、Collection、Query 和 Union

这一变化最大的影响是引入了新的 hook useQuery(),它允许使用 Queryable Schema 直接查找 store。

class User extends Entity {
username = '';
id = '';
groupId = '';
pk() {
return this.id;
}
static index = ['username' as const];
}

const bob = useQuery(User, { username: 'bob' });
const bob = useQuery(User, { id: '5' });

同样地,我们可以通过 controller.get 在 controller 和 snapshot 中查找 Queryable

const bob = snapshot.get(User, { username: 'bob' });

此外,我们还投入了更多的性能改进,使大多数操作的性能提升了约 2 倍,而 Query 则快了 16 倍。

迁移指南

破坏性变更:

其他亮点:

  • useCache() 接受带有 sideEffects 的 Endpoint(例如 Resource.update)
  • 允许 Entity.pk() 返回数字。
  • 2 到 16 倍的性能改进

v0.10:一致的 null 处理、URL 工具函数

· 阅读需 4 分钟
Nathaniel Tucker
Creator of Reactive Data Client

本次发布聚焦于一致性和可扩展性。null 处理现在在所有 schema 类型中保持统一,使数据转换更可预测。新的 URL 工具函数支持自定义 URL 构造和搜索参数编码。

class MyEndpoint<O extends RestGenerics = any> extends RestEndpoint<O> {
searchToString(searchParams) {
// Use qs library for complex nested object encoding
return qs.stringify(searchParams);
}
}
迁移指南

破坏性变更:

其他改进: