前端 ajax 加载缓存方案

2020-01-17  本文已影响0人  纯爱枫若情

前言

前端页面,对于用户来说,衡量其好坏,最直观的印象是加载速度。

如果页面一点开即渲染好了,那么用户第一印象分也会高,其次才是页面设计的美观度,页面布局的合理性等等因素。

一些常用的前端缓存

我想,前端工程师都知道,由于网络传输的环境的限制,很多时候,页面加载的瓶颈其实是在 ajax 请求上面的。

对于 html、css、image 等等静态资源,服务器一般都会通知浏览器缓存起来,用以提升页面下次请求的加载速度。

比如我们众所周知的 304 Not Modified 的应用场景。

即使服务器不做这种技术处理,而比较现代点的浏览器,一般也会对页面的静态资源作出缓存处理。

比如使用 chrome,你打开 devTools,多刷新几次,便会发现,加载的时候,有很多静态资源是从 cache 中拿出来的。

image.pngimage.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();
        }
      };
    });
  });
};
上一篇 下一篇

猜你喜欢

热点阅读