news 2026/8/24 14:03:21

Harmony os 技术实战|拼豆制图37:最近生成记录如何做到去重、置顶与限长

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Harmony os 技术实战|拼豆制图37:最近生成记录如何做到去重、置顶与限长

用户每生成或导出一张自制图,页面都会把它放进“最近生成”。这个列表看似只是数组头插,但实际要同时满足四个规则:只记录用户生成内容、当前项排在最前、同一 ID 不重复、最多保留 10 条。若分散成多次pushfilterslice,收藏页、作品页和导出流程很容易得到不同顺序。

拼豆制图把规则集中在savePatternRecord()中。本篇逐行验证现有循环的边界,说明为什么它最多得到 10 条而不是 11 条,并进一步处理异步持久化、重复对象版本和内置图收藏等容易被忽略的数据语义。

一、先把业务不变量写成一句话

最近记录的期望可以表达为:

next = [current] + old 中所有 id != current.id 的项 next 最多取前 10 条 只有 user-generated 开头的图案进入最近记录 当前图案无论是否自制,都可以加入收藏

这里“最近记录”和“收藏”是两个集合,不能因为一次操作同时更新就混成同一规则。内置图可以收藏,但不应出现在“我的生成”;用户生成图可以既在最近列表又在收藏。

二、现有实现如何在一次循环中完成三件事

核心代码:

privatesavePatternRecord(pattern:Pattern):void{if(pattern.id.indexOf('user-generated')===0){constnextRecords:Pattern[]=[pattern];for(leti=0;i<this.generatedRecords.length;i++){if(this.generatedRecords[i].id!==pattern.id&&nextRecords.length<10){nextRecords.push(this.generatedRecords[i]);}}this.generatedRecords=nextRecords;}if(!this.isFavorite(pattern.id)){this.favoriteIds=this.favoriteIds.concat([pattern.id]);}}

循环从[pattern]开始,因此当前项天然置顶;遇到相同 ID 就跳过,因此不会重复;只有nextRecords.length < 10才追加,因此最终长度不超过 10。最后一次从 9 追加到 10,后续项全部跳过。

三、为什么条件顺序不会产生第 11 条

以旧列表已有 10 条且当前项不在其中为例:

初始 next 长度:1 追加旧记录 1..9:长度依次到 10 旧记录第 10 条:next.length < 10 为 false,不追加 最终:10

若当前项原本位于旧列表第 4 位:

初始放入新版 current 旧 1、2、3 依次追加 旧第 4 项同 ID,被跳过 继续追加后续项,直到总长 10

因此它不仅去掉重复,还用传入的新对象替换旧对象。若同一 ID 的标题、统计或格子内容发生更新,置顶项会携带最新数据。

四、ID 前缀是来源协议,不应到处复制

当前来源判断为:

pattern.id.indexOf('user-generated')===0

同一前缀还用于收藏列表中决定是否加入当前生成图。字符串协议简单,但若未来 ID 改成 UUID,这些判断会同时失效。更稳妥的模型是显式来源字段:

typePatternOrigin='built-in'|'user';interfacePattern{id:string;origin:PatternOrigin;// ...}

保存逻辑变为pattern.origin === 'user'。业务 ID 只负责身份,来源字段负责分类,二者不再互相推断。

若暂时不能改模型,也应抽出isUserPattern(pattern),避免多个页面各写一遍前缀判断。

五、收藏去重与最近记录去重要共享同一身份

收藏只保存 ID:

privateisFavorite(id:string):boolean{returnthis.favoriteIds.indexOf(id)>=0;}if(!this.isFavorite(pattern.id)){this.favoriteIds=this.favoriteIds.concat([pattern.id]);}

最近记录保存完整Pattern,但两者都用pattern.id判断身份。这样作品内容更新不会制造第二个收藏项,收藏页仍可通过 ID 找到最新版记录。

风险在于 ID 由Date.now()生成。如果极端情况下同一毫秒生成两个不同作品,它们会被视为同一项,第二个覆盖第一个。页面当前生成流程串行,概率很低,但数据层最好使用更强的唯一 ID 策略,例如时间戳加进程内递增序号。

privatestaticsequence:number=0;functionnextUserId():string{return`user-generated-${Date.now()}-${IdFactory.sequence++}`;}

六、把数组规则抽成纯函数更容易回归

页面方法同时修改两份状态,不方便单独验证。可以提取:

functionupsertRecent(current:Pattern,oldRecords:Pattern[],limit:number):Pattern[]{if(limit<=0)return[];constnext:Pattern[]=[current];for(leti=0;i<oldRecords.length&&next.length<limit;i++){if(oldRecords[i].id!==current.id){next.push(oldRecords[i]);}}returnnext;}

页面只负责来源判断和状态提交。纯函数参数显式包含limit,可以验证 0、1、10 等边界,也不会把“10”散落到界面代码。

这里不建议先filterslice再展开,虽然写法短,但会创建多个中间数组。当前最多 10 条差异不大,显式单循环更容易看出何时停止。

七、列表限长不等于存储限长

当前generatedRecords是页面@State,应用重启后会恢复为空。未来接入持久化时,要决定存什么:

摘要:id、标题、尺寸、色数、创建时间、文件路径 完整图:4900 个 chartCells + 1225 个 previewCells + 色卡

把 10 张完整图都塞进一个 Preferences JSON 会快速膨胀,也会让每次置顶都重写大量数据。更合理的是 Preferences 保存最近 ID 或摘要列表,完整图纸按 ID 写入独立 JSON 文件;删除第 11 条摘要时,再根据收藏和引用关系决定是否删除文件。

限长规则属于“最近视图”,不一定意味着用户作品只能存在 10 张。若产品要长期作品库,最近列表与作品仓库必须分离。

八、异步持久化要防止旧写覆盖新写

数组在内存中同步更新时,顺序确定。接入异步存储后,连续保存 A、B 可能出现:B 先写完,A 后写完,最终磁盘回到旧状态。

可在服务层串行写入:

privatewriteChain:Promise<void>=Promise.resolve();enqueueSave(snapshot:PatternSummary[]):Promise<void>{this.writeChain=this.writeChain.then(async()=>{awaitthis.repository.saveRecent(snapshot);});returnthis.writeChain;}

或者每次写入带updatedAt/版本号,仓库拒绝旧版本覆盖新版本。页面不要一边先更新数组,一边由多个点击处理器各自读取旧快照再保存。

导出和生成都调用savePatternRecord(),集中入口已经为后续串行化创造了条件。

九、收藏列表合并还要防跨来源重复

favoritePatterns()先遍历用户记录,再检查当前生成图,最后遍历内置图。当前生成图只有在结果中不存在同 ID 时才追加:

if(this.generatedPattern.id.indexOf('user-generated')===0&&this.isFavorite(this.generatedPattern.id)&&!this.hasPatternInList(result,this.generatedPattern.id)){result.push(this.generatedPattern);}

这一步说明“去重”不仅发生在保存时,也发生在不同数据源合并时。若未来从磁盘加载记录,应继续保证用户记录、当前编辑对象和内置仓库共享同一 ID 命名空间,或使用{origin,id}复合身份。

不要用对象引用去重。持久化反序列化后,同一业务图案会变成新对象,只有稳定 ID 才能跨生命周期识别。

十、用小数组覆盖所有边界

建议固定样本:

// 空旧列表 + A -> [A]// [A旧版] + A新版 -> [A新版]// [B,C] + A -> [A,B,C]// 10 条无 A + A -> A + 前 9 条// 10 条且 A 在末尾 + A新版 -> A新版 + 前 9 个非 A// limit=1 -> [A]// limit=0 -> []

页面验收再验证:生成同一对象后多次导出不会重复,生成 11 个不同对象后只显示最近 10 个,收藏的内置图不会混入最近生成,删除收藏不会改变最近顺序。

若出现 11 条,检查限长条件是否放在 push 之后;若当前项重复,核对 ID 是否有空格或类型差异;若新内容没有替换旧内容,确认初始数组放入的是传入对象而不是旧列表对象。

十一、结语

最近列表并不是简单的unshift()。一条可靠的更新需要同时守住来源、身份、顺序和容量。拼豆制图用“新对象先入、循环跳过同 ID、长度小于 10 才追加”的单次遍历实现了最小闭环,收藏则复用相同 ID 保持跨集合一致。

下一步演进的重点不是把循环改得更短,而是把来源从 ID 前缀升级为字段、把数组规则提成纯函数、把最近摘要与完整作品分开持久化,并让异步写入按顺序提交。这样“最近 10 条”才不会意外变成“作品最多只能有 10 条”。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/24 14:02:29

不写代码的自动化:我这一周用自然语言干掉的 5 类重复工作

不写代码的自动化&#xff1a;我这一周用自然语言干掉的 5 类重复工作 最近深度使用了两个 AI 办公平台三个月&#xff0c;确实把日常工作效率拉起来了。这周做了一次实验&#xff1a;把日常重复性工作全部交给 AI 自动化工具处理&#xff0c;记录一下真实结果。 一、我干掉了哪…

作者头像 李华
网站建设 2026/8/24 14:01:34

Linux一键安装AnyDesk图形化远程桌面工具脚本|兼容Ubuntu/CentOS等主流发行版,科研办公零门槛

温馨提示&#xff1a;文末有联系方式 为什么选择AnyDesk作为Linux远程管理工具 AnyDesk凭借低延迟、高安全性及轻量级架构&#xff0c;成为科研人员、IT支持工程师和远程办公用户的首选Linux图形化远程桌面解决方案。 支持全系主流Linux系统的一键部署脚本 本安装脚本深度适配…

作者头像 李华
网站建设 2026/8/24 14:00:31

烟台家电维修疏通防水补漏一站式服务-欧米到家规范上门检修

前言在烟台这座滨海宜居城市&#xff0c;居家生活与商业办公都高度依赖各类家电设备&#xff0c;小到冰箱、洗衣机、燃气灶&#xff0c;大到中央空调、壁挂炉、商用制冷设备&#xff0c;一旦突发故障&#xff0c;会直接打乱生活节奏、影响办公经营。与此同时&#xff0c;马桶地…

作者头像 李华
网站建设 2026/8/24 14:00:05

为什么有人说自动启停“省油但伤车“?

自动启停本身不是积碳元凶&#xff0c;但它把"微型冷启动"塞进了你的堵车日常&#xff0c;发动机最脆弱的瞬间被反复触发。这笔账&#xff0c;得算在养护上。一、朋友一句"随口操心"&#xff0c;你心里咯噔一下早晚高峰&#xff0c;红灯排长龙。你的车自动…

作者头像 李华
网站建设 2026/8/24 13:58:18

十一、uni-app中的事件

一、 基础交互与手势事件 在 uni-app 移动端开发中&#xff0c;手势事件是最基础的交互能力&#xff0c;常见于按钮点击、列表操作、长按菜单以及拖拽等场景。事件名称触发时机实际开发场景tap手指触摸后马上离开按钮点击、列表项跳转、弹窗关闭&#xff08;强烈建议替代 click…

作者头像 李华