【OpenHarmony/HarmonyOs 】收藏、最近浏览与推荐探索:打造本地化学习状态闭环
【OpenHarmony/HarmonyOs 】收藏、最近浏览与推荐探索:打造本地化学习状态闭环
本文基于我的 OpenHarmony/HarmonyOS 项目「物理视界 PhysicsVision」整理。项目中通过
PersistentStorage、@StorageLink、收藏列表、最近浏览、推荐探索等功能,构建了一套轻量本地化学习状态闭环。
这一篇单独讲:一个学习 App 如何在不登录、不联网的情况下,也能记录用户学习路径。❤️
一、为什么学习 App 需要状态闭环?
如果一个学习应用每次打开都像第一次使用,用户体验会很割裂。
好的学习应用应该知道:
- 用户学过哪些模型;
- 用户收藏了哪些重点;
- 哪些内容还没探索;
- 用户最近停留在哪里;
- 下次应该推荐什么。
这些能力不一定要依赖账号和服务器。
「物理视界」目前就是通过本地状态完成这些功能。
二、全局状态初始化
项目首页在 aboutToAppear 中初始化持久化字段:
PersistentStorage.persistProp('favorites', '')
PersistentStorage.persistProp('highScore', 0)
PersistentStorage.persistProp('totalPlayed', 0)
PersistentStorage.persistProp('visitedModels', '')
这里保存了四类数据:
favorites:收藏模型;highScore:挑战最高正确率;totalPlayed:挑战次数;visitedModels:已浏览模型。
这些字段虽然简单,但足够支撑收藏、最近浏览、成就和设置页统计。
三、使用 @StorageLink 跨组件共享
在实验室页中:
@StorageLink('favorites') favStr: string = ''
@StorageLink('visitedModels') @Watch('onVisitedChange') visitedModels: string = ''
在收藏页中:
@StorageLink('favorites') favStr: string = ''
@StorageLink('visitedModels') visitedModels: string = ''
在设置页中:
@StorageLink('favorites') favStr: string = ''
@StorageLink('highScore') highScore: number = 0
@StorageLink('totalPlayed') totalPlayed: number = 0
这样不同组件可以读写同一份状态。
用户在实验室收藏模型后,收藏页和设置页统计会同步变化。
四、收藏功能:用字符串保存索引列表
项目中收藏数据用逗号分隔字符串保存,例如:
1,5,12
判断是否收藏:
isFavorite(idx: number): boolean {
if (this.favStr.length === 0) return false
let parts: string[] = this.favStr.split(',')
for (let i = 0; i < parts.length; i++) {
if (parseInt(parts[i]) === idx) return true
}
return false
}
切换收藏:
toggleFavorite(idx: number): void {
if (this.isFavorite(idx)) {
let parts: string[] = this.favStr.split(',')
let newParts: string[] = []
for (let i = 0; i < parts.length; i++) {
if (parseInt(parts[i]) !== idx) newParts.push(parts[i])
}
this.favStr = newParts.join(',')
} else {
this.favStr = this.favStr.length === 0 ? idx.toString() : this.favStr + ',' + idx.toString()
}
}
这个方案很轻量,适合小规模模型列表。
如果后续数据量变大,可以改成 JSON 字符串或关系型本地存储。
五、收藏页:分类筛选和空状态
收藏页支持按分类筛选:
private filterNames: string[] = ['全部', '力学', '电磁学', '光学', '热学', '波动']
筛选函数:
getFilteredFavList(): number[] {
let all: number[] = this.getFavList()
if (this.selectedFilter === 0) return all
let filterCat: string = this.filterNames[this.selectedFilter]
let result: number[] = []
for (let i = 0; i < all.length; i++) {
if (this.categoryTags[all[i]] === filterCat) {
result.push(all[i])
}
}
return result
}
如果没有收藏,页面显示空状态提示。
这比空白页面更友好,也能引导用户去实验室页添加收藏。
六、最近浏览:记录已访问模型
实验室页中记录访问:
recordVisit(idx: number): void {
if (this.visitedModels.length === 0) {
this.visitedModels = idx.toString()
return
}
let parts = this.visitedModels.split(',')
for (let i = 0; i < parts.length; i++) {
if (parseInt(parts[i]) === idx) return
}
this.visitedModels = this.visitedModels + ',' + idx.toString()
}
这里做了去重:已经访问过的模型不会重复加入。
点击模型时:
this.recordVisit(idx)
router.pushUrl({ url: this.routes[idx] }).catch(() => {})
这样路由跳转和学习记录同时发生。
七、最近浏览列表:取最近 6 个
updateRecentList(): void {
if (this.visitedModels.length === 0) {
this.recentList = []
return
}
let parts: string[] = this.visitedModels.split(',')
let result: number[] = []
let start: number = parts.length > 6 ? parts.length - 6 : 0
for (let i = parts.length - 1; i >= start; i--) {
let val: number = parseInt(parts[i])
if (val >= 0 && val < this.names.length) {
result.push(val)
}
}
this.recentList = result
}
显示最近浏览的价值很大:
- 用户可以快速回到上次学习;
- 不需要重新搜索;
- 适合碎片化学习;
- 可以作为跨设备续接的基础。
八、推荐探索:优先未学习的基础模型
推荐逻辑如下:
getRecommended(): number[] {
let result: number[] = []
for (let i = 0; i < this.names.length; i++) {
if (!this.isVisited(i) && this.difficulties[i] === '基础') {
result.push(i)
if (result.length >= 4) break
}
}
if (result.length < 4) {
for (let i = 0; i < this.names.length; i++) {
if (!this.isVisited(i) && this.difficulties[i] === '进阶') {
result.push(i)
if (result.length >= 4) break
}
}
}
return result
}
这是一种很朴素但有效的推荐:
- 先补基础;
- 再看进阶;
- 不推荐已经访问过的内容;
- 推荐数量控制在 4 个,避免信息过载。
九、统计数据:学习进度可视化
项目中有这些统计函数:
getVisitedCount(): number {
if (this.visitedModels.length === 0) return 0
return this.visitedModels.split(',').length
}
getFavCount(): number {
if (this.favStr.length === 0) return 0
return this.favStr.split(',').length
}
getProgressPercent(): number {
return Math.round(this.getVisitedCount() / this.names.length * 100)
}
这些数据用于首页 Header、设置页数据统计和成就系统。
学习状态被多个页面消费,应用就形成了闭环。
十、可继续优化的方向
后续可以做:
- 最近浏览支持重复排序,把最近点击的排最前;
- 收藏数据改成 JSON,保存收藏时间;
- 推荐逻辑结合挑战错题分类;
- 增加“继续学习”入口;
- 支持一键清空最近浏览;
- 未来做跨设备同步或备份恢复。
总结
收藏、最近浏览和推荐探索看似是小功能,但它们是学习 App 的体验骨架。
有了这些状态,应用才能从“内容列表”变成“学习路径”。
「物理视界」当前使用本地 PersistentStorage 和 @StorageLink 就实现了基础闭环,不需要账号,也不需要网络。
这种本地优先的设计,简单、稳定,也更符合教育应用的隐私需求。❤️

更多推荐



所有评论(0)