美文网首页
第四篇:小程序之缓存策略

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

作者: 码农壹号 | 来源:发表于2018-09-15 17:55 被阅读0次

    缓存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)
      }
    }
    

    相关文章

      网友评论

          本文标题:第四篇:小程序之缓存策略

          本文链接:https://www.haomeiwen.com/subject/hsjtnftx.html