当前位置: 首页 > news >正文

HarmonyOS 6商城开发学习:商品浏览记录本地存储——PersistentStorage+AppStorage驱动去重与上限截断

在HarmonyOS 6购物比价或电商类应用中,"最近浏览"是高频场景:用户进入商品详情页,自动将商品追加到浏览记录列表(去重、上限保留如20条),我的-浏览历史页按时间倒序展示,支持清空。记录只需存本地,不需要账号云同步。

官方行业实践推荐用PersistentStorage 持久化 + AppStorage 响应式驱动,本文将完整实现这一方案——含数据模型、去重截断、历史列表展示与清空。


一、需求拆解与数据模型

1. 功能点

  • 进入商品详情 → 调用addBrowseRecord(goods)→ 同ID去重后置顶、超上限截断 → 自动落盘

  • 浏览历史页 → 从 AppStorage 读取数组 →ForEach渲染卡片

  • 清空 → 置空数组 → 自动同步 PersistentStorage

2. 数据模型

// model/BrowseRecord.ets export interface BrowseGoods { id: string; name: string; price: string; // "¥199" img: Resource; // 缩略图 browseTime: number; // Date.now() 时间戳 } export const MAX_BROWSE = 20; // 最多保留条数 export const STORE_KEY = 'browse_history';

二、持久化初始化(关键一步)

必须在UIAbility 的onWindowStageCreate​ 或入口页aboutToAppear最早时机调用,将 key 与 AppStorage 双向绑定:

// 在 EntryAbility.ets 或首个 Page aboutToAppear 中 import { PersistentStorage } from '@ohos.arkui.node'; // 初始化:若磁盘无值则用 '[]' PersistentStorage.persistProp('browse_history', '[]');

⚠️persistProp(key, defaultValue)只需调用一次/应用生命周期,后续直接用AppStorage.get/set即可自动读写磁盘。


三、浏览记录管理工具类

// utils/BrowseStore.ets import { BrowseGoods, MAX_BROWSE, STORE_KEY } from '../model/BrowseRecord'; /** 读取当前浏览记录(解析JSON) */ export function loadBrowseList(): BrowseGoods[] { try { const raw = AppStorage.get<string>(STORE_KEY) ?? '[]'; return JSON.parse(raw) as BrowseGoods[]; } catch { return []; } } /** 写入并自动持久化 */ function save(list: BrowseGoods[]) { AppStorage.setOrCreate(STORE_KEY, JSON.stringify(list)); // PersistentStorage 监听 AppStorage 变化自动 flush,无需手动调 } /** 添加浏览记录(去重→置顶→截断) */ export function addBrowseRecord(goods: BrowseGoods) { let list = loadBrowseList(); // 去重:移除旧相同ID list = list.filter(i => i.id !== goods.id); // 置顶 list.unshift({ ...goods, browseTime: Date.now() }); // 上限截断 if (list.length > MAX_BROWSE) { list = list.slice(0, MAX_BROWSE); } save(list); } /** 清空浏览记录 */ export function clearBrowseHistory() { save([]); }

四、商品详情页——触发记录(示例)

// pages/GoodsDetailPage.ets import { addBrowseRecord } from '../utils/BrowseStore'; import { BrowseGoods } from '../model/BrowseRecord'; @Entry @Component struct GoodsDetailPage { // 模拟当前商品 private goods: BrowseGoods = { id: 'G007', name: 'HarmonyOS 6 旗舰耳机', price: '¥599', img: $r('app.media.ic_headphone'), browseTime: 0 }; aboutToAppear() { // 进入详情即记录浏览 addBrowseRecord(this.goods); } build() { Column() { Image(this.goods.img).width(200).height(200).objectFit(ImageFit.Contain) .margin(24) Text(this.goods.name).fontSize(18).fontWeight(FontWeight.Bold) Text(this.goods.price).fontSize(16).fontColor('#FF5722').margin(8) Text('已加入浏览记录').fontSize(12).fontColor('#999').margin(40) } .width('100%') .height('100%') .justifyContent(FlexAlign.Center) .backgroundColor(Color.White) } }

五、浏览历史页——列表展示与清空

// pages/BrowseHistoryPage.ets import { loadBrowseList, clearBrowseHistory } from '../utils/BrowseStore'; import { BrowseGoods } from '../model/BrowseRecord'; import { promptAction } from '@kit.ArkUI'; @Entry @Component struct BrowseHistoryPage { // 用 @State 同步,实际项目可在 aboutToAppear 中 loadBrowseList 初始化 // 若想完全响应式可包装成 @StorageLink('browse_history') 自行解析,此处简化为 onShow 重载 @State list: BrowseGoods[] = []; onShow() { this.list = loadBrowseList(); } build() { Column() { // 标题栏 Row { Text('浏览记录') .fontSize(20) .fontWeight(FontWeight.Bold) Blank() if (this.list.length > 0) { Text('清空') .fontSize(13) .fontColor('#FF5722') .onClick(() => { clearBrowseHistory(); this.list = []; promptAction.showToast({ message: '已清空' }); }) } } .padding({ horizontal: 16, top: 16, bottom: 8 }) // 列表 if (this.list.length === 0) { Column() { Text('暂无浏览记录') .fontSize(14) .fontColor('#BBB') } .layoutWeight(1) .justifyContent(FlexAlign.Center) } else { List() { ForEach(this.list, (item: BrowseGoods) => { ListItem() { Row({ space: 12 }) { Image(item.img) .width(64).height(64).borderRadius(8).objectFit(ImageFit.Cover) Column() { Text(item.name).fontSize(15).fontColor('#333').maxLines(1) Text(item.price).fontSize(14).fontColor('#FF5722').margin({ top: 4 }) Text(formatTime(item.browseTime)) .fontSize(11) .fontColor('#AAA') .margin({ top: 2 }) }.layoutWeight(1).alignItems(HorizontalAlign.Start) } .padding(12) } }, (item: BrowseGoods) => item.id) } .layoutWeight(1) .padding(12) } } .width('100%') .height('100%') .backgroundColor('#F5F6F8') } } // 时间格式化辅助 function formatTime(ts: number): string { if (!ts) return ''; const d = new Date(ts); return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')} ${String(d.getHours()).padStart(2,'0')}:${String(d.getMinutes()).padStart(2,'0')}`; }

六、避坑指南

问题

原因

修复

重启后记录丢失

未调PersistentStorage.persistProp()或调太晚(页面已mount)

EntryAbility.onWindowStageCreate最早调一次

存储值变[object Object]

直接AppStorage.set(STORE_KEY, list)JSON.stringify

存字符串化JSON:JSON.stringify(list)

历史页不刷新

onShow未重新loadBrowseList()

onShow中刷新@State list

ForEach key用index

列表重排可能UI异常

key用item.id(唯一商品ID)

超限不截断

unshiftslice(0,MAX)

添加后list = list.slice(0, MAX_BROWSE)


七、总结:浏览记录存储SOP

  1. 初始化PersistentStorage.persistProp('browse_history','[]')应用启动时执行一次

  2. 进入详情addBrowseRecord()→ 去重→置顶→截断→AppStorage.set(字符串化数组)

  3. 历史页onShowloadBrowseList()渲染;清空调clearBrowseHistory()。

  4. PersistentStorage自动落盘:AppStorage值变化即异步写磁盘,无需手动flush。

核心法则:HarmonyOS 6本地浏览记录 =PersistentStorage绑定AppStorage + JSON序列化数组 + 去重置顶截断,轻量、响应、无服务端依赖。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任。

http://www.jsqmd.com/news/975548/

相关文章:

  • 2026真空绝热板行业深度:能效新国标倒逼百亿市场洗牌,五家核心制造商技术实力与服务能力横向拆解 - 品研笔录
  • 2026年9款AI面试工具全景盘点:精选测评与终极选择指南
  • StarCore SC140 DSP混合编程:C调用汇编的ABI、堆栈与优化实践
  • 曲靖市黄金回收白银回收铂金回收实测 + 5 家正规线下门店盘点 - 信誉隆金银铂奢回收
  • 2026北京美国本科转学中介怎么挑?GPA与课程匹配度是关键 - 品牌2026
  • 别再乱调TCP参数了!一次生产环境HTTP请求RST丢包排查,我搞懂了tcp_tw_recycle和timestamps的坑
  • 钦州市黄金回收白银回收铂金回收攻略,实地甄选五家优质实体店 - 诚金汇钻回收公司
  • MCprep技术架构深度解析:Blender中Minecraft工作流解决方案
  • 如何快速获取中小学智慧教育平台电子课本的PDF文件
  • 企盛教育李登老师是谁? - 制造业避坑李哥
  • 天水市黄金回收白银回收铂金回收攻略,实地甄选五家优质实体店 - 诚金汇钻回收公司
  • 【高级别会议|往届会后2个月见刊】第六届电气工程与机电一体化技术国际学术会议(ICEEMT 2026)
  • 大模型岗位深度解析:小白程序员必备进阶指南(收藏版)
  • 多款百度音频转文字会议录音转写2026年实测对比,准确度比拼,黑马胜出,差距竟然这么大
  • 基于ActiveX与VBScript的嵌入式电机控制GUI开发实战解析
  • 5分钟彻底告别风扇噪音:Windows免费风扇控制神器完全指南
  • 告别复杂配置:OpCore-Simplify智能自动化工具让黑苹果配置变得简单快捷
  • CAN总线错误处理与MSCAN中断服务程序实战解析
  • 2026年大连全屋定制怎么选?源头工厂直营 vs 品牌连锁的真实对比与避坑指南 - 精选优质企业推荐官
  • DiffSinger:基于浅层扩散机制的高保真歌唱语音合成系统
  • 2026年PDF解密软件主流厂商横评:如何选合适的服务商 - 资讯速览
  • 企业微信SCRM怎么选才能不踩坑?选型参考与常见问题梳理 - 资讯速览
  • 磁力链接转种子文件终极指南:Magnet2Torrent深度解析与技术实现
  • MPC8xx嵌入式系统SDRAM接口设计与UPM编程实战指南
  • 丽水市黄金回收避坑指南,2026最新行情和正规回收标准 - 润富黄金回收
  • 2026龙岩市黄金回收白银回收铂金回收怎么变现?实地探访 5 家本地老牌回收店铺 - 中安检金银铂钻回收
  • AI-Shoujo HF Patch终极指南:如何用70+插件一键提升游戏体验
  • Windows下免安装的耳机插拔实时监听工具(C++源码+编译好的exe)
  • 嵌入式硬件安全实践:基于PKCS#11标准集成NXP HSE引擎
  • 八、shell脚本