缓存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// bluefor (let item of set.values()) { console.log(item);}// red// green// bluefor (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') // 键是 undefinedmap.size // 2map.has('name') // truemap.get('name') // "张三"map.has('title') // truemap.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))//对象转为 Mapfunction 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>、map > * false: 缓存Map Map 、 Map 、Map (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) }}复制代码