ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

RedwoodJS 分页实战:从 GraphQL 服务端到前端 Pagination 组件的完整实现指南

RedwoodJS 分页实战:从 GraphQL 服务端到前端 Pagination 组件的完整实现指南 后端前端Web框架开发工具【免费下载链接】redwoodRedwoodGraphQL项目地址https://gitcode.com/gh_mirrors/re/redwood点击查看免费下载本指南以 RedwoodJS 官方教程博客项目为基础讲解如何在 RedwoodJS 全栈应用中为博客文章列表实现基于「页码page 偏移量offset」的经典分页方案从 API 侧的 SDL 与 Service 编写、GraphQL 查询改造到 Web 侧 Cell 的beforeQuery数据预处理与路由 query 参数透传最终生成可点击跳转的Pagination组件。读完本文你将掌握一套可直接复制的 RedwoodJS 分页实现路径并能理解其底层机制服务层 Prisma 的take/skip、路由层的 query 解析与 Cell 生命周期钩子。分页方案概览在 RedwoodJS 中如何组织一次「分页查询」RedwoodJS 的典型数据流是Web 侧的 Cell 发起 GraphQL 查询 → API 侧的 Service 解析器调用 Prisma Client 访问数据库 → 返回结果渲染到页面。分页场景下我们需要在链路的两个环节分别扩展能力API 侧新增一个返回「当前页文章列表 文章总数」的 GraphQL 查询如postPage并在 Service 中利用 Prisma 的take每页条数与skip跳过的条数实现偏移分页同时用count()返回总数供前端计算总页数Web 侧修改 Cell 的QUERY与Success组件并通过 RedwoodJS 路由的 query 字符串机制?page2把页码从 URL 传递到页面组件再透传给 Cell最终在列表尾部渲染分页导航。本指南将沿用官方教程见 教程总览创建的博客项目。假设你已有一个可运行的教程代码目录并且数据库中至少有六篇帖子本文按每页五条演示六条即可产生两页。第一步扩展 SDL定义分页查询的类型结构打开 API 侧的 SDL 文件api/src/graphql/posts.sdl.js或 TypeScript 项目中的api/src/graphql/posts.sdl.ts在现有Query类型中新增一个postPage查询。为了让前端能够渲染分页导航查询结果不能只返回帖子数组还要同时返回帖子总数因此我们引入一个PostPage包装类型export const schema gql # ... type PostPage { posts: [Post!]! count: Int! } type Query { postPage(page: Int): PostPage posts: [Post!]! post(id: Int!): Post! } # ... 两个细节值得注意PostPage.posts使用[Post!]!非空数组保证客户端一定能拿到数组可能为空数组而非nullpostPage(page: Int)的page参数被设计为可选。这样客户端不传page时服务端可以默认返回第一页URL 无查询参数时页面依然可用。第二步在 Service 中实现分页解析器Prisma take / skip / count打开api/src/services/posts/posts.js或.ts为postPage查询新增解析器。这是整个分页逻辑的核心用take控制每页条数用skip计算偏移量并用count获取总数const POSTS_PER_PAGE 5 export const postPage ({ page 1 }) { const offset (page - 1) * POSTS_PER_PAGE return { posts: db.post.findMany({ take: POSTS_PER_PAGE, skip: offset, orderBy: { createdAt: desc }, }), count: db.post.count(), } }实现要点默认参数page 1与 SDL 中参数可选的设计相呼应未传page时回落到第一页偏移量公式offset (page - 1) * POSTS_PER_PAGE第一页跳过 0 条第二页跳过 5 条以此类推take与skip这是 Prisma Client 的分页基础能力对应 SQL 层面的LIMIT/OFFSET配合orderBy: { createdAt: desc }保证分页结果顺序稳定避免翻页时出现重复或遗漏count并行获取总数返回的count供前端计算总页数Math.ceil(count / POSTS_PER_PAGE)。完成这一步后API 侧已经可以响应形如「第 2 页的 5 条帖子 总数」的 GraphQL 请求——Apollo 客户端发起查询解析器经由 Prisma 从数据库取数这正是 RedwoodJS 中「GraphQL 层 → Service 层 → 数据库层」的标准调用链。第三步改造 Cell 的 QUERY 与 Success 组件Web 侧负责展示帖子列表的是BlogPostsCell对应博客首页 HomePage。首先把它的QUERY从原来的posts查询改为postPage查询并把page作为查询变量传入export const QUERY gql query BlogPostsQuery($page: Int) { postPage(page: $page) { posts { id title body createdAt } count } } 如果是 TypeScript 项目可以借助 RedwoodJS 自动生成的类型与TypedDocumentNode获得端到端类型安全import type { BlogPostsQuery, BlogPostsQueryVariables } from types/graphql import type { TypedDocumentNode } from redwoodjs/web export const QUERY: TypedDocumentNodeBlogPostsQuery, BlogPostsQueryVariables gql query BlogPostsQuery($page: Int) { postPage(page: $page) { posts { id title body createdAt } count } } 查询结果结构从「帖子数组」变成了「包含posts和count的对象」所以同文件中的Success组件也要同步调整解构方式export const Success ({ postPage }) { return postPage.posts.map((post) BlogPost key{post.id} post{post} /) }export const Success ({ postPage, }: CellSuccessPropsBlogPostsQuery, BlogPostsQueryVariables) { return postPage.posts.map((post) BlogPost key{post.id} post{post} /) }第四步利用路由 query 参数把 page 传给页面组件现在的问题是如何把「当前页」这个值喂给查询。RedwoodJS 提供了一条捷径URL 查询字符串中的参数会自动作为 props 传递给对应的页面组件。回忆教程中的路由写法Route path/blog-post/{id:Int} page{BlogPostPage} nameblogPost /——路径参数id会被作为 prop 传给BlogPostPage。查询字符串走的是同一套机制但不需要修改任何路由定义?page2会被路由层解析后以pageprop 的形式注入页面组件。这一点有源码可证在 路由实现 中路由层通过parseSearch(location.search)把location.search解析为对象再与路径参数合并成allParams传给页面而 parseSearch 实现 会遍历URLSearchParams的每个 key产出{ key1: val1, key2: val2 }形式的普通对象。因此HomePage只需声明pageprop 并向下传递const HomePage ({ page 1 }) { return ( BlogLayout BlogPostsCell page{page} / /BlogLayout ) }const HomePage ({ page 1 }) { return ( BlogLayout BlogPostsCell page{page} / /BlogLayout ) }行为说明访问https://awesomeredwoodjsblog.com?page2时HomePage的pageprop 会被设为字符串2再原样透传给BlogPostsCell没有?page时page默认值为1由组件默认参数兜底。第五步用 beforeQuery 把字符串页码解析为数字路由传来的page是字符串例如2而 GraphQL 变量需要数字。Cell 提供beforeQuery生命周期钩子允许我们在查询执行前改写 props / 组装查询变量。在BlogPostsCell中补充export const beforeQuery ({ page }) { page page ? parseInt(page, 10) : 1 return { variables: { page } } }export const beforeQuery ({ page, }: FindBlogPostQueryVariables): GraphQLQueryHookOptions FindBlogPostQuery, FindBlogPostQueryVariables { page page ? parseInt(page, 10) : 1 return { variables: { page } } }从实现层面看beforeQuery是 RedwoodJS Cell 工厂的内置扩展点在 createCell 的实现 中Cell 默认把「传入的 props 直接当作 GraphQL 变量」构造查询选项当自定义beforeQuery存在时则以你的返回值即{ variables }替代默认行为再交由useQuery执行。因此这里把字符串page用parseInt(page, 10)转成数字后放入variables.page查询就会以数字参数请求postPage。第六步本地验证分页效果执行yarn rw dev启动开发服务器默认端口 8910访问 http://localhost:8910应只看到前五条帖子第一页把 URL 改为 http://localhost:8910?page2应看到后续五条帖子若总共只有六条则此处只显示一条。这验证了「URL query → 页面 prop → Cell 查询变量 → GraphQL 请求 → 数据库偏移查询」整条链路已打通。第七步生成并实现 Pagination 分页组件最后添加一个可点击跳转的页码选择器。用 RedwoodJS CLI 生成组件骨架yarn rw g component Pagination然后实现如下逻辑核心是count除以每页条数向上取整得到总页数逐页生成Linkimport { Link, routes } from redwoodjs/router const POSTS_PER_PAGE 5 const Pagination ({ count }) { const items [] for (let i 0; i Math.ceil(count / POSTS_PER_PAGE); i) { items.push( li key{i} Link to{routes.home({ page: i 1 })} {i 1} /Link /li ) } return ( h2Pagination/h2 ul{items}/ul / ) } export default Paginationimport { Link, routes } from redwoodjs/router const POSTS_PER_PAGE 5 const Pagination ({ count }: { count: number }) { const items [] for (let i 0; i Math.ceil(count / POSTS_PER_PAGE); i) { items.push( li key{i} Link to{routes.home({ page: i 1 })}{i 1}/Link /li ) } return ( h2Pagination/h2 ul{items}/ul / ) } export default Pagination关键点routes.home({ page: i 1 })RedwoodJS 的命名路由函数接受一个参数对象其中的键会被序列化为 URL 查询字符串生成/ ?page2这样的地址。点击某个页码链接后浏览器地址变为?pageN路由层解析 query → 页面 prop → Cell 变量完成翻页Math.ceil(count / POSTS_PER_PAGE)用总数除以每页条数向上取整得到总页数并据此渲染对应数量的页码项组件保持无样式符合官方教程「不加 CSS」的风格若想美化只需去掉列表项前的圆点并改为水平排列即可。第八步把 Pagination 接入 BlogPostsCell最后在BlogPostsCell的Success组件末尾渲染Pagination并记得在文件顶部导入它import Pagination from src/components/Pagination // ... export const Success ({ postPage }) { return ( {postPage.posts.map((post) BlogPost key{post.id} post{post} /)} Pagination count{postPage.count} / / ) }import Pagination from src/components/Pagination // ... export const Success ({ postPage, }: CellSuccessPropsBlogPostsQuery, BlogPostsQueryVariables) { return ( {postPage.posts.map((post) ( BlogPost key{post.id} post{post} / ))} Pagination count{postPage.count} / / ) }至此博客首页在帖子列表尾部就会出现「Pagination」标题与一组数字页码点击即可跳转对应页面。已知局限与改进方向当前实现存在一个明显的技术局限它对「页数很多」的场景处理得不够优雅。假设共有 100 页列表会一次性渲染 100 个页码链接。官方文档把它留作读者练习——一个更完善的分页组件通常需要加入「上一页 / 下一页」按钮并处理第一页 / 最后一页的禁用态显示窗口化的页码如1 … 4 5 6 … 20避免页码爆炸用useMatch或当前pageprop 高亮当前页可选地借助 Prisma 的游标分页cursor-based pagination替代偏移分页应对深分页性能问题。相关资源与进一步阅读本教程构建于官方教程之上前置基础见 教程总览完整的本篇内容含 JS/TS 双版本代码见 官方 Pagination 指南最新版位于 docs/docs/how-to/pagination.md想深入理解 Cell 机制beforeQuery、afterQuery、Loading/Failure/Empty/Success生命周期可阅读 createCell 源码路由如何解析查询字符串并注入页面 props见 router.tsx 与 parseSearch 实现分页相关的数据库与客户端原理可参考 Prisma 的分页文档与 Apollo 的分页文档链接见原文档末尾。赞分享后端前端Web框架开发工具【免费下载链接】redwoodRedwoodGraphQL项目地址https://gitcode.com/gh_mirrors/re/redwood点击查看免费下载相关推荐终极解决TranslucentTB启动失败VCLibs依赖修复完全指南终极解决TranslucentTB启动失败VCLibs依赖修复完全指南 TranslucentTB作为Windows任务栏透明化工具因其轻量级和美观效果备受后端前端Web框架开发工具RedwoodJS 分页实战从 GraphQL 查询到前端分页组件的完整实现RedwoodJS 分页实战从 GraphQL 查询到前端分页组件的完整实现 本篇技术指南基于 RedwoodJS 官方文档 docs/docs/how to后端前端Web框架开发工具RedwoodJS 分页实战从 GraphQL SDL 到 Prisma 查询的完整实现指南RedwoodJS 分页实战从 GraphQL SDL 到 Prisma 查询的完整实现指南 导读 本文基于 RedwoodJS 官方教程 tutorial后端前端Web框架开发工具创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表