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

缓存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集合吗?很不幸的是,答案不可用。但是可以缓存数组。那么如果非要缓存呢?
本文的核心来了。
经过处理后,本地缓存形式结构如下图:

缓存.jpg

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)
  }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容