前端 ajax 加載緩存方案

前端 ajax 加載緩存方案

前言

前端頁面,對於用戶來說,衡量其好壞,最直觀的印象是加載速度。

如果頁面一點開即渲染好了,那麼用戶第一印象分也會高,其次纔是頁面設計的美觀度,頁面佈局的合理性等等因素。

一些常用的前端緩存

我想,前端工程師都知道,由於網絡傳輸的環境的限制,很多時候,頁面加載的瓶頸其實是在 ajax 請求上面的。

對於 html、css、image 等等靜態資源,服務器一般都會通知瀏覽器緩存起來,用以提升頁面下次請求的加載速度。

比如我們衆所周知的 304 Not Modified 的應用場景。

即使服務器不做這種技術處理,而比較現代點的瀏覽器,一般也會對頁面的靜態資源作出緩存處理。

比如使用 chrome,你打開 devTools,多刷新幾次,便會發現,加載的時候,有很多靜態資源是從 cache 中拿出來的。

image.png

這麼處理,有一個核心的理念,就是我們常說的:用時間換空間!

Space–time tradeoff

如果有興趣的童鞋,可以瞭解下相關話題:https://en.wikipedia.org/wiki/Space%E2%80%93time_tradeoff

我們犧牲了電腦磁盤的空間甚至是內存,爲了減少了網絡傳輸的數據量,達到優化頁面加載的效果。

當然,這裏面需要一個動態的平衡,而如何掌握好這個平衡點,纔是重中之重,纔是區分編程水平高低的分水嶺。

ajax 數據持久化

但是對於 ajax 請求的數據,這些個機制,就無能爲力了。

特別,對於前端數據可視化的應用或者 3d 遊戲應用,一些複雜的對象,都是通過模型加載到場景中去的。

而模型數據,則一般都是通過 ajax 請求來加載的。

對於小型的模型,加載起來,每次通過 ajax 也無傷大雅,加載延遲也不太能感知的到。

但是有時候模型做的稍微精細一點的話,模型的文件數據也會開始膨脹起來了。

幾兆甚至幾十兆也常常是家常便飯。

模型太大,對顯卡的要求就會增高,一般差點的顯卡就會帶不動,這個問題是後話了,我們先略去不考慮,單就說模型的數據的加載。

如果每次都從服務器去加載模型數據,這個加載成本就偏高了。

那麼,我們可以利用 localStorage 或者 indexedDB 來做 ajax 數據的持久化工作。

localStorage

localStorage 優點是用起來簡單,方便,學習成本也低。

但是有個致命的缺點是,它單個頁面,允許存儲的容量太小了!

在 stackoverflow 上,有相關話題的討論,感興趣的同學可以自己去看看:https://stackoverflow.com/questions/2989284/what-is-the-max-size-of-localstorage-values

如果想知道自己瀏覽器 localStorage 容量的限制,可以打開 devtools,粘貼入下面這段代碼,測試一下:

if (localStorage && !localStorage.getItem('size')) {
    var i = 0;
    try {
        // Test up to 10 MB
        for (i = 250; i <= 10000; i += 250) {
            localStorage.setItem('test', new Array((i * 1024) + 1).join('a'));
        }
    } catch (e) {
        localStorage.removeItem('test');
      	console.log('The max size of localStorage is:', (i - 250) / 1000, 'MB');
    }
}

chrome 瀏覽器的 localStorage 大小限制爲 5 MB,這個大小,對於我們的 3d 模型資源來說,無異於杯水車薪。

那麼對於我們來說,最好的方式就是採用 indexedDB 來做 ajax 數據的持久化。

indexedDB

indexedDB,看名字我們就知道了,他是一種數據庫,一種瀏覽器提供給我們前端頁面直接使用的數據庫。

接下來,我就簡單的介紹一下,如何用 indexedDB 來做 ajax 數據持久化。

創建數據庫

定義一個 getDB 方法,獲取數據庫實例,方便我們進行增加、查詢等操作。

傳入 databaseVersion 來控制版本,如果升級了,刪除之前的數據庫中的表(ObjectStore)

var databaseVersion = 1;
var db;
var getDB = function() {
  return new Promise(function(resolve, reject) {
    function openDatabase() {
      // 打開數據庫
      var DBOpenRequest = indexedDB.open('test', databaseVersion);

      DBOpenRequest.onsuccess = function(event) {
        if (db === "opening") db = event.target.result;
        resolve(db);
        console.log('數據庫打開成功');
      };

      function createObjectStore(db, name){
        if (db.objectStoreNames.contains(name)) {
          db.deleteObjectStore(name)
        }
        db.createObjectStore(name, { autoIncrement: true });
      }

      // 數據庫首次創建版本,或者window.indexedDB.open傳遞的新版本(版本數值要比現在的高)
      DBOpenRequest.onupgradeneeded = function(event) {
        var db = event.target.result;

        createObjectStore(db, 'modelPool');

        resolve(db);
      };
    }

    if (db === undefined) {
      db = 'opening';
      openDatabase();
    } else {
      if (db === 'opening') {
        var timer = setInterval(() => {
          if (db !== 'opening') {
            resolve(db);
            clearInterval(timer);
          }
        }, 50);
      } else {
        resolve(db);
      }
    }
  });
};

增加數據

採用最簡單的,存儲 key value 的方式,將數據存儲到數據庫中的表中去

var add = function(value, key, storeName = 'modelPool') {
  getDB().then(function(db) {
    var transaction = db.transaction([storeName], 'readwrite');
    var request = transaction.objectStore(storeName).add(value, key);

    request.onsuccess = function(event) {
      console.log('數據寫入成功');
    };

    request.onerror = function(event) {
      console.log('數據寫入失敗');
    };
  });
};

查詢數據

var read = function(url, storeName = 'modelPool') {
  return getDB().then(function(db) {
    var transaction = db.transaction([storeName]);
    var objectStore = transaction.objectStore(storeName);
    return new Promise(function(resolve, reject) {
      var request = objectStore.get(url);

      request.onerror = function(event) {
        console.log('事務失敗');
        reject();
      };

      request.onsuccess = function(event) {
        if (request.result) {
          resolve(request.result);
        } else {
          console.log('未獲得數據記錄');
          reject();
        }
      };
    });
  });
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章