请求代码片段

📅 2026/8/22 22:16:46
请求代码片段
代码片段ComposablefunSearchCombinedContent(loadState:LoadState,searchCombinedItemList:ListSearchCombinedItem?,keyword:String,fetchData:()-Unit,content:ComposableLazyGridItemScope.(index:Int,item:SearchCombinedItem,ListSearchCombinedItem)-Unit){valsafeListsearchCombinedItemList?:emptyList()valsearchResultPageColumnsappLayoutProfile.searchResultPageColumns// 记录上次已进入 Loading 的 keyword, 用于判断 keyword 变化后是否还在等 ViewModel 响应varpendingKeywordbyremember{mutableStateOf()}// keyword 变化且 ViewModel 还没进 Loading 时, 持续显示 Loading 覆盖旧数据valisPendingSearchkeyword.isNotEmpty()keyword!pendingKeywordloadState!isLoadState.LoadingLaunchedEffect(keyword,loadState){if(keyword.isNotEmpty()){// ViewModel 进入 Loading, 说明已接收本次 keyword 的请求, 更新 pendingKeyword 结束 pendingif(loadStateisLoadState.Loading){pendingKeywordkeyword}valisKeywordChangedkeyword!pendingKeywordvalisNeedFetchStateloadStateisLoadState.Idle||loadStateisLoadState.LoadErrorif(isKeywordChanged||isNeedFetchState){数据驱动请求触发 fetchData:$keyword.iLog(TAG)fetchData()}}}}分析 这段代码的精妙之处解析这段代码由团队中的高级开发编写是一段教科书级别的 “声明式 UI 时序与边界控制” 逻辑。它不仅限制了 fetchData() 的重复请求还用极其高明的方式抹平了 Compose 在“输入框关键词频繁变化”到“ViewModel 网络响应”之间的视觉断层。它的精妙之处主要体现在以下三个核心防线1. 核心防线精准的“双状态交叉比对”isKeywordChanged || isNeedFetchState传统的错误写法通常只在 LaunchedEffect(keyword) 里一变就无脑调用 fetchData()。而这段代码引入了 loadState 与 pendingKeyword只有当满足以下两点之一才触发条件 AisKeywordChanged 当前最新 keyword 与已经被 ViewModel 确认消费的 pendingKeyword 不相等。条件 BisNeedFetchState 当前的加载状态是 Idle 或 LoadError 失败需要重试。拦截效果如果当前处于正常的 Loading 或 LoadSuccess 阶段且 keyword 没有变无论页面怎么重组Recomposition由于两项条件都为 falsefetchData() 都会被绝对拦截完全杜绝了多余的重复网络请求。2. 最亮眼的设计通过 pendingKeyword 建立的“请求回执单”机制痛点当用户输入了新词 keyword “B” 瞬间ViewModel 的网络请求还没发出来、状态还是上一次 keyword “A” 时的 LoadSuccess。此时如果不做处理界面会继续显示 “A” 的旧数据产生严重的数据错乱视觉欺骗。精妙解法代码声明了 pendingKeyword并在 loadState is LoadState.Loading 时才执行 pendingKeyword keyword。含义这相当于 UI 发出了请求只有看到 ViewModel 亮起 Loading 红灯才算拿到“网络回执单”。在拿到回执之前isPendingSearch 会一直为 true。有了这个变量下层 UI 就可以在 ViewModel 还没来得及改变状态时强行用 Loading 盖住上一轮的旧数据。3. 避免了由于“二次重组”引发的“状态吞噬”在 LaunchedEffect 内部只有当满足条件时才去调用 fetchData()而修改 pendingKeyword 则紧紧绑定在 loadState is LoadState.Loading 这一客观事实中。这意味着状态的同步是由数据驱动的而不是由点击事件驱动的。这确保了在车载系统弱网或极速输入时UI 表现依然高度稳定绝不卡死或错乱。下面为你输出一份标准的技术沉淀与设计模式文档可直接用于团队内部的代码评审Code Review或 Wiki 沉淀 技术文档声明式搜索页面状态联动与请求节流拦截机制## 1. 业务背景在车载媒体或大屏搜索业务中用户在搜索框SearchBar输入关键词后由于车载系统网络波动或由于输入法连续高频输入未做防抖或防抖边缘场景下容易导致以下两个技术痛点重复请求异常同一关键词在重组时被多次反复触发 fetchData()造成带宽浪费与服务器压力。视觉错乱断层数据污染当关键词从 A 变为 B 瞬间异步网络请求尚未响应页面依旧维持 A 的成功结果列表导致用户感知到“图文不符”的重大视觉缺陷。本组件通过引入“快照锁”与“请求回执”机制彻底优雅地解决了以上问题。2. 核心架构设计## 2.1 状态闭环流向图[Keyword 发生变更] ── [触发 isPendingSearch true] ── [UI 强行展示 Loading 覆盖旧数据]│ ▲▼ │[LaunchedEffect 拦截检测] │ (直到状态同步)│ │├─ 若 keyword pendingKeyword ── [绝对拦截, 拒绝请求] ││ │└─ 若 keyword ! pendingKeyword ── [触发 fetchData()] ── [ViewModel 变更 loadState 为 Loading]│▼[锁定 pendingKeyword keyword][isPendingSearch 归零, 结束悬挂]3. 代码机制深度剖析## 3.2 变量分工与“回执锁”keyword外界输入的、当前绝对实时的最新关键词。pendingKeyword已被底层 ViewModel 成功接收并开始处理的关键词快照。if (loadState is LoadState.Loading) {pendingKeyword keyword}设计精妙点这是全段代码的灵魂。UI 发起请求不算完只有检测到 loadState 确实变为 Loading 时才证明 ViewModel 已经开始为了新关键词而跑网络了。此时才把 keyword 赋值给 pendingKeyword相当于由数据驱动完成了请求的闭环确认打回执单。3.3 拦截算法与按需触发val isKeywordChanged keyword ! pendingKeywordval isNeedFetchState loadState is LoadState.Idle || loadState is LoadState.LoadErrorif (isKeywordChanged || isNeedFetchState) {fetchData()}节流逻辑如果用户没有改词且页面是由于滑动、动画引起的普通重组isKeywordChanged 必定为 false。如果此时数据早已加载成功LoadSuccessisNeedFetchState 也为 false。此时进入组件的 LaunchedEffect条件完全不成立直接跳出从而 100% 拦截了重复的网络请求。只有当用户输入新词Changed或网络彻底失败LoadError 触发重试时通道才允许被打开执行 fetchData()。3.4 视觉过渡拦截Anti-Flickerval isPendingSearch keyword.isNotEmpty() keyword ! pendingKeyword loadState !is LoadState.Loading体验升级点此变量用于 UI 层。当用户敲下搜索新词的瞬间ViewModel 的网络可能慢了 50 毫秒才切换到 Loading。在这个时差内由于 keyword ! pendingKeyword 且还没进 LoadingisPendingSearch 会立刻飙升为 true。UI 层据此可以直接切入全屏 Loading 骨架屏提前强制把上一轮的残留老数据遮挡住给车载用户带来极其丝滑、符合直觉的无缝切换体验。4. 结论与规范沉淀本方案展示了在不借助传统命令式三方框架如 RxJava/Flow 防抖的情况下单靠 Compose 声明式状态机的双向联动就能完成精密的请求节流与视觉防炫目控制。团队开发规范要求后续凡是涉及“多标签切换、输入框搜索、动态分类切换”等外部单一因变量如 ID / Key / Word驱动异步网络请求的页面必须全盘参考本案的 pending 回执模式进行重构。