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

羽球联盟 HarmonyOS NEXT 实战系列 (11/20):游客与登录用户的数据空间隔离

文章导读

  • 游客空间使用g,登录用户使用u_<uid>,收藏、关注、历史和浏览量都按 scope 隔离。
  • 项目没有接后端账号体系,但仍然通过稳定 uid 让同一昵称复用自己的本地空间。
  • 登录、退出登录后都会重新灌入当前空间的状态,保证个人中心、详情页和列表页同步刷新。

页面效果

这篇文章对应的用户感知非常明确:游客态和登录态的个人中心统计不能串。游客在首页收藏一篇文章后,如果再用昵称登录,看到的应该是登录用户自己的收藏、关注和浏览记录,而不是把游客数据直接带过来。

从页面上看,这只是一组数字和一个登录入口;从实现上看,它要求持久化、AppStorage 和页面响应式保持同一份事实来源。只要这里处理不好,收藏页、球员关注和浏览历史都会变得不可信。

实战拆解

羽球联盟没有对接服务端账号体系,但它并没有因此忽略“不同用户数据要隔离”这件事。PrefStore先定义一组需要按用户空间隔离的 key:favoritesfollowshistoryviewCountsJson。随后,所有读写都通过storeKey()拼接 scope 前缀,游客走g,登录用户走u_<uid>

uid 的生成也很实用。项目没有把昵称直接当成 key 使用,而是通过一个稳定哈希函数把昵称映射成 ASCII uid。这样同一个昵称多次登录会回到同一份本地空间,不同昵称之间又能自然隔离。它当然不能替代真正的账号认证,但对于本地演示、页面联调和教程讲解已经足够。

真正决定页面体验的,是rehydrateScoped()。用户登录、退出登录或重启应用时,当前空间的收藏、关注、历史和浏览量都会重新写回 AppStorage。由于详情页、个人中心、球员页都通过@StorageLink订阅这些状态,所以切换空间后,数字和按钮状态能立即反映当前身份,而不是停留在旧数据上。

关键代码

privatestaticstableUidFor(nickname:string):string{leth:number=5381;for(leti=0;i<nickname.length;i++){h=(((h<<5)+h)+nickname.charCodeAt(i))&0x7fffffff;}return'U_'+h.toString(36).toUpperCase();}privateasyncrehydrateScoped():Promise<void>{constfavStr=awaitthis.store!.get(this.storeKey('favorites'),'[]')asstring;consthistStr=awaitthis.store!.get(this.storeKey('history'),'[]')asstring;constfollowStr=awaitthis.store!.get(this.storeKey('follows'),'[]')asstring;constviewJson=awaitthis.store!.get(this.storeKey('viewCountsJson'),'{}')asstring;AppStorage.setOrCreate<string[]>('favorites',JSON.parse(favStr)asstring[]);AppStorage.setOrCreate<string[]>('history',JSON.parse(histStr)asstring[]);AppStorage.setOrCreate<string[]>('follows',JSON.parse(followStr)asstring[]);AppStorage.setOrCreate<string>('viewCountsJson',viewJson);}asynclogin(nickname:string,avatarUrl?:string):Promise<void>{constuid:string=PrefStore.stableUidFor(nickname.trim());AppStorage.setOrCreate<string>('loggedInUserId',uid);this.scope=`u_\${uid}`;awaitthis.rehydrateScoped();}

这段代码的关键点在于“空间切换之后立刻重建当前视图所需状态”。如果只是把昵称和登录态写进 Preferences,而不重新加载当前用户的收藏、关注和历史,界面看上去就像已经登录,但内容仍然是上一位用户的数据。

取舍分析

这个方案最直接的好处,是不需要引入服务端就能把“身份隔离”这件事做对。对于教程型项目来说,这比单纯演示一个登录表单更有价值,因为它把用户态真正落到了收藏、关注和历史这些可见能力上。

它的边界也必须说清楚。基于昵称生成 uid 只是为了本地空间隔离,不是安全认证。如果后续接入真实账号体系,uid 的来源、游客转正式用户的数据迁移、跨设备同步等问题都要重新设计。但在当前项目规模下,这个方案已经足够轻量而且稳定。

设计落点

  • 收藏、关注、历史和浏览量都应以当前用户空间为准,而不是放在全局 key 下共享。
  • 登录态是全局状态,用户资产是分空间状态,两者要明确区分。
  • 空间切换后必须重新写回 AppStorage,页面统计和按钮状态才会同步。

易踩坑

  • 不要把收藏、关注、历史全部写成全局 key,否则游客和登录用户会互相污染。
  • 不要把@StorageLink派生结果缓存成普通字段,切换空间后很容易出现界面不同步。
  • 本地 uid 只是演示方案,不要把它包装成真实账号安全设计。

验证方式

  • 游客先收藏一篇文章,再登录新昵称,确认收藏数切换到该昵称自己的空间。
  • 退出登录回到游客态,确认游客收藏和浏览记录仍然存在。
  • 用同一昵称再次登录,确认此前该昵称的收藏、关注和历史能被恢复。

参考资料

  • Preferences 持久化存储
  • ArkUI AppStorage 与状态管理
  • ArkUI @StorageLink 装饰器

小结

第 11 篇真正解决的是“同一个设备上的不同身份能不能看见各自的数据”。只要空间前缀、状态回灌和页面响应式三件事配合起来,哪怕没有后端账号体系,这个本地身份闭环也已经足够可靠。

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

相关文章:

  • VNote笔记云同步加密实战:用Cryptomator构建端到端安全方案
  • 移动端抓包实战:Wireshark配置与HTTPS解密全攻略
  • AI驱动软件测试自动化:核心原理、十大工具解析与落地实践
  • Spring Boot中基于拦截器与消息转换器的AES自动加解密方案
  • Spring Security Remember-Me持久化Token的5种时效管理策略
  • Rust JWT库jsonwebtoken深度解析:从算法原理到安全实践
  • AI数据分析中的数据安全保障:从隐私增强技术到模型鲁棒性实践
  • 基于Playwright与Parsel的Boss直聘招聘数据采集与存储实战
  • openEuler G11N国际化技术栈:gettext、JSON、XML资源文件实战指南
  • Python Playwright实战:绕过JS反爬,高效抓取金融数据
  • GPT-4 面试模拟实战:基于 4 个求职要诀构建 10 轮对话评测框架
  • LLM 如何把 What is 25 plus 17? 提取出表达式 25 + 17 并生成 tool_call
  • OpenBrowser:基于AI语义理解的智能浏览器自动化框架实战指南
  • ai模特mj生成一次搞定,批量电商图也能轻松制作
  • 鸿蒙数理化生体系:宏观–微观全域贯通统一理论
  • Python测试数据生成:Mock与Faker的实战应用与最佳实践
  • Spring Boot接口响应数据自动加密:基于ResponseBodyAdvice与AES的优雅实现
  • XGBoost 2.0 scale_pos_weight 实战:金融风控场景下召回率提升 15% 的调参指南
  • Java安全管理器构建在线评测系统安全沙箱:原理、实战与避坑指南
  • Java反射安全控制终极指南:setAccessible原理、风险与最佳实践
  • 基于Django的喀什地区景点推荐系统:技术栈、研究背景、核心代码与系统测试
  • 第1篇:一段 if-else 的代码,为什么成了“屎山”?
  • 团队共享 GPT5.5 API 额度设置方法
  • Symfony安全加固实战:防御会话劫持与CSRF攻击的深度指南
  • SpringBoot+Mybatis数据安全插件:基于拦截器实现字段自动加解密与脱敏
  • RootSIFT:改动最小,收益最大#
  • iOS平台JUnit测试实战:J2ObjC跨平台测试框架原理与配置指南
  • 基于Docker与Kubernetes的WebdriverIO自动化测试环境容器化实践
  • 手机号码定位神器:3分钟快速查询归属地完整指南
  • Wireshark+Fiddler手机抓包全攻略:从环境搭建到HTTPS解密与深度分析