缓存key分为内存缓存和本地缓存。如果是持久化的,需要缓存到本地中。但是,小程序中对于一些特殊结构的对象,如Map、Set的缓存是不支持的。那如果非要缓存呢,该如何实现呢?且听我娓娓道来。
点击我查看完整项目
一、内存缓存
java中,内存缓存经常用到的是集合,List、Set、Map、数组 等。那么小程序都有啥呢?答案是数组、Set、Map集合,没有List集合。当然,此Map非java中的那个Map,java中Map是个抽象类, 具体的实现类是HashMap、LinkedHashMap等。
首先来介绍下小程序中的Set、Map基本上使用
1、Set
去除重复成员的缓存容器
(1)Set实例化
const s = new Set();
Set 函数可以接受一个数组
const set = new Set([1, 2, 3, 4, 4]);
(2)api介绍
add(value):添加某个值,返回 Set 结构本身。
delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
has(value):返回一个布尔值,表示该值是否为Set的成员。
clear():清除所有成员,没有返回值。
Array.from方法可以将 Set 结构转为数组。
(3)Set遍历
keys():返回键名的遍历器
values():返回键值的遍历器
entries():返回键值对的遍历器
forEach():使用回调函数遍历每个成员
示例:
let set = new Set(['red', 'green', 'blue']);
for (let item of set.keys()) {
console.log(item);
}
// red
// green
// blue
for (let item of set.values()) {
console.log(item);
}
// red
// green
// blue
for (let item of set.entries()) {
console.log(item);
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]
/**
* 直接用for...of循环遍历(常规)
*/
for (let x of set) {
console.log(x);
}
// red
// green
// blue
//forEach的遍历
set = new Set([1, 4, 9]);
set.forEach((value, key) => console.log(key + ' : ' + value))
// 1 : 1
// 4 : 4
// 9 : 9
2、Map
key-value 键值对形式。
key:的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,
(1)Map的api介绍
const map = new Map([
['name', '张三'],
['title', 'Author']
]);
map.set('edition', 6) // 键是字符串
map.set(262, 'standard') // 键是数值
map.set(undefined, 'nah') // 键是 undefined
map.size // 2
map.has('name') // true
map.get('name') // "张三"
map.has('title') // true
map.get('title') // "Author"
map.clear()
map.size // 0
(2)Map的遍历
keys():返回键名的遍历器。
values():返回键值的遍历器。
entries():返回所有成员的遍历器。
forEach():遍历 Map 的所有成员。
示例:
const map = new Map([
['F', 'no'],
['T', 'yes'],
]);
for (let key of map.keys()) {
console.log(key);
}
// "F"
// "T"
for (let value of map.values()) {
console.log(value);
}
// "no"
// "yes"
for (let item of map.entries()) {
console.log(item[0], item[1]);
}
// "F" "no"
// "T" "yes"
// 或者
for (let [key, value] of map.entries()) {
console.log(key, value);
}
// "F" "no"
// "T" "yes"
// 等同于使用map.entries()
for (let [key, value] of map) {
console.log(key, value);
}
// "F" "no"
// "T" "yes"
注意:
1、Map 结构转为数组结构
const map = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'],
]);
[...map.keys()]
// [1, 2, 3]
[...map.values()]
// ['one', 'two', 'three']
[...map.entries()]
// [[1,'one'], [2, 'two'], [3, 'three']]
[...map]
// [[1,'one'], [2, 'two'], [3, 'three']]
2、数组 转为 Map
new Map([
[true, 7],
[{foo: 3}, ['abc']]
])
3、Map 转为 JSON
JSON.stringify(strMapToObj(strMap));
//Map 转为对象
function strMapToObj(strMap) {
let obj = Object.create(null);
for (let [k,v] of strMap) {
obj[k] = v;
}
return obj;
}
4、JSON 转为 Map
objToStrMap(JSON.parse(jsonStr))
//对象转为 Map
function objToStrMap(obj) {
let strMap = new Map();
for (let k of Object.keys(obj)) {
strMap.set(k, obj[k]);
}
return strMap;
}
3、数组
略
二、本地缓存
还是先来看下官网的列子:
同步缓存:
try {
wx.setStorageSync('key', 'value')
} catch (e) {
}
异步缓存
wx.setStorage({
key:"key",
data:"value"
})
我想说的是,setStorageSync能直接缓存Map、Set集合吗?很不幸的是,答案不可用。但是可以缓存数组。那么如果非要缓存呢?
本文的核心来了。
经过处理后,本地缓存形式结构如下图:
1、缓存map含数组的集合
由于小程序setStorageSync只支持缓存String和对象(非集合),所以存的时候回,将map转为String。取的时候,将String转为Map
解决思路:
- 1、先遍历需要缓存的集合cacheMap,得到key和value
- 2、将value集合转为数组valueArray
- 3、新建一个Map集合tempMap,存入数据,且key是需要缓存集合cacheMap的key,value是被转换的数组valueArray
- 4、将tempMap集合转换为tempArray数组
- 5、然后将tempArray数组通过JSON.stringify(tempArray),转换为String字符串cacheString
- 6、最后将字符串cacheString缓存到缓存中,完成Map转换为String的缓存转换
注意:
1、由于小程序无法直接缓存map集合,也无法缓存Set集合,只能缓存字符串。所以最终需要将对Map集合的缓存,转变为String的缓存
2、由于JSON.stringify方法只支持将数组转换为String,所以,要在将Map转为String之前,必须将要缓存的集合cacheMap原来的value(Set集合)转为数组
3、支持如map<String,object>、map<String,Set<String>>、map<String,Set<Bean>>
不支持双层Map集合,如Map<String,Map<?>>
具体实现代码:
/** isNestedFlage: 嵌套类的复杂Map集合
* true ,缓存缓存map含数组的集合,如 map<String,Set<String>>、map<String,Set<Bean>>
* false: 缓存Map<String,Integer> Map<String,Boolean>、 Map<String,String>、Map<String,Bean>(Bean是object的实体类)
* /
export function cacheMap(cacheKey, cacheMap, isSync = true) {
if (cacheMap == null || cacheMap.length == 0 || !cacheKey) {
return false;
}
if (tempMap == null) {
tempMap = new Map()
} else {
tempMap.clear()
}
let isNestedFlage = false;
cacheMap.forEach(function (value, key, cacheMap) {
console.log("Key: %s, Value: %s", key, value);
if (Object.getPrototypeOf(value) == Array.prototype || Object.getPrototypeOf(value) == Set.prototype) {
//将value 数组
var valueArray = Array.from(value)
// // 将数组转换为一个json字符串
tempMap.set(key, valueArray)
isNestedFlage = true
}
});
if (!isNestedFlage) {
tempMap = cacheMap
}
// 将Map集合转为数组
var tempArray = [...tempMap]
var cacheString = JSON.stringify(tempArray)
cacheKeyAndValue(cacheKey, cacheString, isSync)
}
2、取缓存map
对于含单例集合,如
map<String,Set<String>>、map<String,Object>
:
思路:
- 1、先根据cacheKey获取缓存信息cacheMapInfo
- 2、将获取缓存信息cacheMapInfo转换为字符串cacheMapStr
- 3、将字符串转为Map集合tempCacheMap
- 4、由于原来缓存Map集合时,将Set集合转为了数组,所以,这里也要对数组还原成Set集合。因此,遍历tempCacheMap,将其value值(即数组),转换为Set集合
- 5、最后将tempCacheMap集合遍历转换的结果存入cacheMap集合中,并且返回
注意:
由于原来缓存Map集合时,将Set集合转为了数组,所以,这里一定要要对缓存转换后的Map集合tempCacheMap的value值(数组)还原成Set集合
具体代码实现:
export function getCacheMap(cacheKey, isSync = true) {
if (!cacheKey) {
return new Map();
}
var cacheMapInfo = getCacheValue(cacheKey)
if (!cacheMapInfo) {
return new Map();
}
var cacheMapStr = JSON.parse(cacheMapInfo)
// 字符串转换为Map集合
var tempCacheMap = util.objToMap(cacheMapStr)
let cacheMap
if (cacheMap == null) {
cacheMap = new Map()
} else {
cacheMap.clear()
}
tempCacheMap.forEach(function (value, key, tempCacheMap) {
console.log("===Key: %s, Value: %s", key, value);
var mapKey = value[0];
if (Object.getPrototypeOf(value[1]) == Set.prototype || Object.getPrototypeOf(value[1]) == Array.prototype) {
// 由于原来缓存Map集合时,将Set集合转为了数组,所以,这里也要对数组还原成Set集合
var mapValue = new Set(value[1]);
cacheMap.set(mapKey, mapValue)
} else if (Object.getPrototypeOf(value[1]) == Map.prototype) {
throw new Error("数据格式错误,暂时不支持Mvalue是Map的结果")
} else { //number、string、boolean、Object对象类型
cacheMap.set(value[0], value[1])
}
});
return cacheMap;
}
3、缓存数组、Set
- 1、支持数组Array集合、和Set集合
- 2、支持简单数据类型(Array、Set),如
set<Integer>、set<Boolean>、set<String>、set<Array>、set<Bean>
(Bean是object的实体类)- 3、支持复杂数据类型(Array、Set),如
set<Set<?>>、 set<Array<?>>、set<Set<Set<?>>、set<Set<Array<?>>> 、 set<Array<Set<?>>>
等
代码实现如下:
// isSync true 同步缓存。且默认是true,同步缓存
export function cacheArray(cacheKey, cacheArray, isSync = true) {
if (cacheArray == null || cacheArray.length == 0 || !cacheKey) {
return false;
}
let realCacheArray = util.setToArray(cacheArray)
var cacheString = JSON.stringify(realCacheArray)
cacheKeyAndValue(cacheKey, cacheString, isSync)
}
4、获取Set,同样适合数组
export function getCacheArray(cacheKey, callback = null) {
if ((callback && typeof (callback) === "function")){
var cacheInfoSync = getCacheValue(cacheKey, function (cacheInfo) {
if ((callback && typeof (callback) === "function") && cacheInfo) { //异步
let cacheArray = JSON.parse(cacheInfo)
callback(cacheArray)
}
});
}else{
var result = getCacheValue(cacheKey)
let realResult
if (result == "undefined" || result == null || result == ""){ //如果返回的是空串、或者是之前未缓存的对象,这里默认是返回空数组
realResult = []
}else{
realResult = JSON.parse(result)
}
return realResult
}
}
5、Object对象的缓存
思路:
遍历对象,获取每个属性名key,和属性值value,然后逐一缓存每个属性值
export function cacheValue(cacheInfo, isSync = true) {
if (!cacheInfo) { //cacheInfo = null 、""
return false;
}
//遍历对象,获取每个属性名key,和属性值value,然后逐一缓存每个属性值
for (var propertyName in cacheInfo) {
if (!propertyName) {
break;
}
try {
var cacheKey = "key_" + propertyName
if (isSync) { //同步缓存
wx.setStorageSync(cacheKey, cacheInfo[propertyName])
} else { //异步缓存
wx.setStorage({
key: cacheKey,
data: cacheInfo[propertyName],
})
}
if ("key_token" == cacheKey) {
getApp().globalData.token = cacheInfo[propertyName]
}
} catch (error) {
console.log("error===", error)
}
}
return true;
}
6、Object对象的获取
export function getCacheValue(cacheKey, callback = null) {
if (!cacheKey) {
return null;
}
var realCacheKey = "key_" + cacheKey;
try {
if ((callback && typeof (callback) === "function")) { //异步获取
wx.getStorage({
key: realCacheKey,
success: function (res) {
if ((callback && typeof (callback) === "function")) {
callback(res.data)
}
}
})
} else { //同步获取
return wx.getStorageSync(realCacheKey)
}
} catch (error) {
console.log(error)
}
}