跳到主要内容

Entity 与数据规范化

Entity 拥有主键,因此可以通过查找表轻松访问。这样一来,无论同一份数据出现在哪个 endpoint 中,你都能方便地查找、更新、创建或删除它。

Entity 缓存

从响应中提取 entity 的过程称为 normalization(规范化)。访问响应时则通过 denormalization(反规范化)逆转这一过程。

全局引用相等

使用 entity 可以将 Reactive Data Client 的全局引用相等保证扩展到比整个 endpoint 响应更细的粒度。

变更与动态数据​

当 endpoint 修改数据时,这称为副作用。用 sideEffect: true 标记 endpoint 会告诉 Reactive Data Client 该 endpoint 不是幂等的,因此不应在可能任意多次调用该 endpoint 的 hook 中使用它,例如 useSuspense() 或 useFetch()

只要在 endpoint 的响应中包含被修改的数据,并指定 schema,Reactive Data Client 就能更新它从中提取的所有 entity。

import { RestEndpoint, schema } from '@data-client/rest';

const todoCreate = new RestEndpoint({
urlPrefix: 'https://jsonplaceholder.typicode.com',
path: '/todos',
method: 'POST',
schema: new Collection([Todo]).push,
});
使用示例
import { useController } from '@data-client/react';
import { todoCreate } from './api/Todo';
import Form from './Form';
import FormField from './FormField';

export default function NewTodoForm() {
const ctrl = useController();
return (
<Form
onSubmit={e => ctrl.fetch(todoCreate, new FormData(e.target))}
>
<FormField name="title" />
</Form>
);
}
信息

变更会自动更新规范化缓存,从而保证数据一致且新鲜。

Schema​

schema 以声明式的方式定义如何处理响应

import { RestEndpoint, Collection } from '@data-client/rest';

const getTodoList = new RestEndpoint({
urlPrefix: 'https://jsonplaceholder.typicode.com',
path: '/todos',
schema: new Collection([Todo]),
});

将 Entity Todo 放入数组 Collection 中,就可以轻松地向其中 push 或 unshift 新的 Todos。

除了数组之外,还提供了另外几种适用于不同模式的 schema。前两种(Object 和 Array)可以简写为对象字面量和数组字面量。

数据类型可变Schema描述可查询
对象✅Entity单个唯一对象✅
✅Union(Entity)多态对象(A | B)✅
🛑Object静态已知的键🛑
Invalidate(Entity)删除 Entity🛑
列表✅Collection(Array)可增长的列表✅
🛑Array不可变列表🛑
All某一类型的全部 Entity 列表✅
映射✅Collection(Values)可增长的映射✅
🛑Values不可变映射🛑
Scalar✅Scalar依赖视角(lens)的 Entity 字段✅
任意Query(Queryable)记忆化的自定义转换✅
Lazy(Schema)延迟反规范化✅

了解更多

嵌套​

此外,Entity 本身也可以通过声明 static schema 成员来指定嵌套 schema。

import { Entity } from '@data-client/endpoint';

class Todo extends Entity {
id = 0;
user = User.fromJS();
title = '';
completed = false;

static key = 'Todo';

static schema = {
user: User,
};
}

class User extends Entity {
id = 0;
username = '';

static key = 'User';
}

了解更多

数据表示​

此外,函数也可以用作 schema,它会在反规范化期间被调用。这对于 bignumber 或 temporal instant 这类表示形式可能很有用

import { Entity } from '@data-client/endpoint';

class Todo extends Entity {
id = 0;
user = User.fromJS();
title = '';
completed = false;
dueDate = Temporal.Instant.fromEpochMilliseconds(0);

static key = 'Todo';

static schema = {
user: User,
dueDate: Temporal.Instant.from,
};
}
信息

得益于全局引用相等保证,每次更新时成员只会构造一次。

检查 store(调试)​

可以安装 DevTools 浏览器扩展来检查和调试 store。

browser-devtools

Data Client 调试指南 »

基准测试​

与非规范化方案相比,entity 级别的记忆化可带来高达 20 倍的反规范化性能提升,以及快 90 倍的变更传播速度。完整的规范化基准测试结果以及完整的 React 集成基准测试,请参阅性能页面。