让我们看看我们为什么要规范化缓存,以及我们必须做什么样的工作才能获得规范化的缓存。
对于主页,我们获取一个 TODO 列表和一个高优先级 TODOS 列表。我们的两个端点返回以下数据:
{
all: [{ id: 1, title: "TODO 1" }, { id: 2, title: "TODO 2" }, { id: 2, title: "TODO 2"}],
highPrio: [{ id: 1, title: "TODO 1" }]
}
如果我们将这样的数据存储到我们的缓存中,我们将很难更新单个待办事项,因为我们必须更新我们存储中的每个数组中的待办事项,或者将来可能存在于我们的存储中。
我们可以规范化数据并且只在数组中存储引用。这样我们就可以轻松地在一个地方更新单个待办事项:
{
queries: {
all: [{ ref: "Todo:1" }, { ref: "Todo:2" }, { ref: "Todo:2" }],
highPrio: [{ ref: "Todo:1" }}]
},
refs: {
"Todo:1": { id: 1, title: "TODO 1" },
"Todo:2": { id: 2, title: "TODO 2" },
"Todo:3": { id: 3, title: "TODO 3" }
}
}
不利的一面是,这种形状的数据现在更难在我们的列表组件中使用。我们将不得不对缓存进行大量转换,大致如下:
function denormalise(cache) {
return {
all: cache.queries.all.map(({ ref }) => cache.ref[ref]),
highPrio: cache.queries.highPrio.map(({ ref }) => cache.ref[ref]),
};
}
请注意Todo:1,如果我们在 React 组件内部运行此函数(这通常在 Redux 中称为选择器),那么现在在缓存内部更新将如何自动更新所有引用 todo 的查询。
GraphQL 的神奇之处在于它是一个带有类型系统的严格规范。这允许像 Apollo 这样的 GraphQL 客户端全局识别对象并规范化该缓存。同时它还可以自动为你去规范化缓存,并在突变后自动更新缓存中的对象。这意味着大多数时候您根本不需要编写缓存逻辑。这应该可以解释为什么它如此受欢迎:最好的代码就是没有代码!
const { data, loading, error } = useQuery(gql`
{ all { id title } highPrio { id title }
`);
此代码在加载时自动获取查询,规范化响应并将其写入缓存。然后使用缓存数据将缓存非规范化回查询的形状。对缓存中元素的更新会自动更新所有订阅的组件。