从10月8日至10日加入我们,在纽约市了解关于 GraphQL Federation 和 API 平台工程的最新技巧、趋势和新闻。加入我们参加2024年纽约市 GraphQL 峰会
文档
免费开始

基于偏移量的分页


我们建议阅读核心分页 API,然后再学习基于偏移量分页的特定考虑因素。

使用基于偏移量的分页,一个列表字段接受一个 ,它表示服务器在返回特定查询的项目时应从列表中的哪里开始。该字段通常也接受一个 ,表示要返回的最大项目数:offset 表示服务器返回项目时应从列表中的哪个位置开始。该字段通常也接受一个 limit 参数,表示要返回的最大项目数:

type Query {
feed(offset: Int, limit: Int): [FeedItem!]
}
type FeedItem {
id: ID!
message: String!
}

这种分页策略适用于不可变列表,或者列表中每个项目的索引永远不会改变的情况。在其他情况下,应避免使用它,转而选择 基于游标的分页,因为移动或删除项目会改变偏移量。如果分页查询之间发生变化,这将导致跳过或重复项目。

尽管它有限制,但基于偏移量的分页在很多应用中是一个常见的模式,部分原因是因为它的实现相对简单。

offsetLimitPagination 辅助函数

提供了一个 offsetLimitPagination 辅助函数,您可以用来为每个相关的列表 字段 生成 字段策略

此示例使用 offsetLimitPaginationQuery.feed 生成字段策略:

index.js
import { InMemoryCache } from "@apollo/client";
import { offsetLimitPagination } from "@apollo/client/utilities";
const cache = new InMemoryCache({
typePolicies: {
Query: {
fields: {
feed: offsetLimitPagination()
},
},
},
});

这定义了一个 merge 函数,用于处理缓存中分页结果的合并(查看源代码)。

fetchMore 结合使用

如果您使用 offsetLimitPagination 如上设置您的源策略,那么您可以像这样使用 fetchMoreuseQuery

FeedData.jsx
const FeedData() {
const { loading, data, fetchMore } = useQuery(FEED_QUERY, {
variables: {
offset: 0,
limit: 10
},
});
// If you want your component to rerender with loading:true whenever
// fetchMore is called, add notifyOnNetworkStatusChange:true to the
// options you pass to useQuery above.
if (loading) return <Loading/>;
return (
<Feed
entries={data.feed || []}
onLoadMore={() => fetchMore({
variables: {
offset: data.feed.length
},
})}
/>
);
}

默认情况下,fetchMore 使用原始 查询变量,所以我们只需要传递变化的 offset。当从服务器返回新数据时,它会自动与缓存中任何现有的 Query.feed 数据合并。这导致 useQuery 使用扩展的数据列表重新渲染。

在本例中,Feed 组件每次渲染时都会接收到整个缓存的列表(data.feed),这其中包括迄今为止接收到的所有页面的数据。这是一个 非分页的 read 函数

使用分页 read 函数

在上述 示例中 返回单独页面的结果,但是每个 query 会返回迄今为止接收到的所有缓存结果。为了将每个 query 的结果限制为您请求的项目,您可以在您的 field 策略中包含一个 分页的 read 函数

由于 offsetLimitPagination 助手当前定义您的 field 策略,您需要将您的 read 函数与助手的结果结合,如下所示:

index.js
import { InMemoryCache } from "@apollo/client";
import { offsetLimitPagination } from "@apollo/client/utilities";
const cache = new InMemoryCache({
typePolicies: {
Query: {
fields: {
feed: {
...offsetLimitPagination(),
read(existing, { args }) {
// Implement here
}
}
},
},
},
});

有关示例实现,请参阅 分页 read 函数

如果您使用分页的 read 函数,您可能需要在调用 fetchMore 后,根据您用例的需求更新您的 offsetlimit 变量。否则,您将继续只渲染第一页的结果。

例如,要显示迄今为止接收到的所有数据,您可以对之前的示例进行如下修改

const FeedData = () => {
const [limit, setLimit] = useState(10);
const { loading, data, fetchMore } = useQuery(FEED_QUERY, {
variables: {
offset: 0,
limit,
},
});
if (loading) return <Loading/>;
return (
<Feed
entries={data.feed || []}
onLoadMore={() => {
const currentLength = data.feed.length;
fetchMore({
variables: {
offset: currentLength,
limit: 10,
},
}).then(fetchMoreResult => {
// Update variables.limit for the original query to include
// the newly added feed items.
setLimit(currentLength + fetchMoreResult.data.feed.length);
});
}}
/>
);
}

此代码使用 React useState Hook 存储当前的 limit 值,并通过在 fetchMore 返回的 Promise 的回调中调用 setLimit 来更新它。

如果需要改变 offset,您也可以用 React useState Hook 存储它。这些 变量何时以及如何改变完全取决于您的组件,并且可能不总是调用 fetchMore 的结果,所以使用 React 组件状态来存储这些变量值是有意义的。

如果您不使用 React 和 useQuery,则由 client.watchQuery 返回的 ObservableQuery 对象有一个名为 setVariables 的方法,可以调用它来更新原始 变量

因为 fetchMore 函数需要做一些额外工作来更新原始 变量(如果你使用的是对那些 read 函数敏感的 read 函数),所以说 fetchMore 鼓励使用第一种 read 函数,这个函数简单地返回所有可用数据。

然而,现在你了解了你的选项,将读取时的分页逻辑从你的应用代码移动到你的 field read 函数中没有什么错误。两种 read 函数都有其用途,并且都可以与 fetchMore 一起工作。

使用 offsetLimitPagination 设置 keyArgs

如果分页 field 接受除 offsetlimit 之外的其他参数,你可能需要 指定 key arguments 来表示两个结果集是否属于同一列表或不同的列表。

要为 offsetLimitPagination 生成的 field 策略设置 keyArgs,将参数名数组提供给函数作为参数:

fields {
// Results belong to the same list only if both the type
// and userId arguments match exactly
feed: offsetLimitPagination(["type", "userId"])
}

默认情况下, offsetLimitPagination 使用 keyArgs: false(没有 key 参数)。

上一页
核心 API
下一页
基于游标的
评分文章评分在GitHub上编辑编辑论坛Discord

©2024Apollo Graph Inc.(商业名称:Apollo GraphQL)。

隐私政策

公司