Web 存储技术

1、背景介绍

第一个Web存储的技术叫作Cookie,它是网站的身份证。是网站为了辨别用户身份,进行session(服务端的session)跟踪而存储在用户本地终端上的数据,也就是说它是存在电脑硬盘上的,一个很小的txt类型的文件。Cookie每次都会跟随http请求发送到服务端,也就是说每个http请求都会带上咱们的cookie数据,所以它存在一个安全性的问题。html

cookie自己也是有很大的局限性的,首先它很小,主流的浏览器最大支持 4096 字节,除了最大字节的限制,每一个网站的cookie个数(也就是每个first每个域)也是有限制的,通常浏览器是20个。除此以外,cookie还会默认跟随全部http请求发送,即便不须要使用这个cookie来鉴别用户可是它也是会跟随http请求发送的,这样就会形成一个网络资源的浪费。而后部分的浏览器还限制了总的cookie个数300个。web

在cookie的诸多局限性下,Web Storage应运而生。Web Storage 解决了不少问题:数据库

好比它支持存储大量数据,支持复杂的本地数据库,并且也不会默认跟随http请求。Web Storage主要是有四个:数组

  • SessionStorage
  • LocalStorage
  • WebSQL
  • indexedDB

2、Cookie的简单介绍

Cookie是HTML4的一个标准,它通常不须要考虑兼容。它是网站的一个身份证,服务器能够针对不一样用户,作出不一样的响应。cookie存储在用户的机器上是一个纯文本,就是一个txt文件并非一个脚本,它不能执行东西只负责记录。浏览器每次请求都会带上当前网站的cookie。浏览器

Cookie分为两种类型,一种呢是会话cookie,也就是临时性的cookie,退出浏览器或者是关闭即删除;安全

另外一种叫持久cookie,它会一直存在,存在的时间由特定的过时时间或者是有效期来决定。服务器

Cookie的域 Domain决定了当前的一个cookie的权限,哪个域可使用这个cookie。cookie

Cookie的路径 Path,下面一个简单的例子:网络

www.baidu.com                id="123456" domain="www.baidu.com"
www.baidu.com/user           id="123456" user="eric" domain="www.baidu.com" path="/user/"

www.baidu.com/search         id="123456";
www.baidu.com/user/search    id="123456" user="eric";

如上www.baidu.com设置了一个id等于123456,domain是 www.baidu.com,而后另一个跟第一个同样多设置了一个user,id相同,可是多了一个user=“Eric”,它的domain设置成了 www.baidu.com,path就到了user下面。这二者设置完成以后,当咱们访问 www.baidu.com/search 时百度只能拿到id,由于user="Eric"是属于user这个域下面的,也就是说在search下面是获取不到的,可是在 www.baidu.com/user/search 这个时候咱们就能够获取到名叫Eric的user。Path也是一种权限的控制只是相较于域domain是低一级的。session

Cookie的安全secure,若是这个属性为TRUE,那么网站只有在https的请求下面才会携带当前的cookie。

Cookie的HttpOnly这个属性若是为TRUE,那么就不容许JavaScript操做cookie。

由于cookie是存储在客户端一个独立的文件,所以服务器是没法分辨用户和攻击者的。关于cookie的目的分为两种:一种是跨站点脚本攻击,一种是跨站请求伪造。

3、SessionStorage

key-value的键值对,是HTML5新增的一个会话存储对象。

SessionStorage是临时保存在同一窗口,也就是同一标签页的数据。若是当前标签页关闭了,那么SessionStorage也就失效了。这也是SessionStorage最显著的一个特色:单页标签限制。

除此以外,它还有的一些特色有:

  • 同源策略,也就是在同一协议,同一主机名和同一端口下的同一tab
  • 只在本地存储,不会跟随http请求发送到服务器
  • 存储方式采用key-value键值对,这里面的value只能存字符串类型,若是存其余的会自动转换成字符串。
  • 存储上线限制达到了5MB,若是当前存储超出上限新的内容会把旧的内容覆盖但不会报错。

属性

  • sessionStorage.length - 键值对数量
  • sessionStorage.key(int index) -> null
  • sessionStorage.getItem(string key) -> null
  • sessionStorage[string key]
  • sessionStorage.setItem(string key, string value)
  • sessionStorage.removeItem(string key)
  • sessionStorage.clear()

Json对象

  • JSON.stringify()
  • JSON.parse()

4、LocalStorage

LocalStorage也是在浏览器的Application下面有一个Local Storage,它和SessionStorage是十分类似的,一样是key-value键值对,也是HTML5的新增存储对象,它与SessionStorage的特色不一样之处在于没有标签页的限制和在浏览器的无痕模式下LocalStorage是不容许读取的,永久性的存储,而后SessionStorage超出限制是覆盖不会报错而LocalStorage超出会报错。

特色

  • 同源策略,也就是在同一协议,同一主机名和同一端口下的同一tab
  • 没有标签页的限制
  • 只在本地存储,不会跟随http请求发送到服务器
  • 存储方式采用key-value键值对,这里面的value只能存字符串类型,若是存其余的会自动转换成字符串。
  • 存储上线限制达到了5MB,若是当前存储超出上限会报错。
  • 无痕模式下不可读取
  • 永久性存储

属性

  • sessionStorage.length - 键值对数量
  • sessionStorage.key(int index) -> null
  • sessionStorage.getItem(string key) -> null
  • sessionStorage[string key]
  • sessionStorage.setItem(string key, string value)
  • sessionStorage.removeItem(string key)
  • sessionStorage.clear()

注意事项:LocalStorage和SessionStorage在web view是不可靠的,web view指的是在开发混合APP的时候使用了浏览器来实现咱们的APP,这个时候是不可靠的,由于在浏览器崩溃的状况下数据可能没有存进去。

另一个在IOS浏览器中不可重复setItem,若是重复会报错,而后这个时候咱们须要先removeItem再添加item。

监听storage的变化

监听storage包括SessionStorage和LocalStorage。而后这里须要提到两个概念:同源和监听同源网页。

  • 同源:协议、域名、端口三者相同,同源的状况下咱们能够共享SessionStorage和LocalStorage。 同源策略还禁止不一样源执行任何脚本。
    http://localhost:63342/simpleApp/app/index.html#/
        (协议)   (域名)   (端口)         (路径)
  • 监听同源网页,可是同一网页是无效的
    window.addEventListener("storage", function (event) {
            console.log(event.key);
            console.log(event.oldValue);
            console.log(event.newValue);
            console.log(event.url);
            console.log(event.storageArea);
        });

5、IndexedDB

IndexedDB 背景

  • Storage(Storage指的是SessionStorage和LocalStorage)不适合存储大量的数据
  • Storage不能提供搜索功能
  • Storage不能创建索引,存储的内容也比较少
  • IndexedDB扩大了web存储的容量,能够达到250MB以上

基本概念

首先它是一个NoSQL,也就是一个非关系型数据库。MySQL和Oracle都是关系型数据库。意思就是说若是创建了两个表在关系型数据库里面咱们能够经过一个外链把多个表联系起来,可是NoSQL不行,在NoSQL里面若是想要多个表关联起来,咱们只能手动的在关联的表里面添加上须要关联的另一个或多个表的id。这是NoSQL与MySQL二者之间的一个区别。

IndexedDB的特色也是和Storage是同样的:

  • 键值对储存 ,可是容许全部类型,不容许主键重复报错
  • 是一个异步操做, 不阻塞浏览器线程
  • 支持事务,事务是SQL数据库的一个概念,也就是说咱们进行任何的增删改查都要在某一个事务下面进行,提供了一个回滚功能,一系列操做有一步失败, 数据库回滚到事务发生以前的状态,这样为了不操做中途出现失败,影响整个数据库的状态
  • 同源限制
  • 支持二进制储存

IndexedDB几个基本概念:

  • IDBDatabase - 数据库
  • IDBObjectStore - 对象仓库
  • IDBIndex - 索引
  • IDBTransaction - 事务
  • IDBRequest - 操做请求
  • IDBCursor - 指针
  • IDBKeyRange - 主键集合

IndexedDB浏览器兼容

IDBDatabase

IDB是IndexedDB的缩写,它呢就是数据库,数据库也叫做数据的一个容器。每个源(同源策略)能够创建不少数据库。Database有一个版本的概念,版本对应着数据库表,同一时刻只能存在一个版本。好比:新增一个表,而后咱们须要把database的版本加一,表里面要新增一列,这时一样须要把数据库版本加一。

注意:1 同一时刻只能有一个版本存在 2 修改数据库结构只能经过升级数据库版本

  • 打开数据库
    /* databaseName不存在则建立 */
     /* version为整数, 新建时为1 */
    
     let database;
     let userStore;
     const request = window.indexedDB.open(databaseName, version);
    
     /* 成功打开数据库 */
     request.onsuccess = event => {
           database = request.result;
     }
    
     /* 打开数据库失败 */
     request.onerror = error => {
           console.log(error);
     }
    
     /* 版本号大于当前数据库版本 */
     request.onupgradeneeded = event => {
           database = event.target.result;
     }

注意:若是在打开数据库时,数据库不存在,将会新建一个

IDBObjectStore(数据库表)

建立表,最好是在upgradeneeded下执行;在建立数据库表的时候须要指定主键,主键表明了惟一的标识,好比 keyPath:‘id’;若是不指定主键,咱们能够指定一个autoIncrement:true,自增的一个概念,也就是不指定主键数据库会自动添加主键并且这个主键就是数字,依次递增的。

const createStore = () => {
 	//若是当前的objectStoreNames.contains包含user,若是不包含user这个表,而后就用这个database.createObjectStore建立了一个表,这个表的名字就叫作user,而后主键就是下面的id
       if(!db.objectStoreNames.contains('user')) {
           userStore = database.createObjectStore('user', { keyPath: 'id' });
       }
 }

指定索引:

const createStore = () => {
       if(!database.objectStoreNames.contains('user')) {
         userStore = database.createObjectStore('user', { keyPath: 'id' });
         userStore.createIndex('name', 'name', { unique: true });
     }
 }

IDBTransaction(事务)

建立完以后须要往里面添加数据,添加数据咱们就须要使用到事务。

事务涉及到数据库的增删改查,它有三个状态:

  • complete
  • error
  • abort

属性:

  • IDBTransaction.db 当前数据库
  • IDBTransaction.mode 模式,使用模式分为readonly和readwrite
  • IDBTransaction.objectStoreNames 当前数据库涉及到的哪几个数组表
  • IDBTransaction.error 回调

数据库的基本操做:增删改查以及清空。

新增数据(add)

分为两种状况:一种是使用自增的数据库的id或者是自增的一个键值,若是已经建立主键,那么新增必须包含主键和另外一种已建立主键但主键不可重复。

const add = () => {
     /* 建立事务 */
     /* 使用某个数据库 */
     /* add新增 */
     transactionRequest = database.transaction(['user'], 'readwrite')
         .objectStore('user')
         .add({ id: 100, name: 'Eric', age: 28, email: 'Ericlee00@163.com' });
 
     /* 成功 */
     transactionRequest.onsuccess = event => {
         console.log('数据写入成功', event);
     };
 
     /* 失败 */
     transactionRequest.onerror = error => {
         console.log('数据写入失败', error);
     }
 }

读取数据(get)

const read = () => {
       /* 建立事务 */
       transaction = database.transaction(['user']);
       /* 选择数据库表 */
       table = transaction.objectStore('user');
       /* 读取数据 */
     transactionRequest = table.get(2);
 
     /* 成功 */
     transactionRequest.onerror = event => {
           console.log('数据读取失败', event);
 	};
 
     /* 失败 */
       transactionRequest.onsuccess = event => {
           if (transactionRequest.result) {
               console.log('数据读取成功', transactionRequest.result);
           } else {
               console.log('未读取到数据');
           }
       };
 }

更新数据(put)

更新不存在的数据时会新建,也就是说在新增数据时若是相同,每每会出错,可是在更新数据时不会出错。若是数据不存在就会新建,若是存在就会一直更新。

const update = () => {
       transactionRequest = database.transaction(['user'], 'readwrite')
           .objectStore('user')
           .put({ id: count, name: 'David', age: 35, email: 'David@xiakedao.com' });
 
       transactionRequest.onsuccess = function (event) {
           console.log('更新数据成功', event);
       };
 
       transactionRequest.onerror = error => {
           console.log('更新数据失败', error);
       }
   }

删除数据(delete)

const delete = () => {
       transactionRequest = database.transaction(['user'], 'readwrite')
           .objectStore('user')
           .delete(2);
 
       transactionRequest.onsuccess = function (event) {
           console.log('删除数据成功', event);
       };
 
       transactionRequest.onerror = error => {
           console.log('删除数据失败', error);
       }
   }

清空数据(clear)

IDBCursor(指针)

提供了一种遍历数据的可能。

const readAll = () => {
       table = database.transaction('user').objectStore('user');
 
       table.openCursor().onsuccess = () => {
           let cursor = event.target.result;
 
           if (cursor) {
               console.log('数据遍历', cursor);
           cursor.continue();
           } else {
               console.log('数据遍历完成');
           }
       };
   }

关闭IndexedDB数据库链接

const closeDataBase = () => {
      database.close();
  }

删除IndexedDB数据库前,须先关闭数据库链接

const deleteDataBase = () => {
      indexedDB.deleteDatabase('first_database');
  }

6、WebSQL

基本概念:并非 HTML5 的规范 , 只能算是一个独立的规范;使用WebSQL是完彻底全的SQL 语句,使用SQL语句来操做客户端数据库;它一共有三个比较重要的概念,分别是:openDatabase 打开数据库,能够是使用现有数据库或者新建数据库;transaction 事务,全部的数据库都支持事务;executeSql 执行SQL语句。

openDatabase(打开数据库)

相比于IndexedDB的概念稍微多一点,主要是有数据库名称、版本号(在IndexedDB里面版本号都是整数,可是在WebSQL里面它能够是小数)、描述文本(介绍数据库是干什么的)、数据库大小和建立回调(function,只在第一次建立的时候才会调用)。

const database = openDatabase('my_database', '1.0', 'first', 2 * 1024 * 1024, function() {
 
 });

Transaction(事务)

  • 建立表
    const createTable = () => {
              database.transaction(function (content) {  
                  content.executeSql('CREATE TABLE IF NOT EXISTS USER (id unique, name)');
              });
        }
  • 添加数据
    const addData = () => {
              database.transaction(function (content) {  
                  content.executeSql('INSERT INTO USER (id, name) VALUES (1, "Eric")');
              });
        }
  • 查询数据
    const searchData = () => {
              database.transaction(function (content) {  
                  content.executeSql('SELECT * FROM USER');
              });
        }
  • 更新数据
    const updateData = () => {
              database.transaction(function (content) {  
                  content.executeSql('UPDATE USER SET name=\'David\' WHERE id=1');
              });
        }
  • 删除数据库表
    const deleteDataBase = () => {
              database.transaction(function (content) {  
                  content.executeSql('DROP TABLE USER');
              });
        }