第四篇:小程序之缓存策略

缓存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)
  }
}
复制代码

转载于:https://juejin.im/post/5b9cd12e5188255c9031c21a

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值