用實(shí)戰(zhàn)-啟示散頁(yè)-81-題庫(kù)排序別寫死在頁(yè)面:把排序策略交給 DeckSortService)
HarmonyOS 應(yīng)用實(shí)戰(zhàn) 81題庫(kù)排序別寫死在頁(yè)面把排序策略交給 DeckSortService如果不同頁(yè)面各自排序首頁(yè)、列表、選擇器會(huì)出現(xiàn)順序不一致。這個(gè)問(wèn)題不能只靠頁(yè)面上補(bǔ)一個(gè)提示解決因?yàn)檎嬲臄帱c(diǎn)在libraryHSP/src/main/ets/services/DeckService.ets、狀態(tài)字段和服務(wù) owner 之間。下面按現(xiàn)有工程事實(shí)展開先看源碼再說(shuō)明風(fēng)險(xiǎn)鏈路最后給出可以落地的補(bǔ)強(qiáng)方式。這篇解決什么排序規(guī)則角度結(jié)論落點(diǎn)當(dāng)前事實(shí)牌組服務(wù)已經(jīng)把內(nèi)置牌組排在前面自建牌組按 updatedAt 倒序排列。libraryHSP/src/main/ets/services/DeckService.ets主要風(fēng)險(xiǎn)如果不同頁(yè)面各自排序首頁(yè)、列表、選擇器會(huì)出現(xiàn)順序不一致。排序規(guī)則要集中在服務(wù)層并用穩(wěn)定字段解釋每一次位置變化。補(bǔ)強(qiáng)方向DeckSortPolicyDeckService.list 應(yīng)返回已經(jīng)排序的 DeckSummary[]調(diào)用方不能再重排。頁(yè)面責(zé)任排序規(guī)則頁(yè)面如果需要搜索也只過(guò)濾服務(wù)層順序不重新 sort。對(duì)排序規(guī)則來(lái)說(shuō)這張表先把邊界固定住當(dāng)前事實(shí)來(lái)自libraryHSP/src/main/ets/services/DeckService.ets風(fēng)險(xiǎn)落在排序規(guī)則要集中在服務(wù)層并用穩(wěn)定字段解釋每一次位置變化。補(bǔ)強(qiáng)方案只圍繞DeckSortPolicy展開不把問(wèn)題擴(kuò)散成泛泛的 HarmonyOS 狀態(tài)管理討論?,F(xiàn)有代碼錨點(diǎn)排序規(guī)則 對(duì)應(yīng) libraryHSP/src/main/ets/services/DeckService.ets下面這段來(lái)自The_Book_of_Answers當(dāng)前工程錨點(diǎn)是summaries.sort。它不是裝飾性引用而是判斷這篇文章能不能落地的依據(jù)。0038name:d.name,0039count:d.answers.length,0040builtIn:d.builtIn,0041colorKey:d.colorKey,0042updatedAt:d.updatedAt0043};0044returnsummary;0045});0046summaries.sort((a:DeckSummary,b:DeckSummary):number{0047if(a.builtIn!b.builtIn){0048returna.builtIn?-1:1;0049}0050returnb.updatedAt-a.updatedAt;0051});0052returnsummaries;0053}0054這段代碼說(shuō)明牌組服務(wù)已經(jīng)把內(nèi)置牌組排在前面自建牌組按 updatedAt 倒序排列。 所以后續(xù)補(bǔ)強(qiáng)不能繞開這個(gè)事實(shí)如果把排序規(guī)則直接寫成頁(yè)面局部變量第一次交互可能看起來(lái)沒(méi)問(wèn)題跨頁(yè)面、重進(jìn)或發(fā)布回歸時(shí)就會(huì)暴露。失敗鏈路問(wèn)題通常不是突然出現(xiàn)如果不同頁(yè)面各自排序首頁(yè)、列表、選擇器會(huì)出現(xiàn)順序不一致。環(huán)節(jié)內(nèi)容排查動(dòng)作觸發(fā)點(diǎn)倉(cāng)儲(chǔ)讀取全部牌組確認(rèn)輸入來(lái)自哪里斷點(diǎn)排序規(guī)則要集中在服務(wù)層并用穩(wěn)定字段解釋每一次位置變化。確認(rèn)服務(wù)層有沒(méi)有攔住外顯現(xiàn)象兩個(gè)頁(yè)面順序不同確認(rèn)頁(yè)面有沒(méi)有清楚反饋這張斷點(diǎn)表是給排序規(guī)則做回歸時(shí)用的。先復(fù)現(xiàn)觸發(fā)點(diǎn)再檢查排序規(guī)則要集中在服務(wù)層并用穩(wěn)定字段解釋每一次位置變化。是否存在最后看用戶能不能從兩個(gè)頁(yè)面順序不同這種現(xiàn)象里得到下一步動(dòng)作。先排除誤區(qū)排序規(guī)則 不是頁(yè)面補(bǔ)丁遇到排序規(guī)則最容易犯的錯(cuò)是先在頁(yè)面里加一個(gè)局部狀態(tài)讓當(dāng)前場(chǎng)景看起來(lái)正常。這個(gè)做法短期能遮住現(xiàn)象但不會(huì)處理兩個(gè)頁(yè)面順序不同這類斷點(diǎn)。臨時(shí)做法看起來(lái)解決了什么后續(xù)會(huì)留下什么頁(yè)面里直接改State當(dāng)前界面刷新了服務(wù)和倉(cāng)儲(chǔ)沒(méi)有真實(shí)狀態(tài)保存失敗后直接返回用戶看不到報(bào)錯(cuò)數(shù)據(jù)可能沒(méi)有寫入每個(gè)入口各寫一份判斷某個(gè)頁(yè)面正常其他入口繼續(xù)出錯(cuò)只看成功路徑操作流程很順失敗分支無(wú)法復(fù)現(xiàn)functionunsafeHandleArticle81(reason:string):void{if(!reason){return;}promptAction.showToast({message:reason});// 只提示不收口下一次進(jìn)入仍可能遇到同一個(gè) 排序規(guī)則 問(wèn)題。}這段反例的目的不是講語(yǔ)法而是提醒邊界排序規(guī)則的根因要回到DeckService.list 應(yīng)返回已經(jīng)排序的 DeckSummary[]調(diào)用方不能再重排。頁(yè)面只能表達(dá)反饋不能替服務(wù)層決定數(shù)據(jù)是否已經(jīng)可靠。按照?qǐng)D里的順序看真正要攔住的是這一句排序規(guī)則要集中在服務(wù)層并用穩(wěn)定字段解釋每一次位置變化。 只要這個(gè)點(diǎn)沒(méi)有收口用戶看到的現(xiàn)象就會(huì)在刷新、保存、返回、導(dǎo)入或發(fā)布時(shí)反復(fù)變化。責(zé)任邊界排序規(guī)則 不該由誰(shuí)兜底DeckService 輸出統(tǒng)一順序頁(yè)面只負(fù)責(zé)展示不追加第二套排序。層級(jí)應(yīng)該負(fù)責(zé)不應(yīng)該負(fù)責(zé)頁(yè)面頁(yè)面如果需要搜索也只過(guò)濾服務(wù)層順序不重新 sort。直接改底層 key 或吞掉失敗服務(wù)DeckService.list 應(yīng)返回已經(jīng)排序的 DeckSummary[]調(diào)用方不能再重排。讓每個(gè)頁(yè)面復(fù)制一套規(guī)則倉(cāng)儲(chǔ)/配置只保存和讀取真實(shí)數(shù)據(jù)理解頁(yè)面交互語(yǔ)義排查排序規(guī)則時(shí)先問(wèn) owner再問(wèn)信號(hào)最后才看樣式。像兩個(gè)頁(yè)面順序不同通常不是單純的 UI 細(xì)節(jié)而是頁(yè)面層有額外 sort沒(méi)被服務(wù)層收住。狀態(tài)模型DeckSortPolicy建議把這件事壓成一個(gè)明確模型。下面代碼是建議補(bǔ)強(qiáng)不表示當(dāng)前工程已經(jīng)存在同名接口。interfaceDeckSortPolicy{builtInFirst:boolean;// 內(nèi)置牌組是否固定靠前primaryKey:updatedAt;// 自建牌組排序字段direction:desc;// 最近更新靠前tieBreaker:name|id;// 時(shí)間相同時(shí)的穩(wěn)定排序}functioncreateDeckSortPolicy(input:PartialDeckSortPolicy):DeckSortPolicy{return{builtInFirst:typeofinput.builtInFirstboolean?input.builtInFirst:false,primaryKey:input.primaryKey||,direction:input.direction||,tieBreaker:input.tieBreaker||name,};}DeckSortPolicy至少要能回答三個(gè)問(wèn)題輸入從哪里來(lái)、失敗怎么表達(dá)、成功之后誰(shuí)刷新。字段少一點(diǎn)沒(méi)關(guān)系但不能讓頁(yè)面靠猜來(lái)決定頁(yè)面如果需要搜索也只過(guò)濾服務(wù)層順序不重新 sort。。提交順序排序規(guī)則 要先有提交點(diǎn)把狀態(tài)模型寫出來(lái)以后還要約束提交順序。否則接口名字很好看實(shí)際調(diào)用仍可能在頁(yè)面、服務(wù)和倉(cāng)儲(chǔ)之間來(lái)回繞。頁(yè)面先收集用戶動(dòng)作只生成DeckSortPolicy或等價(jià)輸入。服務(wù)讀取必要的當(dāng)前狀態(tài)執(zhí)行DeckService.list 應(yīng)返回已經(jīng)排序的 DeckSummary[]調(diào)用方不能再重排。。通過(guò)邊界后再寫倉(cāng)儲(chǔ)、Preferences 或運(yùn)行時(shí)信號(hào)。頁(yè)面根據(jù)結(jié)果展示成功、失敗、刷新或恢復(fù)入口。asyncfunctionsubmitArticle81(state:DeckSortPolicy):Promisevoid{constresult:DeckSortPolicyResultawaitcommitDeckSortPolicy(state);if(!result.ok){DeckSortPolicyNotice(result.reason);return;}AppStorage.setOrCreate(article81ChangedAt,result.changedAt);}這個(gè)順序的價(jià)值在失敗分支。只要排序規(guī)則沒(méi)通過(guò)服務(wù)閘門就不應(yīng)該提前寫入運(yùn)行時(shí)信號(hào)也不應(yīng)該讓頁(yè)面表現(xiàn)得像已經(jīng)保存成功。服務(wù)閘門先判斷能不能提交排序規(guī)則的服務(wù)層應(yīng)該先判斷邊界再執(zhí)行寫入或刷新。下面的寫法把reason返回給頁(yè)面是為了讓同一秒內(nèi)順序跳動(dòng)能被解釋而不是只彈一個(gè)“失敗”。interfaceDeckSortPolicyResult{ok:boolean;reason:string;changedAt:number;}functionexplainDeckSortPolicy(state:DeckSortPolicy):string{if(!state.builtInFirst){returnbuiltInFirst 還沒(méi)有確認(rèn);}return;}asyncfunctioncommitDeckSortPolicy(state:DeckSortPolicy):PromiseDeckSortPolicyResult{constreason:stringexplainDeckSortPolicy(state);if(reason){return{ok:false,reason,changedAt:Date.now()};}return{ok:true,reason:ready,changedAt:Date.now()};}這里的關(guān)鍵是排序規(guī)則不滿足邊界時(shí)不要寫倉(cāng)儲(chǔ)也不要悄悄修改運(yùn)行時(shí)狀態(tài)。先返回原因頁(yè)面再?zèng)Q定展示刷新、重試、回退還是放棄入口。頁(yè)面接入提示可以清楚規(guī)則不要搬到 UI頁(yè)面要做的是把用戶動(dòng)作表達(dá)清楚。比如頁(yè)面如果需要搜索也只過(guò)濾服務(wù)層順序不重新 sort。這類信息適合放到 UI但真正的校驗(yàn)和寫入順序仍然應(yīng)該在服務(wù)層。BuilderfunctionDeckSortPolicyNotice(reason:string){if(reason){Column({space:8}){Text(排序規(guī)則).fontSize(16).fontWeight(FontWeight.Medium)Text(reason).fontSize(13).fontColor(AppColor.danger)}.padding(12).border({width:1,color:AppColor.danger}).borderRadius(AppRadius.md)}}這種接入方式對(duì)排序規(guī)則有一個(gè)好處頁(yè)面可以保持輕量服務(wù)仍然是規(guī)則 owner。以后再加列表頁(yè)、設(shè)置頁(yè)、恢復(fù)頁(yè)或發(fā)布回歸也不用圍繞DeckSortPolicy復(fù)制判斷條件。結(jié)構(gòu)收口從源碼到驗(yàn)證只走一條線排序規(guī)則的結(jié)構(gòu)圖要能互相對(duì)應(yīng)源碼錨點(diǎn)證明當(dāng)前事實(shí)DeckSortPolicy描述新增邊界服務(wù)規(guī)則決定提交方式驗(yàn)證點(diǎn)證明內(nèi)置牌組始終在自建牌組之前。沒(méi)有被吞掉。驗(yàn)證路徑成功和失敗都要跑序號(hào)驗(yàn)證點(diǎn)1內(nèi)置牌組始終在自建牌組之前。2重命名自建牌組后該牌組更新到自建區(qū)前面。3時(shí)間相同的兩副牌順序穩(wěn)定不因刷新抖動(dòng)。4選擇器和列表頁(yè)看到的順序一致。Write-Host查看 排序規(guī)則 的源碼錨點(diǎn)rg-nsummaries\.sort|DeckSortPolicyD:\ProgramData\huawei\lesson\The_Book_of_Answers\libraryHSP\src\main\ets\services\DeckService.etsWrite-Host查看相關(guān)狀態(tài)字段rg-nbuiltInFirst|primaryKey|direction|AppStorageKey|AppPrefKey|StorageLink|updatedAtD:\ProgramData\huawei\lesson\The_Book_of_Answers這些命令只能檢查排序規(guī)則的源碼落點(diǎn)和關(guān)鍵字段不代表已經(jīng)完成真機(jī)交互。涉及內(nèi)置牌組始終在自建牌組之前。這種場(chǎng)景時(shí)還需要在 DevEco/設(shè)備側(cè)補(bǔ)完整流程驗(yàn)證。負(fù)向回歸排序規(guī)則 要故意跑壞一次只跑成功路徑很容易誤判。排序規(guī)則至少要準(zhǔn)備一組負(fù)向樣本讓服務(wù)層證明它會(huì)拒絕錯(cuò)誤輸入而不是靠頁(yè)面剛好沒(méi)有觸發(fā)問(wèn)題。負(fù)向樣本期望反應(yīng)說(shuō)明缺少builtInFirst返回明確原因不寫倉(cāng)儲(chǔ)驗(yàn)證主輸入不是默認(rèn)通過(guò)觸發(fā)兩個(gè)頁(yè)面順序不同保留當(dāng)前頁(yè)面狀態(tài)驗(yàn)證失敗分支不會(huì)靜默返回模擬缺少 tieBreaker頁(yè)面顯示下一步動(dòng)作驗(yàn)證用戶能繼續(xù)處理重復(fù)執(zhí)行提交結(jié)果保持一致驗(yàn)證服務(wù)閘門能承受重進(jìn)asyncfunctionrunNegativeArticle81(state:DeckSortPolicy):Promisestring{constresult:DeckSortPolicyResultawaitcommitDeckSortPolicy(state);if(result.ok){returnunexpected success;}returnresult.reason;}這組負(fù)向樣本能把排序規(guī)則從“看起來(lái)能用”推進(jìn)到“失敗時(shí)也能解釋”。寫文章時(shí)把這部分放出來(lái)讀者才能按自己的工程復(fù)現(xiàn)。常見(jiàn)問(wèn)題先定位斷點(diǎn)現(xiàn)象常見(jiàn)原因優(yōu)先檢查兩個(gè)頁(yè)面順序不同頁(yè)面層有額外 sort移除頁(yè)面排序使用 DeckService.list同一秒內(nèi)順序跳動(dòng)缺少 tieBreaker補(bǔ)充 name 或 id 穩(wěn)定排序內(nèi)置牌組被擠到后面builtIn 優(yōu)先級(jí)丟失排序第一條件使用 builtIn遇到排序規(guī)則的異常時(shí)不要先改樣式。先確認(rèn)是哪一層沒(méi)有交接頁(yè)面是否保留builtInFirst服務(wù)是否統(tǒng)一處理規(guī)則倉(cāng)儲(chǔ)或配置是否留下舊值運(yùn)行時(shí)信號(hào)是否發(fā)出。落地順序先最小閉環(huán)再補(bǔ)體驗(yàn)在libraryHSP/src/main/ets/services/DeckService.ets附近確認(rèn)當(dāng)前鏈路避免改錯(cuò)文件。增加DeckSortPolicy或等價(jià)狀態(tài)模型字段只保留能解釋失敗的部分。把排序規(guī)則的規(guī)則放到服務(wù)層頁(yè)面只展示原因和下一步動(dòng)作。跑成功路徑和失敗路徑特別是內(nèi)置牌組始終在自建牌組之前。。constregressionForArticle81:string[][內(nèi)置牌組始終在自建牌組之前。,重命名自建牌組后該牌組更新到自建區(qū)前面。,時(shí)間相同的兩副牌順序穩(wěn)定不因刷新抖動(dòng)。];收口排序規(guī)則 的處理方法可以壓成一句話DeckService 輸出統(tǒng)一順序頁(yè)面只負(fù)責(zé)展示不追加第二套排序。 當(dāng)前工程已經(jīng)有可依賴的源碼基礎(chǔ)文章里的新增接口和函數(shù)屬于建議補(bǔ)強(qiáng)真正落地時(shí)優(yōu)先保持 owner 清楚再補(bǔ)頁(yè)面反饋。結(jié)論內(nèi)容邊界可以直接復(fù)用牌組服務(wù)已經(jīng)把內(nèi)置牌組排在前面自建牌組按 updatedAt 倒序排列。libraryHSP/src/main/ets/services/DeckService.ets建議新增DeckSortPolicyDeckService.list 應(yīng)返回已經(jīng)排序的 DeckSummary[]調(diào)用方不能再重排。暫不聲明真機(jī)行為、CSDN 上傳、發(fā)布結(jié)果沒(méi)有實(shí)際執(zhí)行就不寫成已完成補(bǔ)頁(yè)面反饋。結(jié)論內(nèi)容邊界可以直接復(fù)用牌組服務(wù)已經(jīng)把內(nèi)置牌組排在前面自建牌組按 updatedAt 倒序排列。libraryHSP/src/main/ets/services/DeckService.ets建議新增DeckSortPolicyDeckService.list 應(yīng)返回已經(jīng)排序的 DeckSummary[]調(diào)用方不能再重排。暫不聲明真機(jī)行為、CSDN 上傳、發(fā)布結(jié)果沒(méi)有實(shí)際執(zhí)行就不寫成已完成