1. 程式人生 > >[聊一聊系列]聊一聊前端存儲那些事兒

[聊一聊系列]聊一聊前端存儲那些事兒

我們 老朋友 必須 獲取 sql 壓力 做了 lan read

https://segmentfault.com/a/1190000005927232

歡迎大家收看聊一聊系列,這一套系列文章,可以幫助前端工程師們了解前端的方方面面(不僅僅是代碼):https://segmentfault.com/blog/frontenddriver

在web開發越來越復雜的今天,前端擁有的能力也越來越多。其中最重要的一項莫過於web存儲。開發者們如果使用得當,這些存儲可以幫助我們提升網頁的性能與靈活度。本文不講個中的細節,只講各種前端存儲的利弊,與各類存儲的應用場景。畢竟這些技術的細節在網上隨處可見,如果讀者你決定使用的話,再去細查也不遲。我們前端人手裏都有哪些存儲武器,都用在什麽地方,請讀者隨我一一聊開去......

1 老朋友cookie

1.1 是什麽?

cookie是什麽就用不著我多說了吧,可是有同學會問了,這也算存儲?當然算,它也可以存東西不是,而且它會在用戶訪問服務器的時候被帶上。但是,筆者在這裏建議,不要使用過量,因為cookie在每次請求的時候都會被帶上。你總不想每次訪問自己網站接口或者文件的時候都帶上一堆可能用不到的信息把?這樣會增大請求包的大小。

1.2 訪問限制性

眾所周知,cookie可以設置訪問域。即,如果你設置cookie的時候,設定了cookie的訪問域名為一個頂級域名,則可以達到幾個子域名共享cookie的效果。如:騰訊網與微信網頁版共享了pac_uid(如圖1.3.1與圖1.3.2)。

技術分享
圖1.2.1

技術分享
圖1.2.2

訪問的限制在種下cookie的時候指定。所以,我們可以設定cookie的訪問域名限制(當然,不能跨域啦)。有些重要信息,如用戶的唯一標識,建議給這些cookie字段加上HttpOnly標識。加上了這個標識的話,我們的客戶端js是無法讀到與寫入加了標識的cookie字段的,這樣非常安全。如果有不了解的讀者,建議百度一下"cookie httpOnly"。

1.3 存儲時長

如果設定了cookie的超時時間的話,那麽cookie將在到期的時候失效。如果沒有設定,那麽cookie就是session級別的啦。這裏請讀者們註意一下。cookie的session含義與我們接下來要講的sessionStorage的session含義有區別。cookie的session是,在未關閉瀏覽器的情況下,所有的tab級別的頁面或新開,或刷新,均屬於一個session,比如,我們打開www.qq.com,在其中種下。test=doctorhou,如圖1.3.1

document.cookie = ‘test=doctorhou‘;

技術分享
圖1.3.1

我們再刷新一下當前頁面,發現cookie還在,如圖1.4.2

技術分享
圖1.3.2

關閉種cookie的tab,我們再新開一個tab,發現cookie還是存在的,如圖1.4.3

技術分享
圖1.3.3

退出瀏覽器,再打開www.qq.com發現test=doctorhou這一項cookie已經不在了。如圖1.3.4

技術分享
圖1.3.4

證明cookie的session含義是在瀏覽器退出時才結束。

1.4 做什麽用比較好?

一般非到不得已,不要在cookie裏面存東西。如果要存儲的話。建議存儲一些同步訪問頁面的時候必須要被帶到服務端的信息。

比如,網站的用戶登錄信息。這個是在訪問時必須要在服務端獲取的信息,所以種在cookie裏面很必要。有的同學會說了,那一些用戶信息呢?比如用戶在我網站都買了什麽東西,之類的。這裏建議存儲在服務端(存在數據庫裏面,或者什麽裏面)。然後使用用戶的cookie唯一ID去數據庫中查詢。我們的目標是,沒有蛀,哦不,越少越好。

2 短暫的sessionStorage

2.1 是什麽?

sessionStorage屬於webstorage的一種,sessionStorage與我們稍後要說的localStorage類似,可以存儲k-v形式的數據,使用方法非常簡單set便可以存儲,如圖2.1.1。

sessionStorage.setItem(‘test‘, ‘doctorhou‘);

技術分享
圖2.1.1

使用sessionStorage.getItem便可以直接獲取。如圖2.1.2:

sessionStorage.getItem(‘test‘);

技術分享
圖2.1.2

顧名思義,sessionStorage,是session級別的存儲。其存儲於客戶端。服務端是無法直接拿到的。

2.2 訪問限制性

不同於cookie,sessionStorage的訪問限制更高一些,只有當前設定sessionStorage的域下才能訪問,而且不同的兩個tab之間不能互通。例,我在www.qq.com下種下了sessionStorage,在wx.qq.com下是,無法訪問的。如圖2.2.1、圖2.2.2:

技術分享
圖2.2.1

技術分享
圖2.2.2

在新開的tab下,或者關閉本TAB再打開後(也是www.qq.com),也是無法訪問到之前種的sessionStorage的。如圖2.2.3:

技術分享
圖2.2.3

而本tab刷新的時候,sessionStorage確是可以訪問的,如圖2.2.4

技術分享
圖2.2.4

以上種種,證明sessionStorage裏面的session,並不同於cookie,是以tab為級別的session。

2.4 做什麽用比較好?

既然是存儲於客戶端而且存儲級別僅僅是一個session的話,還是建議存儲一些當前頁面刷新需要存儲,且不需要在tab關閉時候留下的信息。剛剛說了,只有頁面刷新才不會清除掉sessionStorage。剩下的均會清理掉sessionStorage,當然,也許可以用sessionStorage來檢測用戶是否是刷新進入的頁面。對於音樂播放器恢復播放進度條等功能等還是挺實用的。

3 簡易強大的localStorage

3.1 是什麽?

localStorage與sessionStorage較為相似,接口也簡單,通過localStorage.setItem/localStorage.getItem即可輕松使用,如圖3.1.1。

localStorage.setItem(‘test‘, ‘doctorhou‘);
localStorage.getItem(‘test‘);

技術分享
圖3.1.1

localStorage可以存儲k-v形式的數據。存儲的值需要是字符串類型,沒法直接存儲對象,但是可以將對象序列化為字符串再存入。如果強行存入object的話,就會被調用object.toString從而悲劇。悲劇的存法,效果如圖3.1.2:

var doctorhou = {
    name: ‘doctorhou‘,
    describe: ‘高大、威猛、帥氣‘
};
localStorage.setItem(‘test‘, doctorhou);
localStorage.getItem(‘test‘);

技術分享
圖3.1.2

正確的存取方法,效果如圖3.1.3:

var doctorhou = {
    name: ‘doctorhou‘,
    describe: ‘高大、威猛、帥氣‘
};
localStorage.setItem(‘test‘, JSON.stringify(doctorhou));
JSON.parse(localStorage.getItem(‘test‘));

技術分享
圖3.1.3

localStorage的存儲周期比sessionStorage長,如果用戶不清理的話,是可以永久存儲的。

3.2 訪問的限制性

localStorage與sessionStorage雖然相似,但是訪問限制卻不盡相同,localStorage的訪問域默認設定為設置localStorage的當前域,其他域名不可以取。這點與sessionStorage相同,但是與sessionStorage不同的是,localStorage設定後,新開tab是可以訪問到的,如圖3.2.1與圖3.2.2

技術分享
圖3.2.1

技術分享
圖3.2.2

3.3 存儲時間

localStorage理論上講是永久性質的存儲。但是,免不了用戶會使用瀏覽器清除數據,或者瀏覽器有時候為了節省,去清除數據。

3.4 大小限制及檢測

localStorage的大小一般限定為4M左右,這點可以根據實際瀏覽器來測試。那麽,如何檢測自己已經消耗了多少空間呢?可以直接將localStorage序列化,看看其字節數,就可以算出其已經占用的空間了(可能會有點誤差,這樣做會把一些轉移符號算進去,可以消除掉後再算)。如圖3.4.1,我們看出了自己大約已經使用了61個字節的

JSON.stringify(localStorage)

技術分享
圖3.4.1

3.5 做什麽用比較好

由於localStorage的穩定性質,及其長效的存儲。筆者建議如果有一些數據,服務器難以承載其壓力,但又要與用戶的信息綁定的話,可以使用localStorage存儲一些狀態,這樣即能緩解服務端壓力,也可以存儲用戶的數據。當然,也有一些小技巧,可以用localStorage提高網站訪問的速度。如筆者寫的一篇淺析文章:
聊一聊百度移動端首頁前端速度那些事兒
讀者們可以嘗試使用。

4 websql與indexeddb

4.1 是什麽?

這兩位可是web存儲中的重型武器。為什麽放在一起說呢,是因為,websql的標準,官方已經不打算維護了,轉而維護了新的indexeddb,讀者可能會問了,那直接說indexeddb就好了,為啥還要說websql,因為websql雖然過時了,但是其兼容性卻出奇的好,幾乎是移動端均可用呀。我們來看一下caniuse上的兼容性數據:

websql的兼容性如圖4.1.1

技術分享
圖4.1.1

indexeddb的兼容性卻不是很好,android4.4之前以及ios7以前都無法使用。如圖4.1.2

技術分享
圖4.1.2

所以筆者建議如下,能用indexeddb的時候,就要使用indexeddb,因為其代表了未來的發展方向。如果用不了indexeddb的盡量使用websql進行代替。其實就是使用一段膩子腳本(polyfill)即可,做一下兼容。接下來我們會嘗試做一點膩子腳本。

websql更像是關系型數據庫,並且使用sql語句進行操作,效果如圖4.1.2

<!doctype html>
<html>
    <head>
        <meta charset="utf-8" />
    </head>
    <body>
        <script>
            var db = window.openDatabase(‘testDB‘, ‘1.0‘, ‘TestDb‘, 2 * 1024 * 1024);
            db.transaction(function (context) {
                context.executeSql(‘CREATE TABLE IF NOT EXISTS cubefe(id, name)‘);
                context.executeSql(‘INSERT INTO cubefe (id, name) VALUES (1, "doctorhou")‘);
            }); 
        </script>
    </body>
</html>

技術分享
圖4.1.2

indexeddb更像是nosql,直接使用js的方法操作數據即可,效果如圖4.1.3

<!doctype html>
<html>
    <head>
        <meta charset="utf-8" />
    </head>
    <body>
        <script>
            var startTime = +new Date();
            console.log(‘starttime:‘, startTime);
            function openDB(dbname, version, cb) {
                var request = window.indexedDB.open(dbname);
                request.onsuccess = function (e) {
                    var db = e.target.result;
                    myDB.db = db;
                    var version = db.version;
                    if (!db.objectStoreNames.contains(‘students‘)) {
                        request = db.createObjectStore(‘students‘, {autoIncrement: true});
                    }
                    cb && cb(e);
                };
            }   

            var myDB = {
                name: ‘test‘,
                version: 4,
                db: null
            };

            openDB(myDB.name, myDB.version, function (e) {
                var db = e.target.result;
                var storeName = ‘students‘;
                var transaction = db.transaction(storeName, ‘readwrite‘);
                var store = transaction.objectStore(storeName);
                store.put({id: 2, name: ‘doctorhou‘}, ‘b‘);
                var request = store.get(1);
                request.onsuccess = function (e) {
                    console.log(request.result);
                    var endTime = +new Date();
                    console.log(‘take-time:‘, endTime - startTime);
                };
            });

        </script>
    </body>
</html>

技術分享
圖4.1.3

4.2 訪問

indexeddb和websql在這一點上與localStorage一致,均是在創建數據庫的域名下才能訪問。而且不能指定訪問域名。

4.3 存儲時間

這兩位的存儲時間也是永久,除非用戶清除數據,可以用作長效的存儲。

4.4 大小限制

理論上講,這兩種存儲的方式是沒有大小限制的。然而indexeddb的數據庫超過50M的時候瀏覽器會彈出確認。基本上也相當於沒有限制了。

4.5 性能如何?

我這邊做了個實驗,indexeddb查詢少量數據也就花費了20MS左右。還是很快的。如圖4.5.1

技術分享
圖4.5.1

大量數據的情況下,相對耗時會變長一些,但是也就在30MS左右,也是相當給力了。如圖4.5.2所示,10W數據+,畢竟nosql。

for (var i = 0; i < 100000; i++) { 
    store.add({id: 2, name: ‘doctorhou‘});
}

技術分享
圖4.5.2

而websql這邊的效率也不錯,10w+數據,簡單查詢一下,只花費了20MS左右,如圖4.5.3所示

<script>
var startTime = +new Date();
var db = window.openDatabase(‘testDB‘, ‘1.0‘, ‘TestDb‘, 2 * 1024 * 1024);
db.transaction(function (context) {
    context.executeSql(‘CREATE TABLE IF NOT EXISTS cubefe(id, name)‘);
    /*for (var i = 0; i < 100000; i++) {
        context.executeSql(‘INSERT INTO cubefe (id, name) VALUES (i, "doctorhou")‘);
    }*/
    context.executeSql(‘SELECT * FROM cubefe WHERE rowid="99999"‘, [], function (tx, results) {    
        console.log(results);
        console.log(‘take-time‘, (+new Date()) - startTime);
    }, null);
}); 
</script>

技術分享
圖4.5.3

4.6 拿來幹什麽用?

當我們是在做一個離線應用,或者webapp的時候,可以考慮使用本地數據庫中存取數據。如果不存大量的數據的話,其實localStorage就夠用了。亦或者,你想把一張用戶的皮膚圖片之類的大量數據存入客戶端緩存起來,localStorage已經不夠用了的話,也可以嘗試一下websql與indexeddb。

接下來的一篇文章,我將會和讀者們一起聊聊前端調試那些事兒,不要走開,請關註我.....

https://segmentfault.com/a/1190000005964730

如果喜歡本文請點擊下方的推薦哦,你的推薦會變為我繼續更文的動力。

以上內容僅代表筆者個人觀點,如有意見筆者願意學習參考各讀者的建議。

[聊一聊系列]聊一聊前端存儲那些事兒