HarmonyOS RDB 收藏列表打开慢怎么办:中式美食怎么用批量查询避免循环查库

HarmonyOS RDB 收藏列表打开慢怎么办:中式美食怎么用批量查询避免循环查库 中式美食的收藏列表刚开始很顺收藏三五道菜时感觉不到问题。等用户收藏了几十道菜再打开“我的收藏”页面开始慢半拍甚至先空一下再出现卡片。这个时候不要先怪 ArkUI 列表也不要急着给页面加骨架屏。先查 RDB 查询次数很多卡顿都是一条一条查库查出来的。慢在哪里收藏列表通常有两张数据一张是收藏关系只存recipeId另一张是菜谱表存标题、封面、分类、步骤等内容。页面展示时需要先知道用户收藏了哪些菜再把这些 id 变成真正的菜谱卡片。数据适合存什么不适合存什么favorite_reciperecipeId、收藏时间菜谱标题、封面、步骤大字段recipe菜谱完整信息用户是否收藏这种个人状态页面 ViewModel展示用字段持久化真实来源这个拆法本身没错。问题出在拿到favoriteIds后怎么查菜谱。如果每个 id 都单独查一次收藏越多查询次数就越多。为什么页面会慢直接原因不是 RDB 不能查而是你让它在一个页面打开动作里做了太多零碎小活。坏写法拿一个 id 查一次下面这种代码看起来很直观先查收藏 id再循环查菜谱。asyncfunctionloadFavoriteRecipesBad():PromiseRecipeCardVM[]{constidsawaitFavoriteDao.listFavoriteRecipeIds()constresult:RecipeCardVM[][]for(constidofids){constrecipeawaitRecipeDao.findById(id)if(recipe){result.push(toCardVM(recipe))}}returnresult}它的问题也很直观收藏 5 条是 5 次查询收藏 50 条就是 50 次查询。页面看起来只是打开一个列表实际背后可能跑了几十次 SQL。这样写在调试数据少的时候没感觉上线后用户数据一多就会露馅。收藏数量循环查库次数页面风险55 次基本无感3030 次中低端设备开始慢8080 次页面首屏明显拖150150 次容易出现空白和等待这里不是说循环一定不能用而是不能把数据库查询放进这种无边界的循环里。循环处理内存数组可以循环访问 RDB 就要谨慎。好写法一次把收藏菜谱拿回来更稳的方式是先拿收藏 id再用IN条件一次查回菜谱。asyncfunctionloadFavoriteRecipes():PromiseRecipeCardVM[]{constidsawaitFavoriteDao.listFavoriteRecipeIds()if(ids.length0){return[]}constrecipesawaitRecipeDao.listByIds(ids)constrecipeMapnewMapstring,Recipe()recipes.forEach((item)recipeMap.set(item.id,item))returnids.map((id)recipeMap.get(id)).filter((item):itemisRecipe!!item).map((item)toCardVM(item))}这段代码有两个关键点。第一RDB 查询次数稳定不会随着收藏数量线性增加。第二最后用ids.map()还原顺序保证列表顺序跟收藏时间一致。很多人做批量查询后会忽略顺序结果页面排序突然乱了。DAO 里怎么写DAO 层不要把 SQL 拼得太随意。实际项目里可以按你使用的 RDB API 组织参数重点是把参数化、空数组、数量上限处理好。classRecipeDao{asynclistByIds(ids:string[]):PromiseRecipe[]{if(ids.length0){return[]}constholdersids.map(()?).join(,)constsqlSELECT id, title, cover, category, cook_time, difficulty FROM recipe WHERE id IN (${holders})constresultSetawaitthis.rdb.querySql(sql,ids)returnthis.readRecipes(resultSet)}privatereadRecipes(resultSet:relationalStore.ResultSet):Recipe[]{constrows:Recipe[][]while(resultSet.goToNextRow()){rows.push({id:resultSet.getString(resultSet.getColumnIndex(id)),title:resultSet.getString(resultSet.getColumnIndex(title)),cover:resultSet.getString(resultSet.getColumnIndex(cover)),category:resultSet.getString(resultSet.getColumnIndex(category)),cookTime:resultSet.getLong(resultSet.getColumnIndex(cook_time)),difficulty:resultSet.getString(resultSet.getColumnIndex(difficulty)),})}resultSet.close()returnrows}}这里还有一个小点收藏列表首屏不一定需要菜谱步骤、营养说明、长文本介绍。列表卡片用什么字段就只查什么字段。详情页需要完整内容时再按 id 查完整菜谱。这样能少搬很多没必要的数据。id 太多时怎么处理如果用户收藏特别多IN条件也不能无限塞。中式美食这种应用一般收藏数量不会夸张但代码还是要留边界。可以按页取收藏 id再批量查这一页对应的菜谱。asyncfunctionloadFavoritePage(page:number,pageSize:number):PromiseRecipeCardVM[]{constidsawaitFavoriteDao.listFavoriteRecipeIds(page,pageSize)if(ids.length0){return[]}constrecipesawaitRecipeDao.listByIds(ids)constmapnewMapstring,Recipe()recipes.forEach((item)map.set(item.id,item))returnids.map((id)map.get(id)).filter((item):itemisRecipe!!item).map((item)toCardVM(item))}这样页面第一次打开只拿第一页用户继续往下滑再拿下一页。解决慢的问题不是把所有东西一次塞给页面而是让首屏先稳住。做法查询次数首屏压力适合场景每个 id 查一次多高不建议全量IN查询少中收藏数量不大分页 id 批量查询少且可控低收藏数量可能变多页面层别做数据库细节ArkUI 页面层最好只关心“我要收藏列表”。数据库怎么查、怎么分批、怎么还原顺序放到 Repository 里。页面层拿到 ViewModel 后负责展示、空状态和点击跳转。StatefavoriteRecipes:RecipeCardVM[][]Stateloading:booleanfalseStateempty:booleanfalseasyncaboutToAppear(){this.loadingtruetry{this.favoriteRecipesawaitFavoriteRecipeRepository.instance.loadFirstPage()this.emptythis.favoriteRecipes.length0}finally{this.loadingfalse}}这样以后要改 RDB 查询、加缓存、加分页都不用改页面结构。页面只是根据loading、empty、favoriteRecipes展示不同状态。怎么验证是真的变快不要只凭感觉说变快了。至少做几组数据看。验证项具体做法期待结果查询次数打日志记录打开收藏列表时的 SQL 次数从 N 次变成 1 到 2 次首屏耗时记录点击到首屏出现卡片的时间收藏 50 条也稳定顺序按收藏时间倒序检查列表批量查询后顺序不乱空状态没有收藏时打开页面直接显示空状态不查菜谱表删除收藏收藏页取消一条后刷新卡片移除数量更新我会特别看“顺序”和“空状态”。很多性能改完后查询次数少了但列表顺序乱了或者空收藏还去查菜谱表。这些都说明边界没有收干净。以后怎么避免以后写这类页面先问三个问题。第一这个页面是不是先拿一批 id再拿详情数据如果是就优先考虑批量查询。第二列表卡片是不是真的需要完整表字段如果只展示标题、封面、分类就不要把长文本和步骤一起查出来。第三页面排序是谁决定的如果收藏时间决定顺序就用收藏 id 列表的顺序还原不要让数据库返回顺序随缘。中式美食的收藏列表、最近浏览、购物清单其实都是同一种问题先有一张关系表再回到菜谱表拿展示数据。把这条链路写稳以后后面做浏览记录、买菜清单、推荐回放都能复用同一套思路。不要让一个列表打开动作偷偷跑几十次查询这是 RDB 页面性能最容易被忽略的坑。