uni-app快取器的封裝

_小白_發表於2019-06-18

在前端開發應用程式中,效能一直都是被大家所重視的一點,然而判斷一個應用程式的效能最直觀的就是看頁面開啟的速度。其中提高頁頁面反應速度的一個方式就是使用快取。一個優秀的快取策略可以縮短頁面請求資源的距離,減少延遲,並且由於快取檔案可以重複利用,還可以減少頻寬,降低網路負荷。

前端常用快取技術在這裡我就不再描述,下面基於Storage對其進行增強,採用Map 基本相同的api。

閱讀以下內容時遇到不懂的,請先科普阮一峰老師的ECMAScript 6 入門

下面是基本程式碼,會在此基礎上進行增強

class MinCache {
  // 將資料儲存在本地快取中指定的 name 中
  set (name, data) {
    try {
      uni.setStorageSync(name, data)
    } catch (e) {
      console.log(e)
    }
  }
  // 從本地快取中獲取指定 name 對應的內容
  get (name) {
    let data
    try {
      data = uni.getStorageSync(name)
    } catch (e) {
      console.log(e)
    }
    return data
  }
  // 從本地快取中移除指定 key
  delete (name) {
    try {
      uni.removeStorageSync(name)
    } catch (e) {
      console.log(e)
    }
  }
  // 返回一個布林值,表示 name 是否在本地快取之中
  has (name) {
    const value
    try {
      const res = uni.getStorageInfoSync()
      value = res.keys.includes(name)
    } catch (e) {
      console.log(e)
    }
    return value
  }
  // 清理本地資料快取
  clear () {
    try {
      uni.clearStorageSync()
    } catch (e) {
      console.log(e)
    }
  }
}

export default MinCache複製程式碼

我們知道快取往往是有危害的,那麼我們最好規定個時間來去除資料。

class CacheCell {
  constructor (data, timeout) {
    this.data = data
    // 設定超時時間,單位秒
    this.timeout = timeout
    // 物件建立時候的時間
    this.createTime = Date.now()
  }
}複製程式碼
  set (name, data, timeout = 1200) {
    const cachecell = new CacheCell(data, timeout)
    try {
      uni.setStorageSync(name, cachecell)
    } catch (e) {
      console.log(e)
    }
  }
  get (name) {
    let data = null
    try {
      data = uni.getStorageSync(name)
      if (!data) return null
      const currentTime = Date.now()
      const overTime = (currentTime - data.createTime) / 1000
      if (overTime > data.timeout) {
        try {
          uni.removeStorageSync(name)
          data = null
        } catch (e) {
          console.log(e)
        }
      }
    } catch (e) {
      console.log(e)
    }
    return data
  }複製程式碼

使用了過期時間進行快取的方式,已經可以滿足絕大部分的業務場景。

uni-app的Storage在不同端的實現不同:

  • H5端為localStorage,瀏覽器限制5M大小,是快取概念,可能會被清理

  • App端為原生的plus.storage,無大小限制,不是快取,持久化

  • 各個小程式端為其自帶的storage api,資料儲存生命週期跟小程式本身一致,即除使用者主動刪除或超過一定時間被自動清理,否則資料都一直可用。

  • 微信小程式單個 key 允許儲存的最大資料長度為 1MB,所有資料儲存上限為 10MB。

  • 支付寶小程式單條資料轉換成字串後,字串長度最大200*1024。同一個支付寶使用者,同一個小程式快取總上限為10MB。

  • 百度、頭條小程式文件未說明大小限制

除此之外,H5端還支援websql、indexedDB、sessionStorage;App端還支援SQLiteIO檔案等本地儲存方案。

我們可以看出來Storage在一些端中是有大小限制的,其實我們的資料只是想要快取,不一定要持久化。

也就是說在應用程式生命週期內使用就行,而且直接操作Storage也不是很好。

我們知道ES6中有Map可以做快取

下面程式碼時基於Map封裝的

let cacheMap =  new Map()
let instance = null
let timeoutDefault = 1200

function isTimeout (name) {
  const data = cacheMap.get(name)
  if (!data) return true
  if (data.timeout === 0) return false
  const currentTime = Date.now()
  const overTime = (currentTime - data.createTime) / 1000
  if (overTime > data.timeout) {
    cacheMap.delete(name)
    return true
  }
  return false
}

class CacheCell {
  constructor (data, timeout) {
    this.data = data
    this.timeout = timeout
    this.createTime = Date.now()
  }
}

class Cache {
  set (name, data, timeout = timeoutDefault) {
    const cachecell = new CacheCell(data, timeout)
    return cacheMap.set(name, cachecell)
  }
  get (name) {
    return isTimeout(name) ? null : cacheMap.get(name).data
  }
  delete (name) {
    return cacheMap.delete(name)
  }
  has (name) {
    return !isTimeout(name)
  }
  clear () {
    return cacheMap.clear()
  }
  setTimeoutDefault (num) {
    if (timeoutDefault === 1200) {
      return timeoutDefault = num
    }
    throw Error('快取器只能設定一次預設過期時間')
  }
}

class ProxyCache {
  constructor () {
    return instance || (instance = new Cache())
  }
}

export default ProxyCache複製程式碼

對Storage和Map封裝的快取進行整合

我們來分析一下

  • Storage和Map共用一套api

    • 在命名上解決以下劃線_開頭命名的快取到Storage,並且Map也有副本

  • 儘量不操作Storage(讀取速度慢)

    • 那就必須在應用程式初始化的時候把Storage載入進Map

  • 像Vue外掛一樣使用

let cacheMap =  new Map()
let timeoutDefault = 1200

function isTimeout (name) {
  const data = cacheMap.get(name)
  if (!data) return true
  if (data.timeout === 0) return false 
  const currentTime = Date.now()
  const overTime = (currentTime - data.createTime) / 1000
  if (overTime > data.timeout) {
    cacheMap.delete(name)
    if (name.startsWith('_')) {
      try {
        uni.removeStorageSync(name)
      } catch (e) {
        console.log(e)
      }
    }
    return true
  }
  return false
}

class CacheCell {
  constructor (data, timeout) {
    this.data = data
    this.timeout = timeout
    this.createTime = Date.now()
  }
}

class MinCache {
  constructor (timeout) {
    try {
      const res = uni.getStorageInfoSync()
      res.keys.forEach(name => {
        try {
          const value = uni.getStorageSync(name)
          cacheMap.set(name, value)
        } catch (e) {
          console.log(e)
        }
      })
    } catch (e) {
      console.log(e)
    }
    timeoutDefault = timeout
  }
  set (name, data, timeout = timeoutDefault) {
    const cachecell = new CacheCell(data, timeout)
    let cache = null
    if (name.startsWith('_')) {
      try {
        uni.setStorageSync(name, cachecell)
        cache = cacheMap.set(name, cachecell)
      } catch (e) {
        console.log(e)
      }
    } else {
      cache = cacheMap.set(name, cachecell)
    }
    return cache
  }
  get (name) {
    return isTimeout(name) ? null : cacheMap.get(name).data
  }
  delete (name) {
    let value = false
    if (name.startsWith('_')) {
      try {
        uni.removeStorageSync(name)
        value = cacheMap.delete(name)
      } catch (e) {
        console.log(e)
      }
    } else {
      value = cacheMap.delete(name)
    }
    return value
  }
  has (name) {
    return !isTimeout(name)
  }
  clear () {
    let value = false
    try {
      uni.clearStorageSync()
      cacheMap.clear()
      value = true
    } catch (e) {
      console.log(e)
    }
    return value
  }
}

MinCache.install = function (Vue, {timeout = 1200} = {}) {
  Vue.prototype.$cache = new MinCache(timeout)
}

export default MinCache複製程式碼

使用方法

name以下劃線_開頭命名的快取到Storage,並且Map也有副本

事件名引數說明返回值
setname快取的key,data快取的資料,timeout(必須數字單位s)快取時間,預設快取1200s, timeout設定為0表示永久快取設定快取資料Map集合
getname快取的key獲取資料(快取過期將返回null)返回快取的資料data
hasname快取的key檢查值true/false
deletename快取的key刪除資料true/false
clear-清空Storage和Map快取資料true/false
// 註冊快取器
Vue.use(MinCache)
// 設定預設快取時間
// Vue.use(MinCache, {timeout: 600})複製程式碼
// 'name'不是以下劃線開頭的表示會快取到Map中,在程式生命週期內有並且在有效時間內有效
this.$cache.set('name', 'MinCache')

// 過期時間設定為0表示不會過期
// 注意:'test'並不是以下劃線命名錶示在程式生命週期永久快取
this.$cache.set('test', 'testdemo', 0)

// 過期時間設定為0表示不會過期
// 注意:'_imgURL'是以下劃線命名錶示永久快取到Storage
this.$cache.set('_imgURL', 'data', 0)複製程式碼
// 獲取快取的資料
this.imgURL = this.$cache.get('_imgURL')
this.name = this.$cache.get('name')
this.test = this.$cache.get('test')複製程式碼

具體使用方法可以參考github

uni-app路由的封裝



相關文章