在前端開發應用程式中,效能一直都是被大家所重視的一點,然而判斷一個應用程式的效能最直觀的就是看頁面開啟的速度。其中提高頁頁面反應速度的一個方式就是使用快取。一個優秀的快取策略可以縮短頁面請求資源的距離,減少延遲,並且由於快取檔案可以重複利用,還可以減少頻寬,降低網路負荷。
前端常用快取技術在這裡我就不再描述,下面基於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端還支援SQLite、IO檔案等本地儲存方案。
我們可以看出來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也有副本
事件名 | 引數 | 說明 | 返回值 |
---|---|---|---|
set | name快取的key,data快取的資料,timeout(必須數字單位s)快取時間,預設快取1200s, timeout設定為0表示永久快取 | 設定快取資料 | Map集合 |
get | name快取的key | 獲取資料(快取過期將返回null) | 返回快取的資料data |
has | name快取的key | 檢查值 | true/false |
delete | name快取的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