后端前端Web框架开发工具【免费下载链接】redwoodRedwoodGraphQL项目地址https://gitcode.com/gh_mirrors/re/redwood点击查看免费下载本篇技术指南以 RedwoodJS 官方教程Tutorial的博客项目为背景完整演示如何为博客文章列表实现经典的分页功能先在后端通过 SDL 定义postPage查询、用 Prisma 的take/skip做 offset 分页再在前端借助 Redwood 路由的查询参数魔法把page传入 Cell最后用redwoodjs/router的Link与routes生成可点击的翻页组件。读完本文你将掌握 RedwoodJS 中SDL → Service → Cell → 路由参数 → 分页组件这条完整链路并理解其背后的源码级工作原理。一、整体思路RedwoodJS 中的 offset 分页链路RedwoodJS 是前后端一体的全栈框架API 侧由 GraphQLSDL Service承担数据访问Web 侧由 Cell 承担查询 生命周期渲染。分页要打通这条链路通常分四步SDL 层在Query类型上新增一个分页查询postPage(page: Int): PostPage返回结构同时包含本页文章列表和文章总数count总数用于计算总页数。Service 层实现解析器用 Prisma 的take取多少条和skip跳过多少条做 offset 分页并返回count。Web 层修改发起查询的 Cell让page通过 URL 查询字符串?page2传入页面组件再经 Cell 的beforeQuery转成查询变量。组件层新增Pagination组件根据count渲染页码链接。其中URL 查询字符串自动变成页面组件 prop是 Redwood 路由提供的能力下文会结合源码说明其实现让你明白为什么不需要改路由定义就能工作。二、前置准备基于官方教程的博客项目本文建立在 RedwoodJS 官方教程foreword 章节产出的博客项目之上。你应该有一个包含Post模型、BlogPostsCell、HomePage、BlogPost组件的可运行项目并已通过yarn rw dev在默认端口 8910 启动过。为了让分页值得做先在数据库中创建足够多的文章。本教程约定每页显示 5 篇文章POSTS_PER_PAGE 5因此至少准备 6 篇文章才能看到两页的效果。说明本教程遵循官方教程的惯例不引入任何 CSS页面样式如去掉列表圆点、改为横向排列留给你自行美化。三、第一步在 SDL 中定义分页查询打开api/src/graphql/posts.sdl.js在gql模板字符串中新增一个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!]!是本页文章数组count: Int!是全部文章数量。后续分页组件需要靠count算出总页数所以必须随查询一起返回。page参数是可选的page: Int而非page: Int!这样当客户端不传页码时Service 可以默认返回第一页URL 不带?page也能正常访问首页。四、第二步在 Service 中实现解析器Prisma take/skip 分页在api/src/services/posts/posts.js中新增解析器。Redwood 的 Service 文件是 GraphQL 解析器的天然载体导出与 SDL 中查询同名的函数即可被自动关联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 计算offset (page - 1) * POSTS_PER_PAGE即第一页跳过 0 条、第二页跳过 5 条以此类推。Prisma 分页原语take限定返回条数5 条skip跳过前 N 条这正是 Prisma 中最常用的 offset 分页写法orderBy: { createdAt: desc }保证按创建时间倒序稳定分页如果排序不稳定翻页时可能出现重复或遗漏。计数查询db.post.count()返回总数用于前端计算总页数。TypeScript 项目中该文件为api/src/services/posts/posts.ts逻辑完全一致参数与返回值可依据生成的类型标注。五、第三步更新 Cell 的 QUERY 与 Success 组件Web 侧博客列表由web/src/components/BlogPostsCell/BlogPostsCell.jsx这个 Cell 负责Cell 用导出的QUERY发起 GraphQL 查询并根据查询状态渲染Loading/Failure/Empty/Success。我们需要把查询从取全部文章改为取某一页。5.1 更新 QUERYexport const QUERY gql query BlogPostsQuery($page: Int) { postPage(page: $page) { posts { id title body createdAt } count } } 查询结构必须与 SDL 中的postPage返回类型对应外层取postPage内部取posts数组和count。在 TypeScript 项目中Redwood 会为 Cell 生成类型QUERY通常这样声明来自当前仓库 Cell 类型体系 支持的方式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 } } 5.2 更新 Success 组件Success收到的 props 结构由查询结果决定查询返回的是postPage对象因此要从postPage.posts中取文章列表export const Success ({ postPage }) { return postPage.posts.map((post) BlogPost key{post.id} post{post} /) }TypeScript 版本中Success应使用 Cell 生成的 props 类型export const Success ({ postPage, }: CellSuccessPropsBlogPostsQuery, BlogPostsQueryVariables) { return postPage.posts.map((post) BlogPost key{post.id} post{post} /) }此时列表能渲染但page还是写死的——接下来解决如何把页码传进来。六、第四步让 URL 查询参数自动变成页面 prop回忆官方教程中的路由用法Route path/blog-post/{id:Int} page{BlogPostPage} nameblogPost /中URL 路径里的id会被 Redwood 解析并作为 prop 传给BlogPostPage。Redwood 路由对 URL 查询字符串query string也有同样的魔法?page2中的page会作为 prop 自动注入到匹配到的页面组件而且无需修改任何路由定义。这并非黑魔法其实现位于仓库的 router.tsx路由匹配时会把location.search解析成键值对再与路径参数合并为allParamsconst searchParams parseSearch(location.search) const allParams { ...searchParams, ...pathParams }parseSearch的具体实现在 util.ts内部基于URLSearchParams将?key1val1key2val2解析为{ key1: val1, key2: val2 }。注意两点一是路径参数的优先级高于同名查询参数{ ...searchParams, ...pathParams }中后者覆盖前者二是它只取每个 key 的第一个值如?foo1foo2只保留1。这正好解释了本文场景URL 是/?page2页面组件收到的 prop 是字符串2。于是我们更新HomePage接收并转发这个 propconst HomePage ({ page 1 }) { return ( BlogLayout BlogPostsCell page{page} / /BlogLayout ) }行为验证用户访问https://awesomeredwoodjsblog.com?page2时HomePage的pageprop 是字符串2它被传给BlogPostsCell若 URL 没有?page参数page默认取1。七、第五步用 beforeQuery 把字符串转成数字URL 查询参数天然是字符串而 GraphQL 变量$page: Int需要数字。Cell 提供的生命周期钩子beforeQuery正是为此设计的它在发起查询之前执行接收 Cell 的 props返回 ApollouseQuery的 options其中variables会被注入查询。在BlogPostsCell.jsx中补充export const beforeQuery ({ page }) { page page ? parseInt(page, 10) : 1 return { variables: { page } } }这里把字符串2用parseInt(page, 10)转成数字2为空则兜底为1。TypeScript 版本可标注更严格的类型如使用FindBlogPostQueryVariables与GraphQLQueryHookOptions组合逻辑相同。关于beforeQuery的底层行为可以参考 createCell.tsx 中 Cell 工厂的默认实现Cell 把收到的 props 交给beforeQuery用其返回的 options 调用useQuery同时 Cell 还会注入fetchPolicy: cache-and-network与notifyOnNetworkStatusChange: true等默认配置。也就是说beforeQuery返回的variables就是真正传给 GraphQL 查询的变量这也是本教程在 Cell 内做参数清洗之所以成立的原因。八、第六步生成并接入 Pagination 翻页组件8.1 生成组件骨架使用 Redwood CLI 生成组件对应 component 生成器yarn rw g component Pagination8.2 编写翻页组件编辑web/src/components/Pagination/Pagination.jsximport { 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 Pagination核心逻辑解析总页数Math.ceil(count / POSTS_PER_PAGE)文章总数除以每页条数向上取整6 篇 → 2 页5 篇 → 1 页。生成页码链接routes.home({ page: i 1 })是 Redwood 路由命名路由named route的标准用法——把参数对象传给路由函数它会自动拼出带查询字符串的 URL。这一点在路由测试中有明确验证见 analyzeRoutes.test.tsxnamedRoutesMap.hello({ queryGuy: 1 })的期望结果就是/hello?queryGuy1。因此routes.home({ page: 2 })会生成/?page2与第六步中HomePage读取的查询参数完全对得上形成闭环。8.3 把 Pagination 接入 BlogPostsCell最后把Pagination渲染到文章列表末尾别忘了在文件顶部importimport Pagination from src/components/Pagination // ... export const Success ({ postPage }) { return ( {postPage.posts.map((post) ( BlogPost key{post.id} post{post} / ))} Pagination count{postPage.count} / / ) }现在Success拿到查询返回的postPage对象上部渲染本页文章下部用postPage.count渲染页码列表。九、运行验证执行yarn rw dev默认端口 8910然后访问http://localhost:8910只能看到最新的 5 篇文章第一页。访问http://localhost:8910?page2看到接下来的 5 篇若你总共只有 6 篇文章这里应只显示 1 篇。点击列表底部的页码链接可在各页之间跳转跳转本质是改变 URL 的?page查询参数Redwood 路由会重新解析并触发 Cell 重新查询。整个数据流为Pagination 的 Link → URL ?pageN → HomePage 的 page prop → BlogPostsCell 的 page prop → beforeQuery 转数字 → postPage(page) 查询 → Service 的 take/skip → 新一页数据 count → Success 重新渲染。十、已知局限与进阶方向教程实现足够跑通分页但需要明确其技术边界页数很多时不优雅Pagination组件会为每一页渲染一个li若博客有 100 页就会出现 100 个页码链接。官方文档明确指出这是一个技术局限并把构建功能更完整的分页组件留给读者作为练习例如引入上一页/下一页、省略号、当前页高亮、窗口化页码等。一次性返回 count 的成本每次翻页都会执行一次db.post.count()在超大表上可以结合缓存或近似计数优化。偏移分页的固有特性take/skip属于 offset 分页文章频繁新增/删除时翻页过程中可能偶发重复或遗漏对实时性要求高的场景可考虑基于游标cursor的分页。参数健壮性beforeQuery中可用Math.max(1, page)等方式防止page0或负数parseSearch只保留重复 key 的第一个值多值参数需自行处理。十一、总结本文基于 RedwoodJS 官方教程博客走通了从 GraphQL 到 UI 的完整分页实现SDL 定义postPage查询与PostPage返回类型Service 用 Prismatake/skip实现 offset 分页并返回countCell 更新QUERY、Success与beforeQuery页面组件利用 Redwood 路由查询参数自动注入 prop的能力接收pagePagination组件通过命名路由routes.home({ page })生成页码链接。同时通过阅读仓库源码router.tsx 的查询参数合并、util.ts 的parseSearch实现、createCell.tsx 的 Cell 生命周期、analyzeRoutes.test.tsx 的命名路由测试印证了每个魔法背后的真实机制。掌握了这条链路你可以在 RedwoodJS 项目中轻松扩展出任何带分页的数据列表。赞分享后端前端Web框架开发工具【免费下载链接】redwoodRedwoodGraphQL项目地址https://gitcode.com/gh_mirrors/re/redwood点击查看免费下载相关推荐RedwoodJS 博客分页实战从 GraphQL SDL 到 Prisma 偏移分页与路由参数传递RedwoodJS 博客分页实战从 GraphQL SDL 到 Prisma 偏移分页与路由参数传递 本篇技术指南围绕 RedwoodJS 官方教程vers后端前端Web框架开发工具RedwoodJS 分页实战从 GraphQL SDL 到 Prisma 查询的完整实现指南RedwoodJS 分页实战从 GraphQL SDL 到 Prisma 查询的完整实现指南 导读 本文基于 RedwoodJS 官方教程 tutorial后端前端Web框架开发工具RedwoodJS 实战在博客应用中从零实现 GraphQL 分页SDL / Service / Cell / 路由 Query String 全链路RedwoodJS 实战在博客应用中从零实现 GraphQL 分页SDL / Service / Cell / 路由 Query String 全链路 这后端前端Web框架开发工具上一篇从零到一cim分布式即时通讯系统的完整项目管理指南下一篇cann/asc-devkit稀疏矩阵乘法示例创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
企业数字化 ERP 产品动态
相关推荐
飞蛋H743飞控从零配置全攻略:固件烧写、传感器校准与避坑指南 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/24 14:46:14
ParlAI BlenderBot 3x 对话数据集数据卡深度解读:从有机交互采集到可训练语料的完整技术图谱 ParlAI BlenderBot 3x 对话数据集数据卡深度解读:从有机交互采集到可训练语料的完整技术图谱 【免费下载链接】ParlAI A framework for training and evaluating AI models on a variety of openly available dialogue datasets. 项目地址: https://gitcode.com/g… · 2026/9/24 14:46:08
2026年9月矿山浮选药剂效果差怎么解决选型思路 九月下旬,北方多数洗煤厂和金属选矿厂进入四季度备料阶段,浮选车间里精煤灰分波动、药剂消耗上升的情况并不少见。矿山浮选药剂效果差,很多时候不是药剂本身失效,而是选型思路没有跟上煤质和矿浆性质的变化。一、浮选效果变差的典… · 2026/9/24 15:15:30
交通运输物流管理小程序开发案例 根据某公司的交通运输仓储物流管理需求,海拔网络完成了一套物流运输管理小程序的定制开发。系统按照老板、派单人员和司机三种使用角色划分功能,将送货任务录入、车辆选择、司机数据填报、任务状态确认及经营数据查看集中到同一套系统中。该项目不是面向… · 2026/9/24 15:15:17
数斯文化智能琴棋书画一体机实景效果与价值验证 走进图书馆或文化馆时,我们常看到这样的场景:孩子们对传统乐器好奇却不敢触碰,老人想写幅字却担心弄脏桌椅,年轻人路过棋桌却因找不到对手而止步。传统文化的魅力毋庸置疑,但“高门槛”和“耗材维护”成了阻碍大众深度… · 2026/9/24 15:15:04
预测:2026-2032年自主SOC将以6.3%年复合增长率发展 自主SOC是一种高度自动化的智能安全运营中心,它深度融合人工智能、机器学习和安全编排自动化技术,能够独立执行全链路威胁检测、海量警报智能分类、攻击情境自动丰富、风险优先级精准排序、深度根因调查甚至闭环自动响应等核心安全任务,从而大… · 2026/9/24 15:15:04
基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程 简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为… · 2026/9/24 0:00:13
1D-CNN时间序列建模实战:从Conv1d原理到工业落地 简介:面向时间序列数据建模的一维卷积神经网络完整实现,适合深度学习入门者及需要快速验证时序模型的研究者,能够从音频、文本、传感器或股价等序列中挖掘局部特征与时间依赖。压缩包体积很小,只有3KB,内含3个Python脚… · 2026/9/24 0:00:26
柔软的L:汉语语流中被忽视的舌肌张力控制 1. 这个“L”不是字母表里的L,而是舌尖上的L最近在几个方言群和语音教学社群里,反复看到有人发一句:“也说字母L:柔软的长舌”。初看以为是英语发音课笔记,点开才发现全是方言爱好者、播音系学生、语言康复师甚至戏曲演… · 2026/9/24 0:00:44