緩存

背景

接口連路很長導(dǎo)致返回數(shù)據(jù)很慢,頁面長時間loading空白,甚至超時報錯,導(dǎo)致整個站的用戶體驗很差。場景特點:

  • 數(shù)據(jù)量比較大,
  • 僅用于進入頁面的時候展示,替代空白的loading頁面或者后端接口報錯的展示數(shù)據(jù)
  • 不要求兼容性

為什么要用緩存

  • 減少網(wǎng)絡(luò)寬帶消耗,減輕擁塞
  • 降低服務(wù)器的壓力
  • 加快頁面的打開速度
  • 如果遠程服務(wù)器故障造成遠程服務(wù)器無法響應(yīng),打開歷史版本可以提供用戶體驗

緩存分類

  • 數(shù)據(jù)庫緩存
  • 服務(wù)器緩存
    • 代理服務(wù)器緩存
    • CDN緩存
  • 瀏覽器緩存
    • HTTP header緩存機制
      • 強緩存(cache-control expires)
      • 協(xié)商緩存(Etag last-modified)
    • 數(shù)據(jù)緩存
      • cookie
      • storage (Local Storage和Session Storage)
      • indexDB
      • web SQL
    • 應(yīng)用緩存
      • mainifest

因為數(shù)據(jù)庫緩存和服務(wù)器緩存不是前端的控制所以提一下,下面考慮瀏覽器緩存,針對場景更多的考慮數(shù)據(jù)緩存

數(shù)據(jù)緩存方式對比

方式 存儲的大小 兼容性 難易程度 影響
cookie 4k 兼容 手動封裝,有難度 和http請求一起發(fā)送
storage 5M ie8 簡單 不和http一起發(fā)送
IndexedDB 無限容量 ie11,safari9.3,opera不支持 復(fù)雜 不和http一起發(fā)送;異步操作大量數(shù)據(jù)操作不會拖慢網(wǎng)頁

IndexedDB

名詞解釋
  • 關(guān)系型數(shù)據(jù)庫:后端存儲數(shù)據(jù)用到的一般是關(guān)系型數(shù)據(jù)庫,例如存儲人員信息,根據(jù)人的id去關(guān)聯(lián)他的考勤,發(fā)布的文章等等。它對于一致性的要求非常嚴格
  • 非關(guān)系型數(shù)據(jù)庫:數(shù)據(jù)結(jié)構(gòu)不固定,非常容易拓展,常見的有3個,1、數(shù)據(jù)結(jié)構(gòu)類似json的key:value,典型的是radius;2、以列來定義每一個表,每一行的內(nèi)容可以不同,茹茹第一行存人員信息,第二行存商品信息;3、mongodb,類似關(guān)系型數(shù)據(jù)庫的非關(guān)系型數(shù)據(jù)庫
  • 游標:可以理解為卡尺,一行行的數(shù)據(jù)
  • 鎖:數(shù)據(jù)庫中的鎖是用來保證數(shù)據(jù)庫高并發(fā)的時候數(shù)據(jù)一致性的一種機制,例如車票售出
  • 事務(wù):對數(shù)據(jù)庫的操作,而且專指一個序列上的操作,例如銀行轉(zhuǎn)帳(轉(zhuǎn)出和轉(zhuǎn)入要么都執(zhí)行要么都不執(zhí)行)
基本概念
  • 數(shù)據(jù)庫:IDBDatabase 對象
    • 可以有任意多個數(shù)據(jù)庫
    • 含有版本的概念,同時只能有一個版本的數(shù)據(jù)庫
  • 對象倉庫:IDBObjectStore 對象
    • 數(shù)據(jù)庫里面含有很多對象倉庫,類似表
  • 索引:IDBIndex 對象
    • 加速數(shù)據(jù)的檢索,可以在對象倉庫里面,為不同的屬性建立索引
  • 指針:IDBCursor 對象
  • 事務(wù):IDBTransaction 對象
    • 數(shù)據(jù)記錄的讀寫和刪改,都要通過事務(wù)完成
    • 事務(wù)對象提供error、abort和complete三個事件,用來監(jiān)聽操作結(jié)果
  • 操作請求:IDBRequest 對象
  • 主鍵集合:IDBKeyRange 對象
基礎(chǔ)操作
  • 打開數(shù)據(jù)庫
  • 建表
  • 寫數(shù)據(jù)
  • 查找數(shù)據(jù)
  • 刪除數(shù)據(jù)
  • 更改數(shù)據(jù)
  • 刪除數(shù)據(jù)庫

下面通過一個例子來完整的演示上面的操作
1、首先定義一些變量,當調(diào)用的時候傳入給函數(shù),就可以新建到自己想要的數(shù)據(jù)庫和表

let defaultParams = {
  indexdbName: '', // 數(shù)據(jù)庫的名字
  indexdbVersion: 1, // 數(shù)據(jù)庫的版本
  tableName: '', // 數(shù)據(jù)表的名字
  data: '', // 影響的數(shù)據(jù)
  unique: 'index' // keyPath(默認是主鍵為index)
}

2、打開數(shù)據(jù)庫

// 如果發(fā)現(xiàn)沒有數(shù)據(jù)庫存在則會創(chuàng)建,執(zhí)行
openIndexDB (params) {
  // 如果瀏覽器本身不支持,則直接返回onupgradeneeded
  // 如果數(shù)據(jù)庫已經(jīng)創(chuàng)建則會直接執(zhí)行onsuccess
  if (!window.indexedDB) { return }
  let _this = this
  // 把用戶傳入的參數(shù)和默認參數(shù)做合并,作為后面建數(shù)據(jù)庫和建表的基礎(chǔ)
  let obj = Object.assign({}, defaultParams, params)
  let openDBRequest = window.indexedDB.open(obj.indexdbName, obj.indexdbVersion)
  openDBRequest.onsuccess = function () {
    // 獲取數(shù)據(jù)
    _this.getData()
  }
  openDBRequest.onerror = function () {
    console.log('openDBRequest onerror')
  }
  openDBRequest.onupgradeneeded = function (event) {
    // request(即event.target)的result屬性是IDBDatabase類型
    let db = event.target.result
    // 創(chuàng)建一個對象存儲空間來存儲信息
    // createObjectStore()方法返回的是IDBObjectStore類型的對象
    let objectStore = db.createObjectStore(obj.tableName, { keyPath: obj.unique, autoIncrement: obj.unique === 'index' })
    // 創(chuàng)建一個索引來快速查找
    // 可能會有重復(fù)的,因此我們不能使用 unique 索引。
    objectStore.createIndex('name', 'name', { unique: false })
    // 存儲數(shù)據(jù)
    // 在新創(chuàng)建的對象存儲空間中保存值
    for (let i in _this[obj.data]) {
      objectStore.add(_this[obj.data][i])
    }
  }
}

3、在表里面添加數(shù)據(jù)

addData (params) {
  if (!window.indexedDB) { return }
  let _this = this
  let obj = Object.assign({}, defaultParams, params)
  let openDBRequest = window.indexedDB.open(obj.indexdbName, obj.indexdbVersion)
  openDBRequest.onsuccess = function (event) {
    const db = event.target.result
    // 找到表,然后設(shè)置readwrite
    const transaction = db.transaction([obj.tableName], 'readwrite')
    let objectStore = transaction.objectStore(obj.tableName)
    for (let i in _this[obj.data]) {
      // 添加數(shù)據(jù)
      objectStore.add(_this[obj.data][i])
    }
  }
}

4、查找數(shù)據(jù)

getData (params) {
  let _this = this
  let obj = Object.assign({}, defaultParams, params)
  let openDBRequest = window.indexedDB.open(obj.indexdbName, obj.indexdbVersion)
  openDBRequest.onsuccess = function (event) {
    const db = event.target.result
    const transaction = db.transaction([obj.tableName], 'readwrite')
    let objectStore = transaction.objectStore(obj.tableName)
    // openCursor()方法返回一個IDBRequest類型的請求對象cursorRequest,
    // 如果該請求對象cursorRequest成功,則cursorRequest的result屬性是IDBCursorWithValue類型的對象
    let cursorRequest = objectStore.openCursor()
    let list = []
    cursorRequest.onsuccess = function (event) {
      // cursorRequest的result屬性是IDBCursorWithValue類型的對象
      // 利用游標找到所有的數(shù)據(jù)
      let cursor = event.target.result
      if (cursor) {
        list.push(cursor.value)
          cursor.continue()
        } else {
          // 把找到的數(shù)據(jù)塞到data里面
          _this[obj.data] = list
        }
    }
  }
}

5、刪除數(shù)據(jù)

// 刪除某一條數(shù)據(jù)
deleteData (params, id) {
  if (!window.indexedDB) { return }
  let obj = Object.assign({}, defaultParams, params)
  let openDBRequest = window.indexedDB.open(obj.indexdbName, obj.indexdbVersion)
  openDBRequest.onsuccess = function (event) {
    const db = event.target.result
    const transaction = db.transaction([obj.tableName], 'readwrite')
    let objectStore = transaction.objectStore(obj.tableName)
    // 根據(jù)id刪除數(shù)據(jù)
    let request = objectStore.delete(id)
    request.onsuccess = function (event) {
      // console.log('刪除數(shù)據(jù)成功', event.target.result)
    }
  }
}
// 刪除該表的所有數(shù)據(jù)
deleteAll (params) {
  if (!window.indexedDB) { return }
  let obj = Object.assign({}, defaultParams, params)
  let openDBRequest = window.indexedDB.open(obj.indexdbName, obj.indexdbVersion)
  openDBRequest.onsuccess = function (event) {
    const db = event.target.result
    const transaction = db.transaction([obj.tableName], 'readwrite')
    let objectStore = transaction.objectStore(obj.tableName)
    // openCursor()方法返回一個IDBRequest類型的請求對象cursorRequest,
    // 如果該請求對象cursorRequest成功,則cursorRequest的result屬性是IDBCursorWithValue類型的對象
    let cursorRequest = objectStore.openCursor()
    cursorRequest.onsuccess = function (event) {
      let cursor = event.target.result
      if (cursor) {
        let request = objectStore.delete(cursor.key)
        request.onsuccess = function (event) {}
        cursor.continue()
      } else {
        console.log('刪除完成')
      }
    }
  }
}

6、更改數(shù)據(jù)

updateData (params, newData) {
  if (!window.indexedDB) { return }
  let obj = Object.assign({}, defaultParams, params)
  let openDBRequest = window.indexedDB.open(obj.indexdbName, obj.indexdbVersion)
  openDBRequest.onsuccess = function (event) {
    const db = event.target.result
    const transaction = db.transaction([obj.tableName], 'readwrite')
    let objectStore = transaction.objectStore(obj.tableName)
    let request = objectStore.put(newData)
    request.onsuccess = function (event) {
      // console.log('更新數(shù)據(jù)成功')
    }
    request.onerror = function (event) {
      // console.log('更新數(shù)據(jù)失敗')
    }
  }
}

7、刪除數(shù)據(jù)庫

deleteIndexDB (params) {
  // 如果瀏覽器本身不支持,則直接返回onupgradeneeded
  // 如果數(shù)據(jù)庫已經(jīng)創(chuàng)建則會直接執(zhí)行onsuccess
  if (!window.indexedDB) { return }
  let obj = Object.assign({}, defaultParams, params)
  window.indexedDB.deleteDatabase(obj.indexdbName)
}

簡單的使用場景(vue實現(xiàn))

data () {
  return {
    list: [], // 用來渲染的列表數(shù)據(jù)
    indexdbParams: { // 創(chuàng)建數(shù)據(jù)庫用的參數(shù)
        indexdbName: 'indexedDBTest', // 數(shù)據(jù)庫的名字
        indexdbVersion: 2, // 數(shù)據(jù)庫的版本
        unique: 'index', // keyPath(默認是主鍵為index)
        tableName: 'test', // 數(shù)據(jù)表的名字
        data: 'list' // 影響的數(shù)據(jù)
    }
  }
},
created () {
  // 先打開數(shù)據(jù)庫,并get數(shù)據(jù)
  // success函數(shù)執(zhí)行了getData,所以會去獲取本地存儲的數(shù)據(jù)放到this.list,作為默認的展示
  this.openIndexDB(this.indexdbParams)
  // 同時發(fā)出請求獲取新的數(shù)據(jù)
  this.getList()
}
methods: {
  getList () {
    fetch('/users.html')
      .then(function(response) {
        // 刪除上次存儲的數(shù)據(jù)
        this.deleteAll(this.indexdbParams)
        this.list = response.data
        // 把新的數(shù)據(jù)寫進去
        this.addData(this.indexdbParams)
      })
  }
}

現(xiàn)在在控制臺的Application下面的indexedDB里面就可以看到你存儲數(shù)據(jù)了,更新之后需要在表的那個地方點擊refresh database更新查看

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容