NavPathStack 返回后列表回到顶部怎么办:中式美食详情页怎么记住筛选和滚动位置

📅 2026/7/15 21:53:38
NavPathStack 返回后列表回到顶部怎么办:中式美食详情页怎么记住筛选和滚动位置
中式美食的菜谱详情页一开始看起来很简单点一张菜谱卡片进详情页看完再返回。真正做起来以后才发现麻烦不在详情页本身而在“从哪里进来”和“返回后该恢复成什么样”。比如在搜索页里筛了“番茄”列表已经滑到中间然后点进“番茄炒蛋”。看完返回时如果页面直接回到列表顶部关键词也没了刚才找菜的过程就被打断了。这个问题不是什么大功能但很影响实际使用因为每次返回都像重新开始找。问题不是 pop 一下这么简单我一开始容易把返回想简单详情页在栈顶返回就是pop。这个想法只在“首页进详情页”时还凑合一旦入口多起来就不够了。中式美食里同一个菜谱可能从这些地方进详情入口进入前页面里有什么状态返回时最怕丢什么搜索页关键词、搜索结果、滚动位置关键词清空、列表回到顶部分类页菜系、难度、排序方式分类条件重置收藏页收藏分组、当前位置返回后跳回默认收藏列表首页推荐推荐来源、卡片位置不知道从哪张推荐卡进来的购物清单食材来源、勾选状态返回后清单统计对不上所以详情页不能只拿一个dishId。dishId只能说明“打开哪道菜”不能说明“从哪里来、返回后怎么恢复”。我把返回信息拆成三类这次我会把信息拆成三类不再都塞进页面临时变量里。信息放哪里为什么菜谱 id路由参数详情页必须知道打开哪道菜来源入口路由参数返回时要知道回搜索、收藏还是推荐列表现场上一个页面自己的状态里关键词、排序、滚动位置属于列表页不属于详情页这个边界很重要。详情页负责展示菜谱不应该替搜索页保存关键词也不应该替收藏页保存分组。详情页只负责把“我是从哪里进来的”带着走返回时让来源页自己恢复现场。一个比较稳的路由上下文可以这样写typeDetailSourcesearch|category|favorite|recommend|shoppinginterfaceDishDetailRoute{dishId:stringsource:DetailSource keyword?:stringcategoryId?:stringsortKey?:stringscrollAnchor?:string}这里的scrollAnchor不一定非得是像素位置。实际项目里我更愿意用菜谱 id 当锚点比如当前列表中最后点开的那张卡片 id。像素位置容易被图片加载、卡片高度变化影响业务 id 更稳定。进入详情页时把现场带过去从搜索页进入详情时不要只传菜谱 id。functionopenDishFromSearch(dishId:string):void{constroute:DishDetailRoute{dishId,source:search,keyword:this.keyword,sortKey:this.sortKey,scrollAnchor:dishId}this.pathStack.pushPath({name:DishDetail,param:route})}这段代码解决的是“返回后不知道恢复哪一份列表现场”的问题。搜索页点进去就带搜索页的现场收藏页点进去就带收藏页的现场。不要让详情页自己猜。如果入口很多可以把构造路由参数的逻辑收起来不要每个页面都手写一遍。functionbuildDetailRoute(input:DishDetailRoute):DishDetailRoute{return{dishId:input.dishId,source:input.source,keyword:input.keyword?.trim(),categoryId:input.categoryId,sortKey:input.sortKey,scrollAnchor:input.scrollAnchor??input.dishId}}这样做的好处是后面要加新入口时不会每个页面都漏一个字段。返回时不要让详情页接管列表详情页最容易写错的地方是在返回按钮里直接改列表状态。这样短期能跑后面会越来越乱。我更倾向于让详情页只做两件事读出自己收到的路由参数。返回时把结果交还给来源页。functionbackFromDetail(route:DishDetailRoute):void{this.pathStack.pop()this.routeContextStore.markPendingRestore(route)}真正恢复关键词、分类、排序、滚动位置的动作应该在来源页里做。functionrestoreSearchPage(route:DishDetailRoute):void{if(route.source!search)returnthis.keywordroute.keyword??this.sortKeyroute.sortKey??defaultthis.reloadList()this.pendingAnchorroute.scrollAnchor}这里有个细节先恢复筛选条件再刷新列表最后滚动到锚点。顺序反了就容易出问题。比如先滚动再刷新列表滚动位置会被新列表覆盖先清空关键词再恢复来源列表会闪一下默认结果。滚动位置别直接绑死像素菜谱列表里有图片有些图片加载得慢卡片高度会变化。如果直接保存scrollY 860这种像素值返回时可能会错位。中式美食这种卡片列表更适合保存“上次点的是哪道菜”。functionrestoreScrollAfterListReady(items:RecipeCardModel[]):void{if(!this.pendingAnchor)returnconstindexitems.findIndex((item)item.idthis.pendingAnchor)if(index0)returnthis.listScroller.scrollToIndex(index)this.pendingAnchorundefined}这样做不一定百分百还原到原来的像素但能把人带回刚才那张菜谱附近。对找菜这个场景来说这比硬记像素稳。这次踩坑后我会重点看这几个地方检查点为什么要看详情页是否只认识dishId只认识 id返回现场大概率会丢来源页有没有保存自己的筛选状态不保存就只能回默认列表返回时是否先刷新再滚动顺序错了滚动会被刷新覆盖列表锚点是不是业务 id用 index 或像素筛选排序后容易错新入口有没有补全 route 字段收藏、推荐、购物清单入口最容易漏这类问题不适合靠一句“返回时保存状态”带过去。要把入口、路由、列表状态、滚动锚点都拆出来不然后面新增收藏入口、推荐入口、购物清单入口时还会继续出同样的问题。改完后解决了什么这次拆完以后页面逻辑会清楚很多详情页只管展示菜谱不再乱动搜索页和收藏页的状态。搜索页、分类页、收藏页各自恢复自己的现场不互相抢工作。返回后能回到刚才那一段列表不用重新输入关键词再找一次。新增入口时只要补source和上下文不需要重写详情页。对中式美食这种多入口菜谱应用来说这个拆法比“哪里坏了补哪里”稳。后面再加最近浏览、厨房笔记、视频拆菜入口也能沿用同一套路由上下文。以后怎么避免再踩我后面会按一个简单规则处理详情页只要一个页面能从多个地方进入就不能只传业务 id。至少要问四个问题这个详情页从哪些入口进来每个入口返回后要恢复什么状态哪些状态属于来源页哪些状态属于详情页返回时有没有稳定锚点而不是靠 index 或像素硬凑如果这四个问题没答清楚先不要急着写跳转代码。跳转写得越早后面补状态越难看。这次 NavPathStack 的重点不是 API 有多复杂而是边界要清楚路由负责把上下文带过去来源页负责恢复现场详情页不要越权。边界清了返回体验自然就稳了。