淺析localStorage存儲的字符編碼、存儲大小限制及單位、鍵值是否占空間、鍵值數量是否影響讀寫性能、如何統計localStorage已使用空間、如何用iframe擴容、如何跨域傳遞數據


  localStorage 存儲我們經常使用,但是你有沒有深入思考下面這些問題呢?

(1)localStorage 存儲的鍵值采用什么字符編碼

(2)5M 的單位是什么

(3)localStorage 鍵占不占存儲空間

(4)localStorage的鍵的數量,對寫和讀性能的影響

(5)寫個方法統計一個localStorage已使用空間

一、localStorage  存儲的鍵值采用什么字符編碼?

  我們先看 MDN 的描述:The keys and the values stored with localStorage are always in the UTF-16 `DOMString`[2] format, which uses two bytes per character. As with objects, integer keys are automatically converted to strings.

  翻譯成中文:localStorage 存儲的鍵和值始終采用 UTF-16 DOMString 格式,每個字符使用兩個字節。與對象一樣,整數鍵將自動轉換為字符串。

  答案:UTF-16

  MDN這里描述的沒有問題,也有問題,因為UTF-16,每個字符使用兩個字節,是有前提條件的,就是碼點小於0xFFFF(65535), 大於這個碼點的是四個字節。這是全文的關鍵。

二、5M 的單位是什么

  5M的單位是什么?選項:
  1. 字符的個數
  2. 字節數
  3. 字符的長度值
  4. bit 數
  5. utf-16編碼單元
  以前不知道,現代瀏覽器,准確的應該是 選項3,字符的長度值 ,亦或 選項5, utf-16編碼單元
  字符的個數,並不等於字符的長度,這一點要知道:
"a".length // 1 
"".length // 1 
"𠮷".length // 2 
"🔴".length // 2

  現代瀏覽器對字符串的處理是基於UTF-16 `DOMString`。但是說5M字符串的長度,顯然有那么點怪異。而根據 UTF-16編碼規則,要么2個字節,要么4 個字節,所以不如說是 10M 的字節數,更為合理。當然,2 個字節作為一個 utf-16 的字符編碼單元,也可以說是 5M 的 utf-16 的編碼單元。
  現代瀏覽器的情況下:
  所以,說是10M 的字節數,更為准確,也更容易讓人理解。
  如果說5M,那其單位就是字符串的長度,而不是字符數。
  答案:字符串的長度值, 或者utf-16的編碼單元。更為合理的答案是 10M 的字節空間。

三、localStorage 鍵占不占存儲空間

  我們把 key 和 value 各設為 2.5M 的長度,執行正常

const charTxt = "a"; let count = (2.5 * 1024 * 1024); let content = new Array(count).fill(charTxt).join(""); const key = new Array(count).fill(charTxt).join(""); localStorage.clear(); try { console.time("setItem") localStorage.setItem(key, content); console.timeEnd("setItem") } catch (err) { console.log("err code:", err.code); console.log("err message:", err.message) }

  然后把 vaule 的長度加 1,再存儲,發現執行失敗,存儲異常,說明 key 和 value 一起占空間

四、localStorage的鍵的數量,對寫和讀性能的影響

  設置 500*1000 個鍵

let keyCount = 500 * 1000; localStorage.clear(); for (let i = 0; i < keyCount; i++) { localStorage.setItem(i, ""); } setTimeout(() => { console.time("save_cost"); localStorage.setItem("a", "1"); console.timeEnd("save_cost"); }, 2000) setTimeout(() => { console.time("read_cost"); localStorage.getItem("a"); console.timeEnd("read_cost"); }, 2000) // save_cost: 0.05615234375 ms // read_cost: 0.008056640625 ms
// 單獨執行保存代碼:
localStorage.clear(); console.time("save_cost"); localStorage.setItem("a", "1"); console.timeEnd("save_cost"); // save_cost: 0.033203125 ms

  key 很多,影響是有的,但是影響不大。

  反過來,如果保存的值特別大

const charTxt = "a"; const count = 5 * 1024 * 1024  - 1
const val1 = new Array(count).fill(charTxt).join(""); setTimeout(() =>{ localStorage.clear(); console.time("save_cost_1"); localStorage.setItem("a", val1); console.timeEnd("save_cost_1"); },1000) setTimeout(() =>{ localStorage.clear(); console.time("save_cost_2"); localStorage.setItem("a", "a"); console.timeEnd("save_cost_2"); },1000) // save_cost_1: 12.276123046875 ms // save_cost_2: 0.010009765625 ms

  可以多次測試,看到:單次值的大小對存的性能影響非常大,讀取也是一樣。所以盡量不要保存太大的值。

  答案:鍵的數量對讀取性能有影響,但影響不大;值的大小對性能影響更大,不建議保存大的數據

五、寫個方法統計一個localStorage已使用空間

function sieOfLS() { return Object.entries(localStorage).map(v => v.join('')).join('').length; }

  將 obj 的 key 和 value 轉換成了 [ [key, value], [key, value] ],然后 map 將 key 和 value 拼接,計算 length 就為已使用的空間

六、使用iframe給頁面的localStorage擴容

  瀏覽器提供的 localStorage 本地存儲的最大空間是5M,如果不夠用呢,這時候就需要考慮來給localStorage擴容。可看這篇文章:https://www.cnblogs.com/cherishSmile/p/8390754.html

  思路如下:

(1)在【A域】下引入【B域】,【A域】空間足夠時,讀寫由【A域】來完成,數據存在【A域】下;當【A域】空間不夠時,讀寫由【B域】來完成,數據存在【B域】下

(2)【A域】空間不夠需要在【B域】讀寫時,通過postMessage 向【B域】發送跨域消息,【B域】監聽跨域消息,在接到指定的消息時進行讀寫操作

(3)【B域】接到跨域消息時,如果是寫入刪除可以不做什么,如果是讀取,就要先讀取本域本地數據通過postMessage向父頁面發送消息

(4)【A域】在讀取【B域】數據時就需要監聽來自【B域】的跨域消息

  注意事項:

(1)window.postMessage()方法,向【B域】發消息,應用window.frames[0].postMessage() 這樣 iframe 內的【B域】才可以接到

(2)同理,【B域】向 【A域】發消息時應用,window.parent.postMessage()

(3)【A域】的邏輯一定要在 iframe 加載完成后進行

  具體代碼流程如下:

1、【A域】的頁面如下:index.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
    <title></title>
    </head>
    <body>
        <button class="set">存儲</button>
        <button class="get">讀取</button>
        <button class="remove">刪除</button>
        <iframe src="http://localhost:8000/storage.html"></iframe>   //嵌入【B域】的一個空頁面
    </body>
    <script src="js/localStorage.js"></script>
</html>

  由於需要判斷【A域】的空間是否足夠,所以需要計算【A域】已經被占用的空間。那么localStorage中的字符串以什么編碼格式存儲的呢?經過上面介紹,我們知道

localStorage中的字符串是以utf-16進行編碼的。

2、【B域】的頁面如下:storage.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title></title>
</head>
<body>
</body>
<script type="text/javascript">
    var fn = function(){}; fn.prototype = { setLocal:function(key,value){ localStorage.setItem(key,value); }, getLocal:function(key){ return localStorage.getItem(key); }, removeLocal:function(key){ localStorage.removeItem(key); }, bindEvent:function(){ var self = this; //監聽
            window.addEventListener('message', function(e){ if(window.parent!=e.source) return; var option = JSON.parse(e.data); if(option.type.toLowerCase()=="get"){ var data = self.getLocal(option.key); window.parent.postMessage(data,'*'); } option.type.toLowerCase()=="set"&&self.setLocal(option.key,option.value); option.type.toLowerCase()=="remove"&&self.removeLocal(option.key); }) }, init:function(){ var self = this; self.bindEvent(); } } var tools = new fn(); tools.init(); </script>

七、如何使用 postMessage+iframe 實現跨域的數據讀取

1、postMessage(data, origin) 方法允許來自不同源的腳本采用異步方式進行通信,可以實現跨文本檔、多窗口、跨域消息傳遞。接受兩個參數:

  data:要傳遞的數據,可以是JavaScript的任意基本類型或可復制的對象,然而並不是所有瀏覽器支持任意類型的參數,部分瀏覽器只能處理字符串參數,所以在傳遞參數時需要使用JSON.stringify()方法對對象參數序列化。

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

2、解決思路

  通過postMessage 向【其他域】發送跨域消息;

  window.parent.postMessage()

  iframe.contentWindow.postMessage()

  監聽跨域消息 window.addEventListener('message', fn );

//【test1域下】 http://www.test1.com/index_a.html
<body>
    <h2>Status</h2>
    <p></p>
    <a href="http://www.test2.com/index_b.html">去index_b查看結果</a>
    <iframe src="http://www.test2.com/getmessage.html" frameborder="0"></iframe>
    <script> window.onload = function(){  //在頁面加載完成后主頁面向iframe發送請求
            window.frames[0].postMessage('jogging, reading and writing', 'http://www.test2.com'); } window.addEventListener('message', function(e){ // 主頁面監聽message事件
            var data = e.data; document.querySelector('p').innerHTML = data; }, false); </script>
</body>

// 【test2域下】 http://www.test2.com/getmessage.html
<body>
    <script> window.addEventListener('message', function(e) {  //iframe接收消息,並把當前狀態發送給主頁面 
            if (e.source != window.parent) return; console.log(e.data); localStorage.setItem('task', e.data); window.parent.postMessage('finished', '*'); }, false); </script>
</body>

// 【test2域下】http://www.test2.com/index_b.html
<body>
    <div>點擊獲取任務</div>
    <p></p>
    <script> document.querySelector('div').addEventListener('click', function(){ document.querySelector('p').innerHTML = localStorage.getItem('task'); }, false); </script>
</body>

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM