前端開發的客戶端本地儲存
4
在前端開發過程中,為了與伺服器更方便的互動或者提升使用者體驗,我們都會在客戶端(使用者)本地儲存一部分資料,比如cookie/localStorage/sessionStorage
。在後端管理系統的前端,更是會涉及到一部分超大資料的請求,一個介面有時會達到5M甚至15M的程度,當這個介面資料並不是經常更新時,我們可以用兩種方式,一種是分頁請求+預載入+懶載入,另一種就是本地儲存+熱更新。而由於第二種方式使用者體驗更優秀,便是我常用的方式。
這篇文章的客戶端本地儲存,我們主要講到cookie/localStorage/sessionStorage/indexedDB
四種技術。
cookie
HTTP Cookie
cookie
,該標準用於瀏覽器儲存會話資訊,在發起HTTP請求時攜帶Cookie
引數:
// Request Header GET /oss/index.php?r=api/jlog/collect HTTP/1.1 Host: gzhxy.baidu.com:8090 Connection: keep-alive Pragma: no-cache Cache-Control: no-cache X-Requested-With: XMLHttpRequest User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36 ··· Cookie: name=Leon; age=24
Cookie
有一些限制:
- 它是以
;
(分號+一個空格)分割的鍵值對字串,在網路傳送時必須是URL編碼的。 - 繫結在固定域名下,不允許其它域名訪問。
- 有些瀏覽器有數量限制,在超出後刪除順序不統一,有些最近最少使用
(LRU)
,有些隨機刪 - 同一域名下總大小限制
5KB
,超出限制後靜默失敗
Cookie
的引數構成:
- 名稱:唯一確定,不區分大小寫(實際編寫建議區分),必須URL編碼
- 值:字串值,必須URL編碼
- 域:該
cookie
欄位有效的域,可以為baidu.com
域名,也可以為cdn.baidu.com
子域名,預設值為當前頁面子域名 - 路徑:該
cookie
欄位有效範圍為指定域下的具體路徑,如cdn.baidu.com/oss
- 失效時間:
cookie
被刪除的時間戳,預設值為瀏覽器會話結束被刪除,也可以手動設定,時間格式為GMT
格式Wdy, DD-Mon-YYYY HH:MM:SS GMT
,可以呼叫Date
例項的toGMTString()
方法轉換,如果設定為過去的時間,該cookie
被立刻刪除 - 安全標誌:為單詞
secure
而非鍵值對,指定後,只有在SSL
連線的時候才會被髮送到伺服器,也就是https
協議
注意引數中只有名稱和值才會被髮送給伺服器,其餘的只是需要瀏覽器識別的命令式引數。
cookie
的介面設定非常的不人性化,往往需要我們對其操作進行封裝才會方便使用。下面我們對其進行增刪查改。
檢視cookie:decodeURIComponent(document.cookie);
新增或修改cookie:
// 需要改成自己需要的cookie和域名、路徑以及是否為https
document.cookie = 'encodeURIComponent(name)=encodeURIComponent(Leon); expries=' + (new Date(Date.now() + 24*60*60*1000)).toGMTString() + '; path=oss; domain=cdn.baidu.com; secure';
刪除cookie: document.cookie = 'name=Leon; expires=' + (new Date(0)).toGMTString();
具體的封裝的方式網上有很多,可以去搜一搜,核心就是對cookie
進行字串檢索和切分,以及將傳入的函式引數最終轉換為字串。
Storage
由於cookie
的大小限制和需要全量傳遞給伺服器,在很多場景下並不適用,所以HTML5
規範中出現了Storage
物件,包含localStorage
和sessionStorage
兩種繼承物件,屬於window
的屬性。它提供了通常5M
的大小空間來儲存無需伺服器互動的本地資料。
Storage
的常用方法:
clear()
: 清除所有值getItem(name)
: 獲取指定name的值key(index)
: 獲得對應索引值的鍵名removeItem(name)
: 刪除指定name的鍵值對setItem(name, value)
: 為指定name設定對應的值
除了這些方法之外,Storage
物件可以直接通過點語法或者方括號語法訪問屬性和操作屬性,也可以通過delete
關鍵字刪除屬性。該物件中的值均為字串。
持久化資料localStorage
通常保留到JS刪除或者使用者清除瀏覽器快取。
會話資料sessionStorage
保留到關閉瀏覽器。由於繫結會話視窗,所以不支援本地檔案讀寫。另外在IE8中,該物件為非同步讀寫,需要呼叫begin()
和commit()
方法保證成功讀取,不再贅述。
對Storage
物件做任何的新增,修改或者刪除操作,都會觸發storage
事件。該事件只支援在與服務端通訊時,一個頁面修改,另一個頁面會觸發該事件。
document.addEventListener('storage', function (e) {
console.log(e);
});
該事件物件的主要屬性:
domain
: 發生變化的儲存空間的域名key
:修改的鍵名newValue
:如果是設定值,則為新值;如果刪除,則為null
oldValue
:更改前的值
IndexedDB
擁有了Storage
利器,已經能解決很多問題,但是通常5M
的大小限制還是會限制一部分場景,比如後臺管理系統的介面資料很容易突破5M
,這個時候就需要我們的瀏覽器資料庫IndexedDB
了。其實在此之前,各家廠商主要推廣的是Web SQL Database
,不過後來被廢棄了,雖然現在在部分平臺上也能使用,但我們不做介紹了。
IndexedDB
資料庫用於瀏覽器儲存結構化資料,區別於傳統資料庫它儲存的是物件,完全採用事務型別,所有的操作被轉換為請求的方式,所以我們需要對每一步操作添加回調函式。
一個完整的例項為:
// 判斷能否正確開啟資料庫,避免多次檢測
let dbOpened = false;
// 開啟本地持久化資料庫,預設版本為1
const request = indexedDB.open('jomocha');
// 當開啟錯誤時
request.onerror = function(event){
console.error('開啟本地持久化資料錯誤', event);
OSS.commonUI.showMsg('開啟本地持久化資料庫錯誤,試用功能,不影響使用,請聯絡zhaoshuaiqiang', 'error');
};
// 當資料庫首次建立該版本時(首次建立或更新版本)
request.onupgradeneeded = function(event){
const db = event.target.result;
// 建立一個數據庫儲存物件,儲存所有的維度項,分為name和list兩個屬性
const objectStore = db.createObjectStore('dimensions', {
keyPath: 'name'
});
// 定義儲存物件的資料項屬性
objectStore.createIndex('name', 'name', {
unique: true
});
};
// 成功打開了資料庫
request.onsuccess = function(event){
dbOpened = true;
const db = event.target.result;
// 新建一個事務,包含oncomplete 和onerror控制代碼事件,預設值為readonly,只讀模式,可並行
const transaction = db.transaction(['dimensions']);
// 開啟儲存物件
const objectStore = transaction.objectStore('dimensions');
const request = objectStore.get('host');
request.onsuccess = function (event) {
// 第一次開啟資料庫時,肯定沒有資料,所以需要檢測
if (event.target.result) {
JomoCha.data = event.target.result.list;
}
};
}
資料庫
當我們需要使用IndexedDB
時,首先要呼叫indexedDB.open()
方法開啟資料庫,如果該資料存在,則發起開啟的請求,如果不存在,則發起建立並開啟的請求。該方法會返回一個IDBRequest
物件,可以在該物件上添加回調方法。具體的方法如示例中的最外層請求。
回撥函式傳入的事件屬性event.target
就指向該請求,即request
。如果發生了錯誤,event.target.errorCode
將會儲存錯誤資訊的錯誤碼;如果成功,event.target.result
就會儲存一個數據庫例項物件。
錯誤碼列表(第二個開始省略字首IDBDatabaseException.
):
IDBDatabaseException.UNKNOWN_ERR(1)
:意外錯誤,無法歸類NON_TRANSIENT_ERR(2)
:操作不合法NOT_FOUND_ERR(3)
:未發現要操作的資料庫CONSTRAINT_ERR(4)
:違反了資料庫約束DATA_ERR(5)
:提供給事務的資料不滿足要求NOT_ALLOWED_ERR(6)
:操作不合法TRANSACTION_INACTIVE_ERR(7)
:試圖重用已完成的事務ABORT_ERR(8)
:請求中斷,未完成READ_ONLY_ERR(9)
:試圖在只讀模式下寫入或修改資料TIMEOUT_ERR(10)
:在有效時間內未完成操作QUOTA_ERR(11)
:磁碟空間不足
物件儲存空間(表)
成功開啟資料庫之後,我們就可以開啟物件儲存空間了,你可以把它理解成資料庫中的表,用於儲存不同的資料,如使用者、交易、購物車等。下面的所有db
代表成功回撥中的資料庫物件.
我們先呼叫db.createObjectStore('dimensions', {keyPath:'name'});
來建立一張表,這個dimensions
為唯一表名,其中的keyPath
屬性指定該表的鍵名,後面儲存的所有資料都必須擁有該屬性。
看下寫入資料庫的例項:
// 每次同步更新最新資料
$.ajax({
url: '?r=tools/api/hosts',
success: function (data) {
// 如果能夠開啟本地資料庫,則儲存
if (dbOpened) {
const request = indexedDB.open('jomocha');
request.onsuccess = function(event){
const db = event.target.result;
// 新建一個事務,讀寫模式,不可並行
const transaction = db.transaction(['dimensions'], 'readwrite');
// 開啟儲存物件
const objectStore = transaction.objectStore('dimensions');
// 使用put方法,有則修改,沒有則新增
const request = objectStore.put({
name: 'host',
list: data.data
});
}
}
}
});
這裡寫入資料庫的物件裡就包含了name
引數。我們拿到資料使用add()
或put()
方法新增資料,這兩種方法區別在於遇到相同的鍵名存在時,add()
報錯,put()
修改原有值。這兩種方法依然為請求,可以對其指定onsuccess()
或onerror()
事件處理回撥,示例中省略了。
事務
在建立完成資料後,就可以對其進行查詢了,indexedDB
中所有讀寫操作都要通過事務,我們呼叫db.transaction();
方法來開啟所有儲存空間(表),也可以傳入引數來控制我們需要開啟的表:
// 開啟一張表
db.transaction('user');
// 開啟多張表
db.transaction(['user', 'dimensions']);
該方法還接受第二個引數作為訪問方式,包含3種:readonly
(預設值)、readwrite
、versionchange
。最後一個比較特別,為在版本更新時使用,不可以與其他事務併發執行,允許任何操作,包括刪除和建立索引。
現在通過事務我們已經確定了操作空間,接下來就可以獲取具體的資料物件了,下面的transaction
代表著上面的db.transaction()
方法返回的事務。事務可以執行多個請求,本身也是一個請求,可以指定相應的oncomplete()
和onerror()
事件處理回撥。其中的oncomplete()
事件不能拿到該事務中請求的資料。
使用transaction.objectStore('dimension');
獲取資料物件,然後就可以通過add/put/get/delete/clear
方法進行增刪查改,均為請求,需要設定onsuccess()
和onerror()
回撥。
遊標遍歷
我們可以使用get()
方法檢索出具體的單個物件,但是如果需要遍歷,我們就要使用到遊標查詢了,也就是指定範圍,然後遍歷資料。下面的objectStore
指代上面transaction.objectStore()
返回的資料物件集。
// 指定遊標範圍
const cursorRange = IDBKeyRange.bound('001', '100');
// 開啟遊標查詢
const request = objectStore.openCursor(cursorRange);
request.onsuccess = function (event) {
const cursor = event.target.result;
// 必須檢查cursor是否存在,如果該項存在,則為IDBCursor例項,否則為null
if (cursor) {
console.log(cursor.key + ': ' + cursor.value);
cursor.continue();
} else {
console.log('遍歷完成');
}
}
request.onerror = function (event) {
console.error('遊標區間獲取失敗');
}
在遊標遍歷時,具體的資料會儲存在event.target.result
裡,如果該項存在,則會為一個IDBCursor
物件例項,否則為null
。例項的屬性:
direction
:數值,表示遊標的方向,next/nextunique/prev/prevunique
,帶有unique
的會去重key
:資料物件鍵value
:資料物件值primaryKey
:遊標當前的使用鍵值,後面會說
在遍歷到遊標中具體的每一項時,可以使用update()
和delete()
來修改,如果想要移動遊標:
continue(key)
:存在key移動到指定項,否則下一項advance(count)
:存在count移動指定項數,否則上一項
我們通過IDBKeyRange
物件來控制鍵範圍,有4種方式指定:
only(key)
:只取得想要的鍵值對,等同於直接呼叫get(key)
lowerBound(key, true)
:第一個元素為key,如果第二個引數為true,從該項的下一項開始,預設值為falseupperBound(key, true)
:最後一個元素為key,如果第二個引數為true,從該項的下一項開始,預設值為falsebound(lowerkey, upperkey, true, true)
:前兩者的結合,1和3對應lower,2和4對應upper
openCursor()
方法接收的第一個引數為範圍區間,如果為null,則預設全部範圍;第二個引數為方向,為next/nextunique/prev/prevunique
四個,帶有unique
的會去重
索引
可以使用objectStore.createIndex('name', 'name', {unique: true});
來建立索引,分別為索引名,索引的屬性名,該屬性值是否唯一。呼叫objectStore.delete('name');
來刪除索引。刪除索引不會影響資料,所以沒有回撥函式。
如果我們不想在主鍵上遍歷遊標或者獲取資料,可以在資料集上取得新的索引列表:
// 直接切換索引
const index = objectStore('dimensions');
// 在該索引上進行遊標遍歷
request = index.openCursor();
如果我們在非主鍵的遊標中,想要去的主鍵值,呼叫index.getKey('007');
併發問題
資料庫操作存在併發問題,但由於是非同步,我們不用擔心,但是如果存在新版本變更還是會導致問題。所以開啟資料庫時指定onversionchange()
處理事件,可以避免這個問題,在setVesion()
時,如果觸發onerror()
代表已經打開了該資料庫,無法現在更新版本,提示使用者關閉其它網頁,重新呼叫更新。
總結
如果需要與伺服器實時互動,使用cookie
,如果需要儲存一些小資訊欄位,使用localStorage
,如果只需要本次會話有效,使用sessionStorage
,如果資料很大,使用indexedDB
。使用什麼技術跟業務場景匹配,但是技術還是都要了解都要會,畢竟,巧婦難為無米之炊。
參考資料
相關推薦
前端開發的客戶端本地儲存
4 在前端開發過程中,為了與伺服器更方便的互動或者提升使用者體驗,我們都會在客戶端(使用者)本地儲存一部分資料,比如cookie/localStorage/sessionStorage。在後端管理系統的前端,更是會涉及到一部分超大資料的請求,一個介面有時會達到5M甚至15M
C#開發WEBService服務 C++開發客戶端調用WEBService服務
blank 內容 目錄 www nal .net 操作 service服務 3.1 編寫程序 http://blog.csdn.net/u011835515/article/details/47615425 遇到問題及解決方法: HTTP 錯誤 500.19- I
maven版axis2調用cxf服務端開發客戶端(三)
param system axis2 測試 log png 依賴 println dep 一、新建一個maven項目 二、pom.xml引入axis2依賴 <dependency> <groupId>org.apache.axis2<
Java開發後臺介面+Android開發客戶端的一個例項(學生成績管理)(一)
本文主要是一個查詢學生成績的一整套系統,主要為功能實現,串聯起來後端和移動端的連線。UI後期可以根據自己的愛好再調整,這裡分為兩部分進行介紹: 1、使用Java開發後臺介面,使用現如今非常流行的SpringBoot框架,使用MySql資料庫,持久層框架使用MyBatis,後臺開發平臺為idea,
js獲取客戶端本地ip
使用js獲取客戶端本地ip,不需要額外引入別的檔案 【注:若IE瀏覽器不進行安全設定,IE瀏覽器會預設攔截ActiveX控制元件的使用,將不會返回客戶端的IP地址】 下邊的程式碼使用於通用瀏覽器,但是IE瀏覽器必須去設定ActiveX控制元件。 <!DOCTYPE html> <h
使用BindingX開發客戶端炫酷動畫
Weex 是一套簡單易用的跨平臺開發方案,能以 web 的開發體驗構建高效能、可擴充套件的 native 應用,為了做到這些,Weex 與 Vue 合作,使用 Vue 作為上層框架,並遵循 W3C 標準實現了統一的 JSEngine 和 DOM API,這樣一來
2019年Web程式設計師路線圖:前端開發+後端開發+DevOps
Web開發人員路線圖已經連續釋出三年,由Github平臺的kamranahmedse倡導,2019版本彙集了19位貢獻者。跟往年一樣,技術思維導圖涵蓋前端開發+後端開發+DevOps,話不多說,直接上圖。 想要學習Web前端的小夥伴,在這推薦下自己的Web學習群:585843
2019年Web程式設計師最新路線圖:前端開發+後端開發+DevOps
Web開發人員路線圖已經連續釋出三年,由Github平臺的kamranahmedse倡導,2019版本彙集了19位貢獻者。跟往年一樣,技術思維導圖涵蓋前端開發+後端開發+DevOps,話不多說,直接上圖。 在這裡我還是要推薦下我自己建的web前端開發學習裙:659
前端:客戶端渲染 vs 伺服器渲染
渲染工作應該由誰完成? 時下,前端 UI 設計越來越複雜,可謂“XX與XX齊飛,XX共XX一色”。 越來越複雜的 UI 意味著越來越重的 渲染工作。 目前通常有兩種選擇:伺服器渲染 與 客戶端渲染 筆者是支援客戶端渲染的(沒錯就是欽點的) 以
使用js獲取客戶端本地ip,不需要額外引入別的檔案
網上找了好久,一直沒找到,後來翻牆谷歌找到的直接貼程式碼:<script type="text/javascript">function getUserIP(onNewIP) { // onNewIp - your listener function for n
前端開發手機端呼叫開發者工具(控制檯資訊,報錯,介面資訊等),ios安卓通用vue-cli專案中mockjs和vConsole的使用
在vue-cli專案中mockjs和vConsole的使用 mockjs使用 1.安裝npm install mockjs 2.在src/assets目錄下建立個util資料夾,並在裡新建一個檔案mock.js 如下: const Mock = require(‘m
原生js獲取客戶端本地ip
function getUserIP(onNewIP) { // onNewIp - your listener function for new IPs //compatibility for firefox and chrome var myPeerConnection = w
社交app應用開發 客戶端+伺服器原始碼
通過前段時間十來天的ios學習,前幾天抽空完成了這個APP,可能問題很多,希望大家諒解。 同時又什麼程式碼的問題,希望大家給提出建議。 本程式是仿照Appstore上的一個應用來參考做的。 程式架構,客戶端(ios)+服務端(java)+Tomcate伺服器+mysql資料庫。 由於時間有限,很多細節方面的東
(Lua) 客戶端本地時間戳和伺服器時間戳不一致的解決方案
問題來源:伺服器的時間是固定的,國內一般都設定為北京時間(東8時區的時間),而客戶端分佈在世界各地,客戶端系統設定的時區是不固定的。很多時候在設計時,沒有考慮時區不一致的情況,直接使用時間戳來進行時間轉換和比較,往往出現很多預想不到的問題。基本概念/方法:1、【時區】:全世界
客戶端資料儲存cookie、localStoeage、sessionStorage(小記)
一、資料儲存分為客戶端儲存和服務端儲存1、而對於客戶端儲存,在html5以前只能通過cookie來實現;html 5以後增加了web儲存(實際儲存本地)的功能,(1)對於web儲存有兩個標準:a、File API 標準: 支援該標準的瀏覽器能夠計算機硬碟的其他檔案中讀取資料b
前端開發移動端注意事項
一、meta的使用1、<meta name="viewport" content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
將客戶端圖片儲存到資料庫中的方法
核心內容: (1)使用到兩個函式模組(FM):SCMS_BINARY_TO_XSTRING 和 SCMS_XSTRING_TO_BINARY; (2)資料庫儲存圖片的欄位設為 RAWSTRING型別(對應於ABAP資料型別XSTRING)。 一、假設場景及前提 (1)將客戶端C盤下的“PIC.JPG”檔案儲
vue開發:生成token儲存在客戶端localStorage中
前面我們已經瞭解了可以通過localStorage在客戶端(瀏覽器)儲存資料。 回顧token 框架中的RESTful api快速領悟(中):token認證 框架中的RESTful api快速領悟(下):token的設定 我們後端有這樣一個介面: http://lo
七牛雲端儲存客戶端(本人開發,開源)
直接不廢話,開源地址:https://github.com/wjs5943283/QiNiuBucketClientQiNiu bucket Client 簡單的七牛儲存客戶端,增加了批量下載功能, 使用wpf 基於 .net framework 4.0 下載Dubug.zip直接使用,填寫AK和S
TCP客戶端圖片上傳服務端儲存本地示例
//TCP客戶端public class TCPClient { public static void main(String[] args)throws IOException { Socket socket = new Socket("127.0.0.1",8888)