HarmonyOS ArkTS CacheUtil 内存缓存实战场景全解析
文章目录
- 前言
- 一、引言:为什么需要内存缓存?
- 二、CacheUtil 工具方法全览
- 三、实战场景详解
- 场景 1:缓存网络请求结果(避免重复请求)
- 场景 2:跨页面传递临时状态
- 场景 3:防抖函数内部依赖 CacheUtil(ClickUtil 源码剖析)
- 场景 4:Demo 页中的缓存读写交互
- 场景 5:状态概览 UI 联动
- 四、使用注意事项
- 五、API 快速参考
- 六、小结
前言
近期发现一款很有意思的HarmonyOS 三方库, 地址 @pura/harmony-utils(V1.4.0) , 作者是"桃花镇童长老", 我这里也是直接通过该作者公布的源码进行案例编写进行,写了到目前写了一部分demo ,感觉确实很有帮助,这里呢也是开始写一个系列的演示demo 供大家参考。如有帮助可以在OpenHarmony中进行下载安装进行使用哦
案例demo导航展示
↓↓↓↓↓↓接下来言归正传 ↓↓↓↓
一、引言:为什么需要内存缓存?
在开发鸿蒙应用时,经常遇到这些痛点:
- 同一个接口数据在页面里被反复读取,导致多次网络请求
- 用户输入的临时状态(如搜索词、表单草稿)在页面跳转后丢失
ClickUtil.debounce防抖函数内部需要保存timeoutID,每次调用都要找个地方存
CacheUtil就是解决这些问题的最轻量工具——它本质是一个全局静态Record<string, Object>,进程内共享、读写极快、零依赖。
二、CacheUtil 工具方法全览
在深入实战之前,先把所有 API 看一遍:
// CacheUtil.ets(工具类源码)exportclassCacheUtil{privatestaticcache:Record<string,Object>={};// 私有缓存对象// 获取缓存中的数据staticget<T>(key:string):T{returnCacheUtil.cache[key]asT;}// 将数据存入缓存staticput<T>(key:string,value:T):void{CacheUtil.cache[key]=valueasObject;}// 删除对应的缓存staticremove(key:string){ObjectUtil.deleteRecord(CacheUtil.cache,key);}// 缓存中的数据是否存在statichas(key:string):boolean{letkeys=Object.keys(CacheUtil.cache);returnkeys.indexOf(key)>=0}// 判断缓存是否为空staticisEmpty():boolean{letkeys=Object.keys(CacheUtil.cache);returnkeys.length<=0;}// 清除缓存数据staticclear():void{CacheUtil.cache={};}}关键特性:
- 静态类,进程级单例,所有页面/组件共享同一份缓存
- 泛型
get<T>/put<T>保证类型安全 - 底层数据结构是
Record<string, Object>,任何类型都能存
三、实战场景详解
场景 1:缓存网络请求结果(避免重复请求)
// 先检查缓存,命中则直接使用constCACHE_KEY='user_profile';asyncfunctiongetUserProfile(userId:string){if(CacheUtil.has(CACHE_KEY)){returnCacheUtil.get<UserProfile>(CACHE_KEY);}// 未命中,发起请求constprofile=awaitfetchUserProfile(userId);CacheUtil.put<UserProfile>(CACHE_KEY,profile);returnprofile;}第一次调用发网络请求并写入缓存,后续调用直接从内存读取,速度提升数十倍。
场景 2:跨页面传递临时状态
// 页面 A:搜索页,用户输入关键词后跳转CacheUtil.put('last_search_keyword',this.searchInput);router.pushUrl({url:'pages/SearchResult'});// 页面 B:结果页,恢复上次的搜索词aboutToAppear(){if(CacheUtil.has('last_search_keyword')){this.keyword=CacheUtil.get<string>('last_search_keyword');}}利用内存缓存代替路由参数传值,适合复杂对象(数组、嵌套对象)的跨页传递。
场景 3:防抖函数内部依赖 CacheUtil(ClickUtil 源码剖析)
这是 CacheUtil 最经典的内部应用场景——ClickUtil.debounce的实现完全依赖它:
// ClickUtil.ets(工具类源码)staticdebounce(func:()=>void,wait:number=1000,clickId:string=ClickUtil.defaultId){// 1. 从缓存取出上一次的 timeoutIDletcacheID=CacheUtil.get<number>(`ClickUtil_debounce_timeoutID_${clickId}`);if(cacheID!==undefined&&cacheID!==null){clearTimeout(cacheID);// 2. 清除上一次的定时器}// 3. 创建新的定时器lettimeoutID=setTimeout(()=>{typeoffunc==='function'&&func();clearTimeout(timeoutID);},wait);// 4. 将新的 timeoutID 存入缓存CacheUtil.put<number>(`ClickUtil_debounce_timeoutID_${clickId}`,timeoutID);}这就是为什么防抖能跨越多次调用"记住"上一次的定时器——靠的就是 CacheUtil 的全局共享特性。
场景 4:Demo 页中的缓存读写交互
以下是案例 Demo(CacheCharClickDemoPage.ets)中完整的缓存操作代码:
// 存入缓存cachePut(){if(this.cacheKeyInput.trim()===''){this.addLog('Cache','Key 不能为空','warn');return;}letvalue:string|number|boolean;if(this.cacheTypeSelect===0){value=this.cacheValueInput;// String 类型}elseif(this.cacheTypeSelect===1){value=Number(this.cacheValueInput)||0;// Number 类型}else{value=this.cacheValueInput.toLowerCase()==='true';// Boolean 类型}CacheUtil.put(this.cacheKeyInput.trim(),value);this.addLog('Cache',`put("${this.cacheKeyInput}",${value}) 成功`,'success');this.refreshCacheList();}// 读取缓存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');}// 删除缓存cacheRemove(){if(this.cacheKeyInput.trim()===''){this.addLog('Cache','请输入 Key','warn');return;}CacheUtil.remove(this.cacheKeyInput.trim());this.addLog('Cache',`remove("${this.cacheKeyInput}") 已删除`,'success');this.refreshCacheList();}// 清空全部缓存cacheClearAll(){CacheUtil.clear();this.addLog('Cache','clear() 所有缓存已清空','success');this.refreshCacheList();}场景 5:状态概览 UI 联动
Demo 中展示了如何用 CacheUtil API 驱动 UI 状态显示:
// UI 中实时读取缓存状态Row(){Column(){Text(`${CacheUtil.isEmpty()?'⏸️ 空':'📦 有数据'}`).fontSize(13).fontWeight(FontWeight.Bold).fontColor(CacheUtil.isEmpty()?'#FF9800':'#00C853')Text('缓存状态').fontSize(11).fontColor('#888')}.layoutWeight(1)Column(){Text(`${this.cacheEntries.length}`).fontSize(13).fontWeight(FontWeight.Bold).fontColor('#4080FF')Text('条目数').fontSize(11).fontColor('#888')}.layoutWeight(1)Column(){Text(CacheUtil.has('test_key')?'✅':'❌').fontSize(13)Text('has(test_key)').fontSize(11).fontColor('#888')}.layoutWeight(1)}.width('100%').padding(14).backgroundColor('#FFFFFF').borderRadius(12)
CacheUtil.isEmpty()决定显示"⏸️ 空"还是"📦 有数据",has('test_key')实时检查特定 key 是否存在。
四、使用注意事项
| 注意点 | 说明 |
|---|---|
| 进程级缓存 | 应用重启后缓存清空,不适合持久化数据(用 Preferences/Asset) |
| 内存占用 | 不要存入大量大体积对象,建议只存关键状态数据 |
| 线程安全 | ArkTS 主线程执行,不存在竞态问题 |
| Key 命名 | 建议用常量定义 key,避免拼写错误(如const USER_KEY = 'user_profile') |
| 生命周期 | 建议在页面销毁时主动remove不再需要的缓存 |
五、API 快速参考
| 方法 | 说明 | 典型用途 |
|---|---|---|
put<T>(key, value) | 存入任意类型的值 | 缓存接口数据、临时状态 |
get<T>(key) | 读取值,需提供泛型 | 读接口缓存、读临时状态 |
has(key) | 检查 key 是否存在 | 缓存命中判断 |
remove(key) | 删除某个 key | 数据过期/失效处理 |
isEmpty() | 检查缓存是否为空 | UI 状态显示 |
clear() | 清空全部缓存 | 用户登出、页面重置 |
六、小结
CacheUtil是一个设计极简却极为实用的工具类。它的核心价值在于:
- 减少 I/O:用内存读写替代文件/网络读写
- 跨组件通信:充当轻量级全局状态容器
- 支撑其他工具类:
ClickUtil.debounce的底层就依赖它存储定时器 ID
对于小白来说,记住这个口诀就够了:“存用 put,取用 get,查用 has,删用 remove,清用 clear”。
