• 项目实战之本地存储篇


    在前端项目开发中,前端的本地存储是必不可少的,今天小编就前端的本地存储在项目中的使用详细的介绍一下。前端本地存储主要有:

    • cookie
    • localStorage
    • sessionStorage
    • webSQL/indexDB

    接下来就这三种前端常用的存储方式进行介绍。

    cookie就是存储在客户端的一小段文本,大小不能超过4kb,在请求接口的时候,cookie会被请求携带着,进而被服务器所读取使用。
    打开浏览器控制台,F12>>Application>>Cookies,随便选择一个域,我们可以看到里面有很多cookie,如下所示:

    主要属性

    • Name

    cookie的名称

    • Value

    cookie的值,大小最大4Kb

    • Domain

    cookie存储的域名

    • Path

    cookie存储的路径

    • Size

    cookie的大小

    • Expires/Max-Age

    这两个属性涉及到 cookie 的存活时间

    Expires 属性指定一个具体的到期时间,到了这个指定的时间之后,浏览器就不再保留这个 cookie ,它的值是 UTC 格式,可以使用 Date.prototype.toUTCString() 格式进行转换。

    Max-Age 属性制定了从现在开始 cookie 存在的秒数,比如 60 * 60 (即一小时)。过了这个时间以后,浏览器就不再保留这个 Cookie。

    Max-Age的优先级比Expires高,如果两者都不设置,则这个cookie会在浏览器关闭的时候失效。

    • HttpOnly

    如果设置了该属性,意思就是这个cookie不能被JavaScript取到,也就防止了cookie被脚本读取,然后当发起请求的时候,该cookie才会被带上。

    • Secure

    指定浏览器只有在加密协议 HTTPS 下才能发送cookie,不需要设置,当协议是https时,会自动开启。

    使用方式

    • 设置cookie
    /**
     * 设置cookie
     * @param {*} key 名称
     * @param {*} val 值
     * @param {*} time 失效时间
     */
    
    export const setCookie = (key, val, time) => {
    
      var date = new Date();
    
      var expiresDays = time;
    
      //将时间转换为cookie设置时间的格式
    
      date.setTime(date.getTime() + expiresDays * 24 * 3600 * 1000);
    
      document.cookie = key + "=" + val + ";expires=" + date.toDateString();
    
    }
    
    • 获取cookie
    /**
     * 获取cookie
     * @param {*} key 名称
     */
    
    export const getCookie = (key) => {
    
      var getCookie = document.cookie.replace(/[ ]/g, "");
    
      var arrCookie = getCookie.split(";")
    
      var tips;
    
      for (var i = 0; i < arrCookie.length; i++) {
    
        var arr = arrCookie[i].split("=");
    
        if (key == arr[0]) {
    
          tips = arr[1];
    
          break;
    
        }
    
      }
    
      return tips;
    
    }
    
    • 删除cookie

    删除cookie的话,可以直接调用设置cookie的方法,将失效时间置为-1,如下:

    setCookie(key,'',-1)
    

    localStorage/sessionStorage

    localStorage/sessionStorage是在html5中新加入的技术,两者除了数据的时效性不一样之外,其他都一样。大小一般为5MB,存储的时候,仅仅在客户端存储,不会随着请求的调用而传递到服务器。

    localStorage不手动删除则永久有效,sessionStorage仅在当前会话有效

    下面F12>>Application>>Cookies看一个存储示例:

    可以看到,其只有两个属性,也就是key(名称)、value(值)

    使用方式

    localStorage和sessionStorage都具有相同的操作方法,例如setItem、getItem和removeItem等,为了使用方便,我们对其进行二次封装,示例如下:

    • 存储Storage
    /**
     * 存储Storage
     */
    export const setStore = (params = {}) => {
      let {
        name,//名称
        content,//内容
        type,//类型
      } = params;
      let obj = {
        dataType: typeof (content),
        content: content,
        type: type,
        datetime: new Date().getTime()
      }
      if (type) window.sessionStorage.setItem(name, JSON.stringify(obj));
      else window.localStorage.setItem(name, JSON.stringify(obj));
    }
    
    • 获取Storage
    /**
     * 判断是否为空
     */
    function validatenull (val) {
      if (typeof val === 'boolean') {
        return false
      }
      if (typeof val === 'number') {
        return false
      }
      if (val instanceof Array) {
        if (val.length == 0) return true
      } else if (val instanceof Object) {
        if (JSON.stringify(val) === '{}') return true
      } else {
        if (val == 'null' || val == null || val == 'undefined' || val == undefined || val == '') return true
        return false
      }
      return false
    }
    
    /**
     * 获取Storage
     */
    export const getStore = (params = {}) => {
      let {
        name,//名称
        debug//是否需要转换类型
      } = params;
      let obj = {},
        content;
      obj = window.sessionStorage.getItem(name);
      if (validatenull(obj)) obj = window.localStorage.getItem(name);
      if (validatenull(obj)) return;
      try {
        obj = JSON.parse(obj);
      } catch{
        return obj;
      }
      if (debug) {
        return obj;
      }
      if (obj.dataType == 'string') {
        content = obj.content;
      } else if (obj.dataType == 'number') {
        content = Number(obj.content);
      } else if (obj.dataType == 'boolean') {
        content = eval(obj.content);
      } else if (obj.dataType == 'object') {
        content = obj.content;
      }
      return content;
    }
    
    • 删除Storage
    /**
     * 删除localStorage
     */
    export const removeStore = (params = {}) => {
      let {
        name,
        type
      } = params;
      if (type) {
        window.sessionStorage.removeItem(name);
      } else {
        window.localStorage.removeItem(name);
      }
    
    }
    
    • 获取全部Storage
    /**
     * 获取全部Storage
     */
    export const getAllStore = (params = {}) => {
      let list = [];
      let {
        type
      } = params;
      if (type) {
        for (let i = 0; i <= window.sessionStorage.length; i++) {
          list.push({
            name: window.sessionStorage.key(i),
            content: getStore({
              name: window.sessionStorage.key(i),
              type: 'session'
            })
          })
        }
      } else {
        for (let i = 0; i <= window.localStorage.length; i++) {
          list.push({
            name: window.localStorage.key(i),
            content: getStore({
              name: window.localStorage.key(i),
            })
          })
    
        }
      }
      return list;
    
    }
    
    • 清空全部Storage
    /**
     * 清空全部Storage
     */
    export const clearStore = (params = {}) => {
      let { type } = params;
      if (type) {
        window.sessionStorage.clear();
      } else {
        window.localStorage.clear()
      }
    
    }
    

    cookie、localStorage、sessionStorage异同

    数据存储时间

    • cookie 可以自己设置失效时间
    • localStorage 不主动清除,则永久存储
    • sessionStorage 当前页面关闭时被删除

    大小

    • cookie 最大4kb
    • localStorage 最大5MB
    • sessionStorage 最大5MB

    请求是否携带

    • cookie 每次请求会携带在请求头中
    • localStorage 不携带,仅在客户端存储
    • sessionStorage 不携带,仅在客户端存储

    易用性

    • cookie 原生api使用不友好,需自己二次封装
    • localStorage 原生接口可使用,也可以自己二次封装
    • sessionStorage 原生接口可使用,也可以自己二次封装

    个人推荐在项目中使用storage存储,cookie存储数据过多,会造成性能问题。当然,大家可以根据实际情况进行选择,二次封装方法已双手奉上。

    webSQL/indexDB

    对于简单的数据存储,storage和cookie就已经够用了,但是如果需要存储比较复杂的关系型数据,再使用storage和cookie,就有点力不从心了。这个时候可以使用webSQL或者indexDB进行存储。

    webSQL

    Web SQL数据库API是一个独立的规范,在浏览器层面提供了本地对结构化数据的存储,已经被很多现代浏览器支持了。

    核心api
    • openDatabase() => 用来打开或创建数据库(没有时则创建,有则打开)
    • transaction() => 这个方法可以控制一个或多个事务,以及基于这种情况提交或者回滚
    • executeSql() => 用于执行实际的 SQL 查询
    判断浏览器是否支持该功能

    从上面的图中可以看出,webSQL兼容性并不是太好,因此使用时,我们需要先判读那浏览器是否支持。

    if (window.openDatabase) {
        // 操作 web SQL        
    } else {
         alert('当前浏览器不支持 webSQL !!!');
    }
    
    webSQL操作类封装

    webSQL操作类封装代码量较大,此处就不再展示,需要的小伙伴可以关注我公众号回复【webSQL操作类】获取。下面给个简单的例子:

    var myDB = {
        name: 'formData',
        version: 1,
        db: null,
    };
    myDB.db = openDatabase(myDB.name, myDB.version, 'test', 100 * 1024 * 1024);
    myDB.db.transaction(function(tx) {
        tx.executeSql('', [], function(tx, result) {
              if(result.rows.length!=0){
                  //result.rows.item(i)
              }
        }, function(tx, error) {
            console.log(error);
        });
    })
    

    常用的SQL语句:

    //新建表
    'CREATE TABLE IF NOT EXISTS 表名 (列名称1 PRIMARY KEY,列名称2 UNIQUE ,列名称3)'
    //删除表
    'DROP TABLE 表名'
    //清空表
    'DELETE FROM 表名'
    //删除条目
    'DELETE FROM 表名 WHERE 列名称1 = ? and 列名称2 = ?'
    //新增一条
    'INSERT INTO 表名 VALUES (?,?,?,?,?,?,?)' //为所有列添加值
    'INSERT INTO 表名 (列名称2,列名称4,列名称6) VALUES (?,?,?)' //为指定列添加值
    //批量增加
    insert into persons 
    (id_p, lastname , firstName, city )
    values
    (200,'haha' , 'deng' , 'shenzhen'),
    (201,'haha2' , 'deng' , 'GD'),
    (202,'haha3' , 'deng' , 'Beijing')
    //更新一条
    'UPDATE 表名 SET 列名称1 = ? where 列名称2 = ? AND 列名称3 = ?'
    'UPDATE 表名 SET 列名称1 = ?,列名称2 = ?,列名称3 = ? where 列名称2 = ? AND 列名称3 = ?'
     //根据主键存在与否,更新或添加一条数据
    'replace into 表名 (列名称1,列名称2,列名称3,列名称4,列名称5) VALUES (?,?,?,?,?) '
     //查找(更多查询请根据自己的需要自由组合)
    'select * from 表名 where 列名称1 = ? and 列名称1 >= ?' //常规查找
    'select * from 表名 where 列名称1 = ? or 列名称1 >= ?' //常规查找
    
    'select * from 表名 ORDER BY ?' //指定排序项
    'select * from 表名 ORDER BY ? LIMIT 2;'//只查找符合条件的2条
    
    WHERE 列名称 IS NOT NULL //非空
    WHERE 列名称 LIKE "111%" //111开头的
    WHERE 列名称 LIKE "%111" //111结尾的
    WHERE 列名称 LIKE "%111%" //包含111的
    WHERE 列名称 NOT LIKE "%111%" //不包含111的
    '_a_'    //三位且中间字母是a的
    '_a'    //两位且结尾字母是a的
    'a_'    //两位且开头字母是a的
    
    WHERE 列名称 GLOB > 111 //大于111
    WHERE 列名称 GLOB >= 111 //大于等于111
    WHERE 列名称 GLOB != 111 //不等于111 
    
    WHERE 列名称 GLOB '111*' //111开头的
    WHERE 列名称 IN ( 25, 27 )  //值为25或27的
    WHERE 列名称 NOT IN ( 25, 27 )  //值不为25或27的
    WHERE 列名称 BETWEEN 25 AND 27  //值在25到27之间的
    WHERE 列名称 IN ( '25', '27' )  //注意:拼接sql时不要忘记引号
    
    //索引
    'CREATE INDEX IF NOT EXISTS 索引名 on 表名 (列名称1, 列名称2) '
    'DROP INDEX 索引名'
    

    indexDB

    IndexedDB标准是HTML5官方认可的本地数据库解决方案。其目的不是取代服务器端数据库,它在一些特定场景下很有用,比如离线应用。IndexedDB是一种轻量级NOSQL数据库,是由浏览器自带。相比Web Sql更加高效,包括索引、事务处理和查询功能。

    从上图可以看出indexDB的兼容性还是不错的。

    使用indexDB
    创建/打开一个数据库

    首先我们需要创建或者打开一个数据库对象,可以使用window.indexedDB.open()方法,示例如下:

    var openRequest =window.indexedDB.open(name, version);
    var db;
    openRequest.onupgradeneeded = function(e) {
      console.log("Upgrading...");}
    openRequest.onsuccess = function(e) {
      console.log("Success!");
      db = e.target.result;
    }
    openRequest.onerror = function(e) {
      console.log("Error");
      console.dir(e);
    }
    

    第一次打开数据库时,会先触发upgradeneeded事件,然后触发success事件

    open方法返回的是一个对象(IDBOpenDBRequest),回调函数定义在这个对象上面

    回调函数接受一个事件对象event作为参数,它的target.result属性就指向打开的IndexedDB数据库

    创建一个存放数据的“对象仓库”

    数据库对象有了,我们还需要创建一个存放数据的“对象仓库”,示例如下:

    db.createObjectStore("test", { keyPath: "email" });
    
    db.createObjectStore("test2", { autoIncrement: true });
    

    keyPath表示的是存储数据的键名,autoIncrement表示是否使用自动递增的整数作为键名。一般来说,两个属性有一个就可以了。

    创建一个数据库事务对象

    transaction方法用于创建一个数据库事务。向数据库添加数据之前,必须先创建数据库务。

    transaction方法返回一个事务对象,该对象的objectStore方法用于获取指定的对象仓库。

    var transaction = db.transaction(["firstOS"],"readwrite");
    
    var store = transaction.objectStore("firstOS");
    

    transaction方法接受两个参数:

    第一个参数是一个数组,里面是所涉及的对象仓库,通常是只有一个;

    第二个参数是一个表示操作类型的字符串。readonly(只读)和readwrite(读写);

    transaction方法有三个事件,可以用来定义回调函数。

    abort: 事务中断; complete: 事务完成; error: 事务出错。

    transaction.oncomplete = function(event) {
        // some code
    };
    
    操作数据

    transaction对象提供了一些api,供我们操作数据。

    • 添加数据 add()

    获取对象仓库以后,就可以用add方法往里面添加数据了,示例如下:

    var transaction = db.transaction(["firstOS"],"readwrite");
    var store = transaction.objectStore(“firstOS”);
    var data = {name: 'monkey'};
    var request = store.add(data,1);
    request.onerror = function(e) {
       console.log("Error",[e.target.error.name](http://e.target.error.name));
    }
    request.onsuccess = function(e) {
      console.log("数据添加成功!");
    }
    

    add方法的第一个参数是所要添加的数据,第二个参数是这条数据对应的键名(key),上面代码将对象o的键名设为1。如果在创建数据仓库时,对键名做了设置,这里也可以不指定键名。

    • 更新数据 put()
    var data = { name: 'monkeysoft' };
    var request = store.put(data);
    
    • 读取数据 get()
    var request = store.get(key);
    
    • 删除数据 delete()
    var request = store.delete(key);
    
    • 清空数据库 clear()
    var request = store.clear();
    
    • 遍历数据 openCursor()
    var request = store.openCursor();
    
    indexDB操作类封装

    indexDB操作类封装代码量较大,此处就不再展示,需要的小伙伴可以关注我公众号回复【indexDB操作类】获取

    以上就是我对前端本地存储的一些理解和整理,如有错,欢迎各位大佬指正,免得误人子弟~嘿嘿。

    参考:
    1、indexDB讲解与封装 https://www.jianshu.com/p/136c268b8559

  • 相关阅读:
    接口测试
    JMeter 插件管理
    JMeter IP欺骗压测
    Maven初窥门径
    都是分号惹的祸 ORA-00911
    插拔式设计思想
    第七章、Ajango自带auth模块
    第七章、中间件续写
    第七章、中间件
    第六章、Cookies和Session
  • 原文地址:https://www.cnblogs.com/monkeySoft/p/13359863.html
Copyright © 2020-2023  润新知