前言

最近在做一个有点奇特的项目,是帮某个旅游镇开发一个管理系统,比如管理摄像头、酒店等信息。奇特在于这个镇没有他们自己的信息系统,也就说每次信息更新都得是镇上的工作人员去收集一遍,然后发给我们去导入。

这就表示我们做的管理系统差不多是一周更新一次信息,而且数据格式比较乱。由于这两个原因,后端开发希望前端这边对数据做解析,比如做一个酒店报表,将所有订单数据做一个图表展示。特别是现在疫情的原因,要展示旅客们都来自哪里。所以后端给我原始的数据,前端进行格式化和归类展示。

另外我也觉得把原始数据给到前端这边,前端处理起来比较灵活,想怎么展示就怎么展示;而且数据更新频率低,不用每次刷新就重新请求接口;后期客户那边有什么改动的地方也不用新增接口。

但是新的问题来了,如果数据放在前端管理,那么如果高效地存储和使用这些数据?最起码不会导致页面卡顿。

如何存储与管理数据

cookie与localStorage

首先想到的是cookielocalStorage,因为这两个比较常用。

cookie的数据量比较小,浏览器普遍限制最大的只能是4k。不合适,pass。

localStoragesessionStorage适合与小数据量的存储,FirefoxChorme限制最大的存储为5MB。另外localStorage是以字符串的方式存储的,存之前要先用JSON.stringify将原始数据变成字符串,用的时候需要用JSON.parse将其恢复成相应的格式。

综上:localStorage只适合与比较简单的数据存储与管理。

而后端给出的数据一般都比较多,有成百上千的。而且需要复杂的查询。如果管理JSON数据就会比较麻烦。所以localStorage也不合适,pass。

WebSQL

只能将目光转向自己没有用过,只有在看面试题中看到过的概念:WebSQL

WebSQL是前端的数据库,它的API并不是HTML5规范的一部分,但它是一个独立的规范。它是基于SQLite实现的。SQLite是一个轻量级的数据库,它占用的空间小。它支持表的创建、插入、修改和删除,但是不支持修改表结构。同一个域可以创建多个DB,每个DB有若干张表。

WebSql有三个核心的方法:

  • openDatabase: 使用现有的数据库或者新建一个数据库对象。
  • transaction: 能够控制一个事务,以及基于这种情况执行提交或者回滚。
  • executeSql:用于执行实际的SQL查询。

1、创建数据库
使用openDatabase()方法来新建一个数据库。

var db = window.openDatabase(
    'test-db', // 数据库名称
    '1.0', // 版本号
    'Test DB', // 描述文本
    '5 * 1024 * 1024' // 设定数据库大小
)
1
2
3
4
5
6

2、创建表
使用transaction()方法。

  • 首先传一个回调函数给transaction方法,它会传一个SQLTransaction实例作为参数,表示一个事务。
  • 然后调用executeSql()方法,该方法接收四个参数
    (1):要执行的SQL语句。
    (2):选项。
    (3):执行成功后的回调函数。
    (4):执行失败后的回调函数。
db.transaction(function (tx) {
    tx.executeSql(
        "create table if not exists testTable(test_id integer primary key, text_hotel, lat, lng, price, checkin_time)", [], function (tx) {
            console.log('success to create a table')
        }
    )
})
1
2
3
4
5
6
7

执行的SQL语句为:

create table if not exists test_data(test_id integer primary key, test_hotel, lat, lng, price, checkin_time, comeFrom)
1

意思是创建一张test_data表,字段有7个,其中:

  • primary key的意思主键。主键用于标志这一行,并且不能有重复的值。
  • integer的意思是可以自动生成数值。

3、插入数据

var order = {
    test_hotel: 'XXX酒店',
    lat: 23.7345343,
    lng: 123.9843455,
    price: 235,
    checkin_time: 2020-04-25,
    comeFrom: '广州'
}

// 插入
tx.executeSql(`insert into testTable (test_hotel, lat, lng, price, checkin_time, comeFrom) values(${order.test_hotel}, ${order.lat}, ${order.lng}, ${order.price}, ${order.checkin_time}, ${order.comeFrom})`);
1
2
3
4
5
6
7
8
9
10
11

在控制台中可以看到

4、查询

  • 城市的游客量
select comeFrom as city, count(test_id) as count from testTable group by comeFrom order by coutn
1

结果如图所示:

WebSQL是关系型数据库,关系型数据库的优缺点我就不去介绍了。只说一下WebSQL的兼容性。

can i use (opens new window)中展示的兼容性来看,并不怎么理想,FirefoxIESafari都不支持。

IndexedDB。

IndexedDB是一个基于JavaScript的面向对象数据库。

IndexedDB有以下特点:

  • 键值对储存。
  • 异步。
  • 支持事务。
  • 同源限制。
  • 储存空间大。
  • 支持二进制储存。

IndexedDBAPIWebSQL要复杂的多。

流程如下:
1、打开数据库
使用indexedDB.open()方法

var instance_db = window.indexedDB.open('test_db', 1);
// 注册失败函数
instance_db.onerror = e => { console.log(e) };
// 注册成功函数
var db = null
// 通过instance_db对象的result属性拿到数据库对象
instance_db.onsuccess = e => { db = instance_db.result; }
1
2
3
4
5
6
7

该方法接收两个参数,第一个参数是字符串,表示数据库的名字。第二个参数是整数,表示数据库的版本。

2、新建数据库
新建数据库与打开数据库是用一个方法。如果指定的数据库不存在,就会新建。不同在于,后续的操作主要在upgradeneeded事件的监听函数里面完成的。

instance_db.onupgradeneened = (e) => {
    db = e.target.result;
    // 新增一个名为test的表,主键是test_id
    var testTable = db.createObjectStore('test', { keyPath: 'test_id' });
    // 新建索引。IDBObject.createIndex()的三个参数分别为索引名称、索引所在的属性、配置对象(说明该属性是否包含重复的值)。
    testTable.createIndex('hotel', 'hotel', { unique: true });
    testTable.createIndex('date', 'date', { unique: true });
}
1
2
3
4
5
6
7
8

3、新增数据
通过事务来完成

var request = db.transaction(['test'], 'readwrite').objectStore('test')
                .add({test_id: 1, hotel: '四季酒店', date: '2020-04-25'});
request.onsuccess = (e) => {};
request.onerror = (e) => {};
1
2
3
4

写入数据需要新建一个事务。新建时必须指定表格名称和操作模式("只读"或"读写")。新建事务以后,通过IDBTransaction.objectStore(name)方法,拿到IDBObjectStore对象,再通过表格对象的add()方法,向表格写入一条记录。

写入操作是一个异步操作,通过监听连接对象的success事件和error事件,了解是否写入成功。

4、读取数据
objectStore.get()方法用于读取数据,参数是主键的值。

var transaction = db.transaction(['test']);
var objectStore = transaction.objectStore('test');
var request = objectStore.get(1);

request.onerror = (e) => {};

request.onsuccess = (e) => {
  if (request.result) {
    console.log('hotel: ' + request.result.hotel);
  } else {
    console.log('未获得数据记录');
  }
};
1
2
3
4
5
6
7
8
9
10
11
12
13

5、更新数据
更新数据要使用IDBObject.put()方法。

var request = db.transaction(['test'], 'readwrite')
                .objectStore('test')
                .put({ test_id: 1, hotel: xxx酒店});

request.onsuccess = (e) => {
    console.log('数据更新成功');
};

request.onerror = (e) => {
    console.log('数据更新失败');
}
1
2
3
4
5
6
7
8
9
10
11

6、删除数据
IDBObjectStore.delete()方法用于删除记录。

var request = db.transaction(['test'], 'readwrite')
                .objectStore('test')
                .delete(1);

request.onsuccess = (e) => {
    console.log('数据删除成功');
};
1
2
3
4
5
6
7

最后来看一下IndexedDB的兼容性。

不用多说,就它了!