uniapp运行在app端如何使用缓存
uniapp运行在app端如何使用缓存
众所周知,uniapp可以一套代码,多端运行。但是需要注意的是,window对象以及document是浏览器特有的(所以app端无法使用localStorage等api),因此,uniapp贴心的为我们准备了getStorage以及setStorage等操作缓存的api。除此以外,还有vue官方推荐的pinia也可以操作缓存。那么当这两者同时存在于一个项目时,我们应该是他们完美结合起来呢?
为什么uniapp提供了所需的api之后还要用到pinia呢?
有这么一个场景:在程序启动时我需要轮询服务器接口获取是否新数据的产生,如果有,则将hasRead字段设为true,又因为这个hasRead字段我需要在程序启动时就能拿到值,因此我是用setStorage
api将其写进缓存。那么,问题来了,我如何将缓存的字段变为响应式的呢?为什么需要响应式?因为当有新数据产生时,我需要在界面中提示用户,而这个字段我是设置在缓存当中的。最开始我是用了以下2种方式来达到响应式的结果。
// 第一种
const cache = uni.getStorageSync('hasRead')
const hasRead = ref(cache)
watch(() => hasRead.value, (newval) => {
if (newval) {
hasRead.value = newval
}
})
// 第二种
const hasRead = ref(true)
const hasReadCache = computed(() => {
const cache = uni.getStorageSync('hasRead')
if (cache) {
return cache
}
return hasRead.value
})
答案显而易见,以上2种方案都无法使缓存变化的同时响应到页面当中(有小伙伴知道原因的话,评论区留言告诉我一下为什么不可以),于是,我秉着试一试的想法使用了pinia
以及pinia-plugin-persistedstate
。
import { defineStore } from 'pinia'
import { ref } from 'vue'
export const useSettingStore = defineStore('setting', () => {
const hasRead = ref(true)
const setHasRead = (value) => {
hasRead.value = value
}
}, {
persist: {
storage: localStorage,
pick: ['hasRead']
}
})
通过pinia,我成功实现的数据的响应式,并且能够将数据存于缓存。那么,问题解决了吗?并没有。为什么呢?因为程序最终时需要运行在Android以及IOS环境的,而以上环境并没有window对象,因此也不会有localStorage。那么应该怎么办呢?机缘巧合之下,当我鼠标移入persist
中的storage
对象时,编辑器跳出来他的类型为StorageLike,直译一下就是像Storage,那么是不是可以理解为我声明一个Storage
对象,只要具有像localStorage
对象的api也可以呢?刚好uniapp也提供了类似的api,说干就干,我创建了一个类,如下:
// utils/storage.js
class Storage {
/**
* 设置缓存
* @param {string} key 缓存键
* @param {any} value 缓存值
* @param {number} [expire] 过期时间(单位:秒)
* @returns {boolean} 是否设置成功
*/
setItem(key, value, expire) {
try {
const data = {
value,
// 计算过期时间戳(如果设置了过期时间)
expireTime: expire ? Date.now() + expire * 1000 : null,
};
uni.setStorageSync(key, data);
return true;
} catch (e) {
console.error('Storage setItem error:', e);
return false;
}
}
/**
* 获取缓存(自动处理过期数据)
* @param {string} key 缓存键
* @returns {any|null} 缓存值或null
*/
getItem(key) {
try {
const data = uni.getStorageSync(key);
if (!data) return null;
// 检查是否过期
if (data.expireTime && Date.now() > data.expireTime) {
this.removeItem(key);
return null;
}
return data.value;
} catch (e) {
console.error('Storage getItem error:', e);
return null;
}
}
/**
* 删除指定缓存
* @param {string} key 缓存键
*/
removeItem(key) {
try {
uni.removeStorageSync(key);
} catch (e) {
console.error('Storage removeItem error:', e);
}
}
/**
* 清空所有缓存
*/
clear() {
try {
uni.clearStorageSync();
} catch (e) {
console.error('Storage clear error:', e);
}
}
/**
* 检查缓存是否存在
* @param {string} key 缓存键
* @returns {boolean}
*/
has(key) {
return this.keys().includes(key);
}
/**
* 获取所有缓存键
* @returns {string[]}
*/
keys() {
try {
const { keys } = uni.getStorageInfoSync();
return keys;
} catch (e) {
console.error('Storage keys error:', e);
return [];
}
}
/**
* 获取缓存信息
* @returns {{
* keys: string[],
* currentSize: number,
* limitSize: number
* }}
*/
getInfo() {
try {
return uni.getStorageInfoSync();
} catch (e) {
console.error('Storage getInfo error:', e);
return { keys: [], currentSize: 0, limitSize: 0 };
}
}
/**
* 设置缓存过期时间
* @param {string} key 缓存键
* @param {number} expire 过期时间(单位:秒)
* @returns {boolean}
*/
setExpire(key, expire) {
const value = this.getItem(key);
if (value === null) return false;
return this.setItem(key, value, expire);
}
/**
* 检查并清理所有过期缓存
*/
checkExpire() {
this.keys().forEach((key) => {
// 通过getItem自动触发过期检查
this.getItem(key);
});
}
}
// 创建单例实例
export const storage = new Storage();
利用storage对象,我将store变成以下代码:
import { defineStore } from 'pinia'
import { ref } from 'vue'
import { storage } from '@utils'
export const useSettingStore = defineStore('setting', () => {
const hasRead = ref(true)
const setHasRead = (value) => {
hasRead.value = value
}
}, {
persist: {
storage: {
getItem: (key) => storage.getItem(key),
setItem: (key, value) => storage.setItem(key, value),
removeItem: (key) => storage.removeItem(key),
},
pick: ['hasRead']
}
})
通过以上思路,我成功解决了uniapp的storage与pinia的结合,并解决了缓存的响应式问题。
最后,虽然这个轮询的方案被我废弃了,但是我还是从其中学到了之前我从未接触到的东西。还有就是如果小伙伴学到了的话,也请点赞,关注。感谢您的支持!!