文章目录
- 背景
- 一、CacheUtil 的应用层次
- 二、模式一:安全读取模式
- 三、模式二:惰性初始化(Lazy Load)
- 四、模式三:ClickUtil 防抖的依赖关系
- 五、模式四:防重复请求标记
- 六、模式五:退出登录时清理
- 七、缓存 UI 展示
- 八、CacheUtil 的局限性
- 九、命名规范建议
- 十、小结
背景
近期发现一款很有意思的HarmonyOS 三方库, 地址 @pura/harmony-utils(V1.4.0) , 作者是"桃花镇童长老", 我这里也是直接通过该作者公布的源码进行案例编写进行,写了到目前写了一部分demo ,感觉确实很有帮助,这里呢也是开始写一个系列的演示demo 供大家参考。如有帮助可以在OpenHarmony中进行下载安装进行使用哦
案例demo导航展示
↓↓↓↓↓↓接下来言归正传 ↓↓↓↓
一、CacheUtil 的应用层次
CacheUtil看起来只是一个简单的键值存储,但在实际开发中可以承担多种角色:
- 工具基础设施:作为其他工具类的底层存储(如
ClickUtil的防抖) - 页面级缓存:同一页面多次访问的数据缓存
- 跨组件状态共享:替代简单的全局变量
- 防重复请求:标记正在进行的网络请求,防止重复发起
效果演示
二、模式一:安全读取模式
最常见的缓存读取方式应该配合has()判断:
// 不推荐:直接 get,可能返回 undefinedconsttoken=CacheUtil.get<string>('userToken');// 如果 token 为 undefined,后续使用会报错// 推荐:先 has 再 getif(CacheUtil.has('userToken')){consttoken=CacheUtil.get<string>('userToken');// 安全使用 token}else{// 处理不存在的情况}Demo 中也遵循了这个模式:
cacheGet(){if(this.cacheKeyInput.trim()===''){this.addLog('Cache','请输入 Key','warn');return;}consthas=CacheUtil.has(this.cacheKeyInput.trim());if(!has){this.addLog('Cache',`Key "${this.cacheKeyInput}" 不存在`,'warn');return;}constval=CacheUtil.get<string|number|boolean>(this.cacheKeyInput.trim());this.addLog('Cache',`get("${this.cacheKeyInput}") =${val}`,'success');}三、模式二:惰性初始化(Lazy Load)
适合计算代价较高的数据:
// 只计算一次,后续直接从缓存读取functiongetExpensiveConfig():Config{constCACHE_KEY='app_config';if(!CacheUtil.has(CACHE_KEY)){constconfig=computeExpensiveConfig();// 耗时计算CacheUtil.put<Config>(CACHE_KEY,config);}returnCacheUtil.get<Config>(CACHE_KEY);}四、模式三:ClickUtil 防抖的依赖关系
ClickUtil的防抖实现完全依赖CacheUtil,这是工具类协作的典型示例:
// ClickUtil 内部的防抖实现staticdebounce(func:()=>void,wait:number=1000,clickId:string=ClickUtil.defaultId){// 1. 从 CacheUtil 读取上一次的 timeoutIDletcacheID=CacheUtil.get<number>(`ClickUtil_debounce_timeoutID_${clickId}`);// 2. 如果存在,清除上一次的定时器if(cacheID!==undefined&&cacheID!==null){clearTimeout(cacheID);}// 3. 设置新的定时器lettimeoutID=setTimeout(()=>{typeoffunc==='function'&&func();clearTimeout(timeoutID);},wait);// 4. 将新的 timeoutID 存入 CacheUtilCacheUtil.put<number>(`ClickUtil_debounce_timeoutID_${clickId}`,timeoutID);}CacheUtil在这里充当了"跨调用的状态存储":每次调用debounce都是独立的函数调用,但通过CacheUtil可以访问上一次调用存储的状态。
五、模式四:防重复请求标记
constLOADING_KEY='isUserInfoLoading';asyncfunctionloadUserInfo(){// 防止重复请求if(CacheUtil.has(LOADING_KEY)&&CacheUtil.get<boolean>(LOADING_KEY)){return;}CacheUtil.put<boolean>(LOADING_KEY,true);try{constuser=awaitfetchUserInfo();CacheUtil.put<UserInfo>('userInfo',user);}finally{CacheUtil.remove(LOADING_KEY);// 无论成功失败都要清除标记}}六、模式五:退出登录时清理
用户退出登录时,需要清理所有与用户相关的缓存:
functiononLogout(){// 方式一:逐个删除已知的 keyCacheUtil.remove('userToken');CacheUtil.remove('userInfo');CacheUtil.remove('userPermissions');// 方式二:一键清空全部(适合缓存全为用户数据的场景)CacheUtil.clear();// 方式三:只清除业务缓存,保留工具缓存// 这时需要约定好 key 的命名规范}在 Demo 中,点击"清空全部缓存"按钮就演示了clear()的效果:
cacheClearAll(){CacheUtil.clear();this.addLog('Cache','clear() 所有缓存已清空','success');this.refreshCacheList();}七、缓存 UI 展示
Demo 中展示了如何实时展示缓存内容:
// 缓存列表展示Column(){Text('缓存内容').fontSize(13).fontColor('#666').fontWeight(FontWeight.Medium).alignSelf(ItemAlign.Start).margin({bottom:8})if(this.cacheEntries.length===0){Text('暂无缓存数据').fontSize(12).fontColor('#CCC').width('100%').textAlign(TextAlign.Center).padding({top:16,bottom:16})}else{ForEach(this.cacheEntries,(e:CacheEntry)=>{Row(){Text(e.key).fontSize(12).fontColor('#D63384').fontFamily('monospace').layoutWeight(0.8).maxLines(1)Text(`:${e.value}`).fontSize(12).fontColor('#555').layoutWeight(1.2).maxLines(1)Text(e.type).fontSize(10).fontColor('#888')}.width('100%').padding({top:6,bottom:6})},(e:CacheEntry)=>e.key)}}八、CacheUtil 的局限性
| 特性 | CacheUtil | 替代方案 |
|---|---|---|
| 持久化 | ❌ 重启丢失 | PreferencesUtil(用户首选项) |
| 加密存储 | ❌ 明文 | AssetUtil(关键资产) |
| 容量限制 | 受内存限制 | 数据库或文件 |
| 类型安全 | 弱(需手动 as) | 使用 TypeScript 接口 |
| 跨进程共享 | ❌ | 共享偏好设置 |
九、命名规范建议
使用CacheUtil时,key 命名建议遵循以下规范,避免冲突:
// 使用模块前缀'user_token'// 用户模块'config_theme'// 配置模块'click_debounce_xxx'// ClickUtil 内部用(已有命名规范)// 避免过于简单的 key'token'// 容易与其他地方冲突'data'// 完全不知道存的是什么十、小结
CacheUtil虽然只有 6 个方法,但通过不同的使用模式,可以解决:
- 安全读取:
has()+get()组合 - 惰性初始化:避免重复计算
- 工具协作:为
ClickUtil等提供状态存储 - 防重复操作:标记进行中的异步操作
- 批量清理:登出时
clear()一键清空
理解CacheUtil的设计,也有助于理解整个 HoUtils 工具集内部的依赖关系。