1. 程式人生 > >cookie與web Storage

cookie與web Storage

doc 鍵值 cat 也有 init 指定 cati 根目錄 repl

一、cookie

1. http頭與cookie

cookie是HTTP Cookie的簡稱。該標準要求:

(1)服務器的HTTP響應頭包含 Set-Cookie字段

響應頭Eg:

HTTP/1.1 200 OK
Content-Type: text/html
Set-Cookie: name=value

該HTTP響應設置了一個名為name,值為value的cookie。服務器將它們發送到瀏覽器,瀏覽器就會存儲這樣的cookie信息。

(2)瀏覽器的HTTP請求頭包含Cookie字段

請求頭Eg:

GET /index.html HTTP/1.1
Cookie: name=value

瀏覽器會為每個請求的請求頭添加Cookie字段,將瀏覽器本地包含的cookie信息發送回服務器。

2. cookie的特點

(1) cookie是綁定在特定域名下的

在一個域名下設置了某cookie後,再給該域名發送請求的話都會發送該cookie。

(2)單個域下的cookie數量、大小有限制

cookie數量限制:
  • IE7+:最多50個
  • Firefox:最多50個
  • Opera:最多30個
  • Chrome和Safari:無限制

cookie超出數量限制後,瀏覽器會清除以前設置的cookie。具體刪除方式不同瀏覽器不同。

cookie尺寸限制

大多數瀏覽器的單個域下所有cookie的總長度限制在 4096B左右。為了瀏覽器兼容性最好將其限制為<= 4095B

(3)如果沒設置失效時間,cookie在瀏覽器關閉時就會被刪除

4. API:document.cookie

(1)獲取cookie

var allCookies = document.cookie;
//name1=value1;name2=value2;name3=value3

可以獲取當前頁面可獲取的所有cookie,每條cookie以分號分隔。獲取cookie鍵值對後,需要使用decodeURIComponent() 來對每個 cookie名和cookie值解碼

(2)設置cookie

document.cookie = encodeURIComponent(name)+ ‘=‘ + encodeURIComponent(value) + ‘;path=somepath;domain=somedomain;max-age=somemaxage;expires=someexpires;secure‘;
  • 使用上述方法會設置新的cookie字符串。這個cookie字符串會被解釋並添加到現有的cookie集合中。並不會覆蓋現有的cookie

  • 只有name和value是必須的。最好設置時使用encodeURIComponent()對name和value進行編碼:

  • 有以下可選的cookie屬性可以跟在名值對之後:

    • domain=: 指定cookie對哪個域是有效的,所有該域下的頁面都能訪問該cookie,所有向該域發送的請求中都會發送該cookie。默認為設置cookie的域

    • path=: 指定cookie對於哪個路徑是有效的。如‘/mydir‘, 即可以指定該cookie只有從指定域名下的‘/mydir‘路徑才能訪問。那麽 域下的其他路徑及根目錄就不能訪問該cookie,也不會向服務器發送該cookie。默認當前文檔位置的路徑

    • max-age=:指定cookie有效時間,以s為單位。正常情況下,max-age的優先級高於expires

    • expires=:指定cookie失效時間,為GMT格式。如: (new Date()).toUTCString()

    • secure:安全標誌,指定後,cookie只能在使用SSL連接(HTTPS協議)的時候才能發送到服務器。

註意: 這些屬性都只存在於瀏覽器,並不會作為cookie信息的一部分發送到服務器,發送只發送名值對,即 請求頭中Cookie字段只包含名值對;但是這些屬性會從服務器發送到瀏覽器,用於給瀏覽器指示,即 s響應頭中Set-Cookie字段不僅包含名值對,也包含這些cookie屬性

5. 自定義cookie方法

mdn的框架:


/*
|*|  * docCookies.setItem(name, value[, end[, path[, domain[, secure]]]])
|*|  * docCookies.getItem(name)
|*|  * docCookies.removeItem(name[, path], domain)
|*|  * docCookies.hasItem(name)
|*|  * docCookies.keys()
|*|
*/

var docCookies = {
  getItem: function (sKey) {
    return decodeURIComponent(document.cookie.replace(new RegExp("(?:(?:^|.*;)\\s*" + encodeURIComponent(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=\\s*([^;]*).*$)|^.*$"), "$1")) || null;
  },
  setItem: function (sKey, sValue, vEnd, sPath, sDomain, bSecure) {
    if (!sKey || /^(?:expires|max\-age|path|domain|secure)$/i.test(sKey)) { return false; }
    var sExpires = "";
    if (vEnd) {
      switch (vEnd.constructor) {
        case Number:
          sExpires = vEnd === Infinity ? "; expires=Fri, 31 Dec 9999 23:59:59 GMT" : "; max-age=" + vEnd;
          break;
        case String:
          sExpires = "; expires=" + vEnd;
          break;
        case Date:
          sExpires = "; expires=" + vEnd.toUTCString();
          break;
      }
    }
    document.cookie = encodeURIComponent(sKey) + "=" + encodeURIComponent(sValue) + sExpires + (sDomain ? "; domain=" + sDomain : "") + (sPath ? "; path=" + sPath : "") + (bSecure ? "; secure" : "");
    return true;
  },
  removeItem: function (sKey, sPath, sDomain) {
    if (!sKey || !this.hasItem(sKey)) { return false; }
    document.cookie = encodeURIComponent(sKey) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT" + ( sDomain ? "; domain=" + sDomain : "") + ( sPath ? "; path=" + sPath : "");
    return true;
  },
  hasItem: function (sKey) {
    return (new RegExp("(?:^|;\\s*)" + encodeURIComponent(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=")).test(document.cookie);
  },
  keys: /* optional method: you can safely remove it! */ function () {
    var aKeys = document.cookie.replace(/((?:^|\s*;)[^\=]+)(?=;|$)|^\s*|\s*(?:\=[^;]*)?(?:\1|$)/g, "").split(/\s*(?:\=[^;]*)?;\s*/);
    for (var nIdx = 0; nIdx < aKeys.length; nIdx++) { aKeys[nIdx] = decodeURIComponent(aKeys[nIdx]); }
    return aKeys;
  }
};

《JavaScript高級程序設計》的框架:

const CookieUtil = {
  get: function(name) {
    const cookieName = encodeURIComponent(name) + ‘=‘;
    const cookieStart = document.cookie.indexOf(cookieName);

    if(cookieStart >= 0) {
      let cookieEnd = document.cookie.indexOf(‘;‘, cookieStart);
      if(cookieEnd === -1) {
        cookieEnd = document.cookie.length;
      }

      const cookieValue = decodeURIComponent(document.cookie.subString(cookieStart + cookieName.length, cookieEnd));
      return cookieValue
    } else {
      return null;
    }
  },

  set: function(name, value, expires, path, domain, secure) {
    const cookieText = encodeURIComponent(name) + ‘=‘ + encodeURIComponent(value);

    if (expires instanceof Date) {
      cookieText += `; expires=${expires.toUTCString()}`;
    }

    if (path) {
      cookieText += `; path=${path}`;
    }

    if (domain) {
      cookieText += `; domain=${domain}`;
    }

    if (secure) {
      cookieText += `; secure`
    }

    document.cookie = cookieText;
  },

  unset: function(name, path, domain, secure) {
    this.set(name, ‘‘, new Date(0), domain, secure)
  }
}

FTC框架

function getCookie(name) {
  const cookieStr = document.cookie;
  const nameLen = name.length;
  const nameStartIndex = cookieStr.indexOf(name+‘=‘);
  if (nameStartIndex < 0) {
    return null;
  }
  const valueStartIndex = nameStartIndex + nameLen + 1;
  let valueEndIndex = cookieStr.indexOf(‘;‘, valueStartIndex);

  if( !valueStartIndex && name !== cookieStr.substring(0, nameLen)) {////當startIndex為0的時候說明該name是第一個cookie,那name必須就是cookieStr.substring(0, name.length)
    return null;
  }
  
  if (valueEndIndex === -1) { //說明它就是最後一個cookie,後面沒有;
    valueEndIndex = cookieStr.length;
  }
  return decodeURIComponent(cookieStr.substring(valueStartIndex, valueEndIndex));
}

function SetCookie (name, value , sec , path , domain) {  
  var argv = SetCookie.arguments,
      argc = SetCookie.arguments.length,
      expires = new Date(),
      secure = (argc > 5) ? argv[5] : false;
  path = (argc > 3) ? argv[3] : null;
  domain = (argc > 4) ? argv[4] : null;
 if(sec === null || sec === ‘‘) {sec = 600 * (24 * 60 * 60 * 1000);}
  else {sec = 1000*sec;}
  expires.setTime (expires.getTime() + sec);
  document.cookie = name + ‘=‘ + escape (value) +((expires === null) ? ‘‘ : (‘; expires=‘ + expires.toGMTString())) +((path === null) ? ‘/‘ : (‘; path=‘ + path)) +((domain === null) ? ‘‘ : (‘; domain=‘ + domain)) +((secure === true) ? ‘; secure‘ : ‘‘);  
}

function DeleteCookie (name) {  
  var exp = new Date(),cval = GetCookie (name);
  exp.setTime (exp.getTime() - 1);
  document.cookie = name + ‘=‘ + cval + ‘; expires=‘ + exp.toGMTString();
}

6. *子cookie

存在意義

為了繞開 瀏覽器單域名下的cookie數限制。使用子cookie可以使用單個cookie進行存儲和訪問,而非對每一個名值對使用不同的cookie存儲。

子cookie的最常見格式:

name=name1=value1&name2=value2&name3=value3

獲取子cookie信息的方法:

const SubCookieUtil = {
  get: function(name, subName) {
    const subCookies = this.getAll(name);
    if(subCookies) {
      return subCookies[subName];
    } else {
      return null;
    }
  },

  getAll: function(name, subName) {
    const cookieName = name + ‘=‘;
    const cookieStart = document.cookie.indexOf(cookieName);
    let subCookieValueStr;

    if (cookieStart >= 0) {
      var cookieEnd = document.cookie.indexOf(‘;‘,cookieStart);
      if (cookieEnd <0 ) {
        cookieEnd = document.cookie.length;
      }
      subCookieValueStr = document.cookie.subString(cookieStart + cookieName.length, cookieEnd);


      if (subCookieValueStr.length > 0) {
        subCookieItemArr = subCookieValueStr.split(‘&‘);

        const result = {};
        for (let item of subCookieItemArr) {
          const keyValueArr = item.split(‘=‘);
          if(keyValueArr.length == 2) {
            result[decodeURIComponent(keyValueArr[0])] = decodeURIComponent(keyValueArr[1]);
          }
        }
        return result;
      }

      return null;
    }
    return null;
  }
}

二、Web Storage: sessionStorage 和localStorage

1.Web Storage簡介

Web Storage包括 sessionStorage, globalStorage,localStorage三種。

特點

克服了cookie的一些限制:

  • Web Storage存儲的數據被嚴格 控制在客戶端不會將數據發回服務器
  • 數據存儲容量更大。對於localStorage,大多數瀏覽器對每個源限制5MB,也有2.5MB的。sessionStorage有的限制是2.5MB,有的是5MB。

三種類型的特點各不相同。

API

方法:

三種Storage實例都有以下方法:

  • Storage.getItem(name):獲取指定名字對應的值
  • Storage.key(index):獲得index位置處的名值對的名字
  • Storage.removeItem(name): 刪除由name指定的名值對
  • Storage.setItem(name, value):為指定的name設置一個值

每個名值對是作為屬性存儲在Storage對象上的,所以也可以通過‘.‘或‘[]‘訪問屬性來獲取值,通過delete刪除對象屬性。但 更推薦用方法 來訪問數據。

事件

storage事件。

對Storage對象的任何修改都會觸發storage事件。包括添加、修改、刪除數據。

其event對象屬性:

  • domain
  • key:設置或刪除的鍵名
  • newValue:如果是設置值,就為新值;如果是刪除值,就為null
  • oldValue:修改之前的值

2. sessionStorage對象

簡介

sessionStorage對象存儲 特定於某個會話(頁面)的數據,對 特定於某個會話的理解:

  • 會話就是 頁面 的意思。其中的數據只能由 最初給對象存儲數據的頁面訪問。即不可被其他頁面訪問。
  • 頁面會話在瀏覽器打開期間一直保持,並且 重新加載或恢復頁面仍會保持原來的頁面會話在新標簽或新窗口打開一個頁面會初始化一個新的會話
  • 其中的數據會在頁面會話結束(即 瀏覽器關閉)時被清除。

用例:

sessionStorage.setItem(‘name‘,‘Boonie‘);
sessionStorage.getItem(‘name‘);//"Boonie"
sessionStorage.removeItem(‘name‘);


const data = {};
for(let i=0, len = sessionStorage.length; i<len; i++) {
  const name = sessionStorage.key(i);
  const value = sessionStorage.getItem(name);
  data[name] = value;
}

3. localStorage對象

簡介

localStorage對象存儲 特定於某個域名的、跨對話的、持久保存的數據。具體理解:

  • 訪問同一個localStorage對象,頁面必須符合 同源協議(即域名、協議和端口相同)
  • 可以跨對話(頁面)訪問,只要頁面符合同源協議
  • 如果 不使用removeItem()或delete刪除,也不清除瀏覽器緩存,存儲於localStorage的數據將 一直存在,一直保留在磁盤上。

用例

localStorage.setItem(‘name‘, ‘Bonnie‘);
localStorage.getItem(‘name‘);

4. *globalStorage[‘xxx.com‘]對象

簡介

localStorage取代了globalStorage。

特性和localStorage基本一樣,但相比localStorage, globalStorage有特定的訪問限制:需要通過[]來指定域。globalStorage對象不是Storage實例,globalStorage[‘xxx.com‘]才是Storage實例。

localStorage相當於 globalStorage[location.host]

大多瀏覽器都不支持globalStorage,Chrome也不支持。

tips: location.host包含域名和端口號,location.hostname只是域名,location.port只是端口號。

用例

globalStorage[‘www.example.com‘].getItem(‘name‘);

參考資料

https://developer.mozilla.org/zh-CN/docs/Web/API/Document/cookie
https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API
《JavaScript高級程序設計》Chapter23

cookie與web Storage