ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

现代前端框架实战(5):数据请求与缓存

现代前端框架实战(5):数据请求与缓存 上一篇把本地、URL、全局与服务端状态分开本篇专攻最后一类。任务列表会用稳定缓存键表达依赖用 staleTime 表达“多久仍可信”并在修改时乐观更新、失败回滚、成功后再验证。一、痛点fetch 只解决传输在 Effect 中fetch还要自行处理加载、错误、组件卸载、重复请求、竞态、重试、窗口聚焦刷新和跨组件共享。更隐蔽的是旧请求后返回覆盖新筛选结果。TanStack Query 把一次查询建模为queryKey queryFn键是缓存身份函数负责获取。凡是影响结果的项目 ID、筛选、页码都必须进入键。缓存不是越久越好。staleTime表示数据在多长时间内仍新鲜gcTime表示无人订阅后保留多久前者决定是否应后台刷新后者决定内存回收。任务列表可允许几十秒陈旧权限判断与库存则需要更短窗口。HTTP Cache-Control 与客户端缓存可并存但必须明确每层失效责任。开始实现前应为每类数据写“可陈旧预算”。项目名称也许五分钟内不变协作任务允许三十秒支付状态则必须立即确认。预算来自业务后果而非库默认值数据晚一分钟会导致用户做错什么是否能通过明显的刷新提示弥补。这样即使未来换成 SWR、Apollo 或自研缓存团队仍知道该如何配置新鲜度。二、原理缓存键是数据函数的参数同一键必须对应同一语义。把对象字段排序或使用库提供的确定性哈希避免逻辑相同却生成多份缓存。请求函数收到取消信号时应传给 fetch用户快速切筛选旧请求即可终止减少带宽和错误覆盖。键还承担失效范围的索引。可以用层级工厂生成tasks.all、tasks.lists()、tasks.list(projectId, filters)和tasks.detail(id)调用方不手写结构。影响结果的租户、项目、筛选、排序、分页和语言都应进入键纯展示状态不应进入。若请求函数偷偷读取键外的变量同一个键就可能返回不同结果缓存将失去正确性。importjsonfromdataclassesimportdataclassdataclassclassEntry:value:objectsaved_at:intstale_after:intdefquery_key(project_id,filters):stablejson.dumps(filters,sort_keysTrue,ensure_asciiFalse)returnftasks:{project_id}:{stable}defread(cache,key,now):entrycache.get(key)ifentryisNone:returnmiss,Noneagenow-entry.saved_at statusfreshifageentry.stale_afterelsestalereturnstatus,entry.value keyquery_key(42,{status:open,owner:me})cache{key:Entry([编写缓存],saved_at100,stale_after30)}fornowin(110,140):status,valueread(cache,key,now)print(now,status,value)运行输出110 fresh [编写缓存] 140 stale [编写缓存]三、实现查询、变更与乐观更新创建单一 QueryClient在客户端 Provider 注入。查询键工厂集中生成[tasks, projectId, filters]避免字符串散落。UI 分清首次 pending 与已有数据时 fetching首次显示骨架后台刷新只显示轻量指示不要把稳定列表清空。错误展示可重试操作并区分认证、校验和暂时网络错误。乐观更新适用于成功概率高、回滚清晰的操作。onMutate先取消相关查询保存旧值再写入预测结果onError恢复快照onSettled失效查询让服务器成为最终裁判。创建任务还需临时 ID 与服务器 ID 对账。涉及付款或不可逆操作不要用“看起来成功”的乐观界面。fromcopyimportdeepcopy cache{tasks:42:[{id:1,title:搭建环境,done:True},{id:2,title:实现缓存,done:False},]}defoptimistic_toggle(store,key,task_id,server_accepts):snapshotdeepcopy(store[key])store[key][{**task,done:nottask[done]}iftask[id]task_idelsetaskfortaskinstore[key]]ifnotserver_accepts:store[key]snapshotreturnrolled backreturnconfirmedprint(optimistic_toggle(cache,tasks:42,2,True))print(cache[tasks:42][1])print(optimistic_toggle(cache,tasks:42,1,False))print(cache[tasks:42][0])运行输出confirmed {id: 2, title: 实现缓存, done: True} rolled back {id: 1, title: 搭建环境, done: True}服务端预取后可脱水给客户端避免首屏再次等待但要确保 QueryClient 按请求创建且只序列化可公开数据。API 层统一检查response.ok、解析错误结构并返回领域类型组件不应重复拼 URL 和翻译状态码。乐观创建比切换状态更复杂因为客户端尚无真实 ID。临时对象应带明确临时标记成功后用服务端实体替换失败后移除并保留表单输入。若用户在确认前又编辑临时任务要定义合并顺序无法清楚回滚时宁可显示提交中并等待确认。乐观更新不是速度开关而是一笔必须可逆、可对账的本地事务。错误也需要分类。认证失败交给会话层字段错误回到表单短暂网络故障允许重试资源冲突则刷新最新版本并提示用户。盲目对所有错误指数重试会放大服务端压力也可能重复非幂等写入。读取请求可在网络错误和部分服务器错误上有限重试写入请求则依赖服务端幂等键与明确策略。四、踩坑失效不是清空全部缓存每次修改后清空所有查询会制造请求风暴。根据实体关系精确失效切换任务影响该项目列表与任务详情未必影响其他项目。反之漏掉筛选维度会让不同视图共享错误数据。无限滚动的页参数也必须进入结构。另一个误区是把后台刷新显示成整页加载。缓存已有可用数据时应保留内容并显示轻量刷新状态只有首次没有数据才使用骨架。否则窗口重新聚焦就会让页面闪空用户也无法判断是刷新还是数据丢失。与此同时旧数据若具有风险需要明确标注更新时间或暂时禁止关键操作不能为了平滑而隐藏陈旧性。默认重试对 GET 通常合理对非幂等 POST 可能重复创建。接口应支持幂等键客户端按错误类型决定重试。预取也不是越多越好只预取高概率下一步并观察命中率与流量。五、验证用时间与乱序测试测试新鲜期内不重复取数、过期后保留旧数据并后台刷新、快速切筛选时旧响应不覆盖、失败乐观更新恢复原值、离线后重连刷新。开发工具观察键、观察者与失效过程网络面板检查重复请求。指标同时记录命中率、错误率、数据可见延迟和后台流量。测试时使用可控时钟和可控 Promise而不是依赖真实等待。先发起open再发起done故意让前者后返回确认当前视图仍是done让变更在乐观写入后失败确认列表、详情和统计一起恢复卸载所有消费者并推进时间确认到达回收期后条目消失。最后查看服务端日志验证一次用户动作产生的请求数量符合预期。这套方法的可迁移核心是四件事完整身份键、业务新鲜度、可逆变更和精确失效。库只替你实现调度不替你定义语义。下一篇的设计系统可以放心消费稳定数据状态而无需在按钮和列表中各自处理请求竞态。下一篇将在稳定数据层之上建立样式与组件库用设计令牌统一颜色和间距用可访问的基础组件承载交互并处理 CSS 隔离、主题和服务端渲染取舍。参考来源TanStack QueryReact 文档MDNAbortControllerMDNHTTP Caching 觉得有用就点个赞 收藏方便回头查阅有疑问直接在评论区留言我看到都会回。 本文属于《现代前端框架实战》系列持续更新关注不迷路。 文章里的代码都能直接跑。想要可直接 clone 的完整工程 配套部署脚本 / 踩坑清单评论一声或发邮件到cj2664qq.com我免费发你。如果你正好在做类似系统、或有工程化难题想找人做也欢迎邮件聊一句——我按实际情况评估能落地的就接单或出方案。评论和邮件都能直接找到我不用跳别的平台。
返回列表