應用緩存:是項目自己的緩存,好比一個js對象緩存的數據,或者狀態管理機制如vuex和redux等進行應用數據存儲,它們在頁面刷新就會丟失。javascript
離線緩存:應用在離線狀況下也能快速訪問的緩存資源,這裏主要講serviceWorker,大多數瀏覽器已經支持serviceWorker提供離線緩存。html
本地緩存:cookie大小通常限制在4kb如下,並且cookie會隨請求發送到後端,因此通常只將用戶登陸態或者權限驗證放在cookie中,避免影響請求傳輸效率;localStorage和sessionStorage的大小通常均是在5mb如下,sessionStorage的生命週期是維持到頁面窗口關閉,而localStorage存儲在瀏覽器緩存中直到代碼刪除或者手動清除瀏覽器緩存,它們都不能跨域訪問。前端
親測手動清除瀏覽器緩存能夠清除localStorage、sessionStorage和cookie。vue
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(key) 獲取指定key本地存儲的值。
localStorage.getItem("job");
.removeItem(key)刪除指定key本地存儲的值。
localStorage.removeItem("job");
.clear() 清除全部的key/value
localStorage.clear();
Cookie(存儲4k)
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)。它相似於關係型數據庫的表格。
數據記錄:對象倉庫保存的是數據記錄。每條記錄相似於關係型數據庫的行,可是隻有主鍵和數據體兩部分。主鍵用來創建默認的索引,必須是不一樣的,不然會報錯。主鍵能夠是數據記錄裏面的一個屬性,也能夠指定爲一個遞增的整數編號。
索引:爲了加速數據的檢索,能夠在對象倉庫裏面,爲不一樣的屬性創建索引。
事務:數據記錄的讀寫和刪改,都要經過事務完成。事務對象提供error
、abort
和complete
三個事件,用來監聽操做結果。
操做流程:
打開數據庫:
var request = window.indexedDB.open(databaseName, version);
這個方法接受兩個參數,第一個參數是字符串,表示數據庫的名字。若是指定的數據庫不存在,就會新建數據庫。第二個參數是整數,表示數據庫的版本。若是省略,打開已有數據庫時,默認爲當前版本;新建數據庫時,默認爲1
。indexedDB.open()
方法返回一個 IDBRequest 對象。這個對象經過三種事件error
、success
、upgradeneeded
,處理打開數據庫的操做結果。
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(如XHR
和localStorage
)不能在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
.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