前端存儲 --- cookie & localStorage & sessionStorage & Web SQL & IndexDB & Cache Storage

應用緩存:是項目自己的緩存,好比一個js對象緩存的數據,或者狀態管理機制如vuex和redux等進行應用數據存儲,它們在頁面刷新就會丟失。javascript

離線緩存:應用在離線狀況下也能快速訪問的緩存資源,這裏主要講serviceWorker,大多數瀏覽器已經支持serviceWorker提供離線緩存。html

本地緩存:cookie大小通常限制在4kb如下,並且cookie會隨請求發送到後端,因此通常只將用戶登陸態或者權限驗證放在cookie中,避免影響請求傳輸效率;localStorage和sessionStorage的大小通常均是在5mb如下,sessionStorage的生命週期是維持到頁面窗口關閉,而localStorage存儲在瀏覽器緩存中直到代碼刪除或者手動清除瀏覽器緩存,它們都不能跨域訪問。前端

親測手動清除瀏覽器緩存能夠清除localStorage、sessionStorage和cookie。vue

不一樣瀏覽器沒法共享localStorage和sessionStorage中的信息。同一瀏覽器的相同域名和端口的不一樣頁面間能夠共享相同的 localStorage,可是不一樣頁面間沒法共享sessionStorage的信息。這裏須要注意的是,頁面僅指頂級窗口,若是一個頁面包含多個iframe且他們屬於同源頁面,那麼他們之間是能夠共享sessionStorage的。

localStorage是經過瀏覽器存儲到本機機器上的磁盤中,生成.localstorage文件但實際上是sqlit數據庫文件。域內安全、永久保存,沒有時間限制,即客戶端或瀏覽器中來自同一域名的全部頁面均可訪問localStorage數據且數據除了刪除不然永久保存,但瀏覽器之間的數據事相互獨立的;java

sessionStorage不是用來存儲在客戶端,而是存儲在整個會話當中,即瀏覽器訪問服務器這個過程,關閉當前瀏覽器窗口或中止服務器即斷開會話,存儲的數據會被刪除。nginx

localStorage和sessionStorage都具備相同的操做方法:setItem、getItem、removeItem、clear。web

setItem 存儲 valuesql

.setItem( key, value) 將value存儲到key字段。vuex

localStorage.setItem("job", "basketballplayer");

setItem 存儲 json對象數據庫

localStorage 和 sessionStorage也可存儲Json對象,存儲時,經過JSON.stringify()將對象轉換爲文本格式;讀取時,經過JSON.parse()將文本轉換回對象。

var user = {
       name:"james", 
 
       job:"basketballplayer"// 存儲值:將對象轉換爲Json字符串
localStorage.setItem('user', JSON.stringify(user));
 
// 取值時:把獲取到的Json字符串轉換回對象
var userJsonStr = localStorage.getItem('user');
var userEntity = JSON.parse(userJsonStr);

console.log(userEntity.name); // => james

getItem獲取value

.getItem(key) 獲取指定key本地存儲的值。

localStorage.getItem("job");

removeItem刪除key

.removeItem(key)刪除指定key本地存儲的值。

localStorage.removeItem("job");

clear清除全部的key/value

.clear() 清除全部的key/value

localStorage.clear();

Cookie(存儲4k)

HTTP協議自己是無狀態的。什麼是無狀態呢, 服務器沒法知道兩個請求是否來自同一個瀏覽器。Cookie其實是一小段的文本信息(key-value格式)。客戶端向服務器發起請求,若是服務器須要記錄該用戶狀態,就使用response向客戶端瀏覽器頒發一個Cookie。客戶端瀏覽器會把Cookie保存起來(這裏應該是瀏覽器自動保存,不用寫代碼)。當瀏覽器再請求該網站時,瀏覽器把請求的網址連同該Cookie一同提交給服務器。服務器檢查該Cookie,以此來辨認用戶狀態。默認狀況下,cookie 在瀏覽器關閉時刪除。
如下圖中若是步驟5攜帶的是過時的cookie或者是錯誤的cookie,那麼將認證失敗,返回至要求身份認證頁面。
 
 
修改cookie
HttpServletResponse提供的Cookie操做只有一個addCookie(Cookie cookie),因此想要修改Cookie只能使用一個同名的Cookie來覆蓋原先的Cookie。新建的Cookie,除了value、maxAge以外的屬性,好比name、path、domain都必須與原來的一致才能達到修改或者刪除的效果。不然,瀏覽器將視爲兩個不一樣的Cookie不予覆蓋。
若是要刪除某個Cookie,則只須要新建一個同名的Cookie,並將maxAge設置爲0,並覆蓋原來的Cookie便可。
從服務器端,發送cookie給客戶端,是對應的Set-Cookie。包括了對應的cookie的名稱,值,以及各個屬性:
Set-Cookie: lu=Rg3vHJZnehYLjVg7qi3bZjzg; Expires=Tue, 15 Jan 2013 21:47:38 GMT; Path=/; Domain=.169it.com; HttpOnly

值得注意的是,從客戶端讀取Cookie時,包括maxAge在內的其餘屬性都是不可讀的,也不會被提交。瀏覽器提交Cookie時只會提交name和value屬性:

GET /spec.html HTTP/1.1  

Host: www.example.org  

Cookie: name=value; name2=value2  

Accept: */*

maxAge屬性只被瀏覽器用來判斷Cookie是否過時。當cookie過時時,瀏覽器在與後臺交互時會自動篩選過時cookie,過時了的cookie就不會被攜帶了。

除了服務器發送給客戶端(瀏覽器)的時候,經過Set-Cookie,瀏覽器自動建立或更新對應的cookie以外,還能夠經過瀏覽器內置的一些腳本,好比javascript,去設置對應的cookie,對應實現是操做js中的document.cookie:

// 設置cookie
document.cookie = "username=Bill Gates; expires=Sun, 31 Dec 2017 12:00:00 UTC; path=/";

// 讀取cookie
// document.cookie 會在一條字符串中返回全部 cookie,好比:'cookie1=value; cookie2=value; cookie3=value;'
var x = document.cookie;

Cookie中的Path與domain

瀏覽器會將domain和path都相同的cookie保存在一個文件裏

 

跨域問題

所謂瀏覽器的同源策略是指,協議,域名,端口相同。當一個瀏覽器的兩個tab頁中分別打開百度和谷歌的頁面。瀏覽器的百度tab頁執行腳本的時候會檢查這個腳本是屬於哪一個頁面的,即檢查是否和當前頁面地址同源,只有和百度同源的腳本纔會被執行。 若是非同源,那麼在請求數據時,瀏覽器會在控制檯中報一個異常,提示拒絕訪問。

瀏覽器是以HTTP請求模式獲取請求資源,如:Http://www.baidu.com:8080/xxxx。其中HTTP是請求協議,www.baidu.com是域名,8080是端口號,請求的意思是使用HTTP協議模式,從域名爲www.baidu.com的服務器上8080端口部署的服務下請求資源XXXX。
即當瀏覽器打開的tab知足同源策略後,能夠在各個文件之間進行數據的相互存取操做,這樣就能夠不用反覆進行重複的數據請求操做,如登陸權限,獲取用戶信息等等。

localStorage和cookie不能跨域讀取的(包括子域)

localStorage和cookie須要跨域的業務場景

www.baidu域名下面登陸了,發現yun.baidu域名下面也天然而然登陸了;淘寶登陸了,發現天貓也登陸了,淘寶和天貓是徹底不同的2個域名。

localStorage的跨域解決方案

若是兩個頁面的主域名相同,將document.domain屬性值設置爲根域名:

document.domain = 'a.com';

域名徹底不相同的話,能夠用postMessage和iframe相結合的方法。postMessage(data,origin)方法容許來自不一樣源的腳本採用異步方式進行通訊:

data:要傳遞的數據,H5規範中提到該參數能夠是Javascript的任意基本類型或可複製的對象,然而並非全部瀏覽器支持任意類型的參數,部分瀏覽器只能處理字符串參數,因此在傳遞參數時須要使用JSON.stringify()方法對對象參數序列化。

origin:字符串類型的參數,指明目標窗口的源,協議+主機+端口號[+URL],URL會被忽略,因此能夠不寫。只是爲了安全考慮,postMessage()方法只會將message傳遞給指定窗口,固然也能夠將參數設置爲"*",這樣能夠傳遞給任意窗口,若是要指定和當前窗口同源的話設置爲"/"。

a.com下頁面經過iframe插入了b.com下的一個頁面,a.com下頁面代碼以下,如下代碼能夠向b.com域下的頁面發送數據:

var iframeWin = document.getElementsByTagName('iframe')[0].contentWindow;
var obj = {
  name: 'a'
};
iframeWin.postMessage(JSON.stringify({
  key: "localstorage",
  data: obj
}), "http://b.com");

b.com下頁面代碼以下,如下代碼能夠接收http://a.com域下頁面發送的數據:

window.onmessage = function(event) {
  if (event.origin !== 'http://b.com') {
    return;
  }
  var payload = JSON.parse(event.data);
  localStorage.setItem(payload.key, JSON.stringify(payload.data));
};

cookie的跨域解決方案

cookie 通常都是因爲用戶訪問頁面而被建立的,但是並非只有在建立 cookie 的頁面才能夠訪問這個cookie。在默認狀況下,出於安全方面的考慮,只有與建立 cookie 的頁面處於同一個目錄或在建立cookie頁面的子目錄下的網頁才能夠訪問。那麼此時若是但願其父級或者整個網頁都可以使用cookie,就須要進行path(路徑)的設置。

path表示cookie所在的目錄, 如:

cookie1的path爲/tag/;

cookie2的path爲/tag/id/;

那麼tag下的全部頁面均可以訪問到cookie1,而只有/tag/id/下的子頁面能訪問cookie2。 這是由於cookie2能讓其path路徑下的頁面訪問。

使用domain

domain表示的是cookie所在的域,默認是頁面的地址,如網址爲 http://www.haorooms.com/post/long_lianjie_websocket ,那麼domain默認爲www.haorooms.com。

如域A爲love.haorooms.com,域B爲resource.haorooms.com,那麼在域A生產一個令域A和域B都能訪問的cookie就要將該cookie的domain設置爲.haorooms.com;

使用jsonp

使用nginx反向代理

Web SQL(能夠在最新版的 Safari, Chrome 和 Opera 瀏覽器中工做)

Web SQL 是在瀏覽器上模擬數據庫,可使用JS來操做SQL完成對數據的讀寫。Web SQL 數據庫 API 並非 HTML5 規範的一部分,可是它是一個獨立的規範。它相似於關係型數據庫。

如下是它的三個核心方法:

openDatabase:這個方法使用現有的數據庫或者新建的數據庫建立一個數據庫對象。

transaction:這個方法讓咱們可以控制一個事務,以及基於這種狀況執行提交或者回滾。

executeSql:這個方法用於執行實際的 SQL 查詢。

咱們可使用 openDatabase() 方法來打開已存在的數據庫,若是數據庫不存在,則會建立一個新的數據庫,使用代碼以下:

var db = openDatabase('mydb', '1.0', 'Test DB', 2 * 1024 * 1024);
// 參數
// 數據庫名稱
// 版本號
// 描述文本
// 數據庫大小
// 建立回調,會在建立數據庫後被調用

在執行上面的建立表語句後,插入一些數據:

var db = openDatabase('mydb', '1.0', 'Test DB', 2 * 1024 * 1024);
db.transaction(function (tx) {
   tx.executeSql('CREATE TABLE IF NOT EXISTS LOGS (id unique, log)');
   tx.executeSql('INSERT INTO LOGS (id, log) VALUES (1, "W3Cschool教程")');
   tx.executeSql('INSERT INTO LOGS (id, log) VALUES (2, "www.w3cschool.cn")');
});

讀取數據庫中已經存在的數據:

var db = openDatabase('mydb', '1.0', 'Test DB', 2 * 1024 * 1024);

db.transaction(function (tx) {
   tx.executeSql('CREATE TABLE IF NOT EXISTS LOGS (id unique, log)');
   tx.executeSql('INSERT INTO LOGS (id, log) VALUES (1, "W3Cschool教程")');
   tx.executeSql('INSERT INTO LOGS (id, log) VALUES (2, "www.w3cschool.cn")');
});

db.transaction(function (tx) {
   tx.executeSql('SELECT * FROM LOGS', [], function (tx, results) {
      var len = results.rows.length, i;
      msg = "
查詢記錄條數: " + len + "

";
      document.querySelector('#status').innerHTML +=  msg;
    
      for (i = 0; i < len; i++){
         alert(results.rows.item(i).log );
      }
    
   }, null);
});

完整實例:

var db = openDatabase('mydb', '1.0', 'Test DB', 2 * 1024 * 1024);
var msg;
 
db.transaction(function (tx) {
    tx.executeSql('CREATE TABLE IF NOT EXISTS LOGS (id unique, log)');
    tx.executeSql('INSERT INTO LOGS (id, log) VALUES (1, "菜鳥教程")');
    tx.executeSql('INSERT INTO LOGS (id, log) VALUES (2, "www.runoob.com")');
    msg = '<p>數據表已建立,且插入了兩條數據。</p>';
    document.querySelector('#status').innerHTML =  msg;
});
 
db.transaction(function (tx) {
tx.executeSql('SELECT * FROM LOGS', [], function (tx, results) {
    var len = results.rows.length, i;
    msg = "<p>查詢記錄條數: " + len + "</p>";
    document.querySelector('#status').innerHTML +=  msg;
 
    for (i = 0; i < len; i++){
        msg = "<p><b>" + results.rows.item(i).log + "</b></p>";
        document.querySelector('#status').innerHTML +=  msg;
    }
}, null);
});

IndexDB

IndexedDB是瀏覽器提供的本地數據庫, 容許儲存大量數據,提供查找接口,還能創建索引。這些都是 LocalStorage 所不具有的。就數據庫類型而言,IndexedDB 不屬於關係型數據庫(不支持 SQL 查詢語句),更接近 NoSQL 數據庫。

要想清理indexDB(瀏覽器數據庫),無需代碼,只要找到indexDB(瀏覽器數據庫)在本機的存儲位置,而後刪除文件夾就能夠了。

indexDB(瀏覽器數據庫)本機(windows版本)存儲文件夾在:C:\Users\ '當前的登陸用戶'\AppData\Local\Google\Chrome\User Data\Default\IndexedDB

IndexedDB 具備如下特色:

(1)鍵值對儲存。 IndexedDB 內部採用對象倉庫(object store)存放數據。全部類型的數據均可以直接存入,包括 JavaScript 對象。對象倉庫中,數據以"鍵值對"的形式保存,每個數據記錄都有對應的主鍵,主鍵是獨一無二的,不能有重複,不然會拋出一個錯誤。

(2)異步。 IndexedDB 操做時不會鎖死瀏覽器,用戶依然能夠進行其餘操做,這與 LocalStorage 造成對比,後者的操做是同步的。異步設計是爲了防止大量數據的讀寫,拖慢網頁的表現。

(3)支持事務。 IndexedDB 支持事務(transaction),這意味着一系列操做步驟之中,只要有一步失敗,整個事務就都取消,數據庫回滾到事務發生以前的狀態,不存在只改寫一部分數據的狀況。

(4)同源限制 IndexedDB 受到同源限制,每個數據庫對應建立它的域名。網頁只能訪問自身域名下的數據庫,而不能訪問跨域的數據庫。

(5)儲存空間大 IndexedDB 的儲存空間比 LocalStorage 大得多,通常來講很多於 250MB,甚至沒有上限。

(6)支持二進制儲存。 IndexedDB 不只能夠儲存字符串,還能夠儲存二進制數據(ArrayBuffer 對象和 Blob 對象)。

IndexedDB包括如下對象:

 

數據庫:IDBDatabase 對象
對象倉庫:IDBObjectStore 對象
索引: IDBIndex 對象
事務: IDBTransaction 對象
操做請求:IDBRequest 對象
指針: IDBCursor 對象
主鍵集合:IDBKeyRange 對象

 

數據庫:IndexedDB 數據庫有版本的概念。同一個時刻,只能有一個版本的數據庫存在。若是要修改數據庫結構(新增或刪除表、索引或者主鍵),只能經過升級數據庫版本完成。

對象倉庫:每一個數據庫包含若干個對象倉庫(object store)。它相似於關係型數據庫的表格。

數據記錄:對象倉庫保存的是數據記錄。每條記錄相似於關係型數據庫的行,可是隻有主鍵和數據體兩部分。主鍵用來創建默認的索引,必須是不一樣的,不然會報錯。主鍵能夠是數據記錄裏面的一個屬性,也能夠指定爲一個遞增的整數編號。

索引:爲了加速數據的檢索,能夠在對象倉庫裏面,爲不一樣的屬性創建索引。

事務:數據記錄的讀寫和刪改,都要經過事務完成。事務對象提供errorabortcomplete三個事件,用來監聽操做結果。

操做流程:

打開數據庫:

var request = window.indexedDB.open(databaseName, version);

這個方法接受兩個參數,第一個參數是字符串,表示數據庫的名字。若是指定的數據庫不存在,就會新建數據庫。第二個參數是整數,表示數據庫的版本。若是省略,打開已有數據庫時,默認爲當前版本;新建數據庫時,默認爲1indexedDB.open()方法返回一個 IDBRequest 對象。這個對象經過三種事件errorsuccessupgradeneeded,處理打開數據庫的操做結果。

error事件表示打開數據庫失敗:

request.onerror = function (event) {
  console.log('數據庫打開報錯');
};

success事件表示成功打開數據庫:

var db;

request.onsuccess = function (event) {
  db = request.result;   // 經過request.result屬性拿到數據庫對象。
  console.log('數據庫打開成功');
};

若是指定的版本號,大於數據庫的實際版本號,就會發生數據庫升級事件upgradeneeded:

var db;

request.onupgradeneeded = function (event) {
  db = event.target.result;   // 經過target.result屬性,拿到數據庫實例。
}

新建數據庫

新建數據庫與打開數據庫是同一個操做。若是指定的數據庫不存在,就會新建。不一樣之處在於,後續的操做主要在upgradeneeded事件的監聽函數裏面完成,由於這時版本從無到有,因此會觸發這個事件。一般,新建數據庫之後,第一件事是新建對象倉庫(即新建表):

request.onupgradeneeded = function (event) {
  db = event.target.result;
  var objectStore;
  if (!db.objectStoreNames.contains('person')) {
    // 新增一張叫作person的表格,主鍵是id
    objectStore = db.createObjectStore('person', { keyPath: 'id' });
  }
}

若是數據記錄裏面沒有合適做爲主鍵的屬性,那麼可讓 IndexedDB 自動生成主鍵:

var objectStore = db.createObjectStore(
  'person',
  { autoIncrement: true }  // 指定主鍵爲一個遞增的整數
)

新建對象倉庫之後,下一步能夠新建索引:

request.onupgradeneeded = function(event) {
  db = event.target.result;
  var objectStore = db.createObjectStore('person', { keyPath: 'id' });
  // 三個參數分別爲索引名稱、索引所在的屬性、配置對象(說明該屬性是否包含重複的值)
  objectStore.createIndex('name', 'name', { unique: false });
  objectStore.createIndex('email', 'email', { unique: true });
}

新增數據指的是向對象倉庫寫入數據記錄,須要經過事務完成。

上面代碼中,寫入數據須要新建一個事務。新建時必須指定表格名稱和操做模式("只讀"或"讀寫")。新建事務之後,經過IDBTransaction.objectStore(name)方法,拿到 IDBObjectStore 對象,再經過表格對象的add()方法,向表格寫入一條記錄。寫入操做是一個異步操做,經過監聽鏈接對象的success事件和error事件,瞭解是否寫入成功:

function add() {
  var request = db.transaction(['person'], 'readwrite')
    .objectStore('person')
    .add({ id: 1, name: '張三', age: 24, email: 'zhangsan@example.com' });

  request.onsuccess = function (event) {
    console.log('數據寫入成功');
  };

  request.onerror = function (event) {
    console.log('數據寫入失敗');
  }
}

add();

讀取數據也是經過事務完成。

function read() {
   var transaction = db.transaction(['person']);
   var objectStore = transaction.objectStore('person');
   // objectStore.get()方法用於讀取數據,參數是主鍵的值
   var request = objectStore.get(1); 

   request.onerror = function(event) {
     console.log('事務失敗');
   };

   request.onsuccess = function( event) {
      if (request.result) {
        console.log('Name: ' + request.result.name);
        console.log('Age: ' + request.result.age);
        console.log('Email: ' + request.result.email);
      } else {
        console.log('未得到數據記錄');
      }
   };
}

read();

遍歷數據表格的全部記錄,使用指針對象 IDBCursor:

function readAll() {
  var objectStore = db.transaction('person').objectStore('person');

   // 新建指針對象的openCursor()方法是一個異步操做,因此要監聽success事件
   objectStore.openCursor().onsuccess = function (event) {
     var cursor = event.target.result;

     if (cursor) {
       console.log('Id: ' + cursor.key);
       console.log('Name: ' + cursor.value.name);
       console.log('Age: ' + cursor.value.age);
       console.log('Email: ' + cursor.value.email);
       cursor.continue();
    } else {
      console.log('沒有更多數據了!');
    }
  };
}

readAll();

更新數據要使用IDBObject.put()方法:

function update() {
  var request = db.transaction(['person'], 'readwrite')
    .objectStore('person')
    .put({ id: 1, name: '李四', age: 35, email: 'lisi@example.com' });   // put()方法自動更新了主鍵爲1的記錄

  request.onsuccess = function (event) {
    console.log('數據更新成功');
  };

  request.onerror = function (event) {
    console.log('數據更新失敗');
  }
}

update();

IDBObjectStore.delete()方法用於刪除數據:

function remove() {
  var request = db.transaction(['person'], 'readwrite')
    .objectStore('person')
    .delete(1);

  request.onsuccess = function (event) {
    console.log('數據刪除成功');
  };
}

remove();

使用索引

索引的意義在於,可讓你搜索任意字段,也就是說從任意字段拿到數據記錄。若是不創建索引,默認只能搜索主鍵(即從主鍵取值)。

假定新建表格的時候,對name字段創建了索引:

objectStore.createIndex('name', 'name', { unique: false });

如今,就能夠從name找到對應的數據記錄了:

var transaction = db.transaction(['person'], 'readonly');
var store = transaction.objectStore('person');
var index = store.index('name');
var request = index.get('李四');

request.onsuccess = function (e) {
  var result = e.target.result;
  if (result) {
    // ...
  } else {
    // ...
  }
}

IndexDB和Web SQL能夠用以下方式刪除:

Cache Storage

Cache Storage和Service Worker能夠實現無後端參與的純前端的離線緩存。

首先了解下Service Worker吧。

直接請求的策略是web請求的作法,客戶端發送請求,服務器返回數據,客戶端再顯示。在中間沒有添加任何東西。那爲什麼如今要在中間加一個Service Worker呢?主要是用戶應付一些特殊場景和需求,如離線處理、消息推送等。而離線應用和消息推送正是目前native app相對於web app的優點所在。因此,Service Worker出現的目的是讓web app能夠和native app開始真正意義上的競爭。

咱們日常瀏覽器窗口中跑的頁面運行的是主JavaScript線程,DOM和window全局變量都是能夠訪問的。Service Worker和JavaScript跑的是徹底不一樣的兩個線程,所以Service Worker不管怎樣也不會阻塞主JavaScript線程,也就是不會引發瀏覽器頁面加載的卡頓的問題。因爲Service Worker設計爲徹底異步,同步API(如XHRlocalStorage)不能在Service Worker中使用。

除了上面的些限制外,Service Worker對咱們的協議也有要求,就是必須是https協議的,但Service Worker在http://localhost或者http://127.0.0.1這種本地環境下的時候也是能夠跑起來的。Service workers會大量使用Promise。

CacheStorage及Cache定義在Service Workers裏。CacheStorage和Cache,是兩個與緩存有關的接口,用於管理當前網頁/Web App的緩存。CacheStorage管理着全部的Cache。CacheStorage主要功能無非就是增刪改查(上面全部方法都返回Promise):

delete(),刪除某個Cache;

open(),打開某個Cache(打開後才能修改Cache),若沒有則新建一個;

keys(),獲得全部Cache的名稱;

has(),判斷某個Cache是否存在。

// 刪除名爲`my-app`的Cache
caches.delete('my-app').then(() => {console.log('Deleted.')})

// 打開名爲`my-app`的Cache
caches.open('my-app').then(cache => {
  // 操控cache
})

Cache是一個類Map的數據結構對象。其鍵都是一個request(url),而值則是response。

.match(requestUrl, options),返回Promise,能獲得requestUrl對應的response

.put(requestUrl, response),將requestUrl及其response保存在Cache裏

.delete(requestUrl),從Cache裏刪除requestUrl及其response

.keys(),返回全部存在Cache的requestUrl

除了上述的基本方法外,Cache還提供 .add(requestUrl),能夠自動取得requestUrl對應的response,而後put進Cache裏。
// 獲得在Cache裏某個url對應的response
cache.match('/users').then(response => {
  // 操控response
})

// 將`/user`及其response添加到緩存裏
cache.add('/users').then(() => {console.log('Done.')})

 

原文:

https://www.jianshu.com/p/e86d92aeae69

https://www.haorooms.com/post/kuayu_localstorage_cookie

https://blog.csdn.net/qq_41480099/article/details/80237636

https://www.jianshu.com/p/f8fb042c70e6

https://www.jianshu.com/p/6fc9cea6daa2

相關文章
相關標籤/搜索