首頁 > web前端 > H5教程 > 主體

了解瀏覽器中的不同儲存類型

青灯夜游
發布: 2020-11-24 17:52:00
轉載
3945 人瀏覽過

了解瀏覽器中的不同儲存類型

在後端開發中,儲存是工作的常見部分。應用程式資料儲存在資料庫中,檔案儲存在物件儲存中,瞬態資料儲存在快取中…似乎存在無限種儲存任何類型資料的可能性。但是,資料儲存不僅限於後端,前端(瀏覽器)還具有許多儲存資料的選項。我們可以透過利用這種儲存方式來提升我們的應用程式效能,保存使用者的偏好,在多個會話,甚至不同的電腦上保持應用狀態。

在本文中,我們將透過不同的可能性在瀏覽器中儲存資料。我們將涵蓋每種方法的三個用例,以掌握其優缺點。最後,你將能夠決定什麼儲存空間是最適合你的用例。

讓我們開始吧!

localStorage API

localStorage 是瀏覽器中最受歡迎的儲存選項之一,也是許多開發人員的首選。資料跨會話存儲,從不與伺服器共用,並可用於相同協定和網域下的所有頁面。儲存空間限制為〜5MB。

令人驚訝的是,GoogleChrome團隊並不建議使用這個選項,因為它封鎖了主線程,而且web workers和service workers無法存取。他們推出了一個實驗:KV Storage,作為一個更好的版本,但這只是一個試驗,似乎還沒有任何進展。

localStorage API 可作為 window.localStorage 使用,並且只能保存UTF-16字串。在將資料儲存到 localStorage 之前,我們必須確保將其轉換為字串。主要的三個功能是:

  • setItem('key', 'value')
  • getItem(' key')
  • removeItem('key')

#它們都是同步的,因此使用起來很簡單,但是它們會阻塞主線程。

值得一提的是,localStorage 有一個稱為 sessionStorage 的雙胞胎。唯一的區別是,儲存在 sessionStorage 中的資料將僅持續當前會話,但API相同。

這個太簡單了,相信大家都用過。

IndexedDB API

IndexedDB是瀏覽器中的現代儲存解決方案。它可以儲存大量的結構化數據,甚至文件和Blob。和每一個資料庫一樣,IndexedDB會對資料進行索引,以便有效率地執行查詢。使用IndexedDB比較複雜,我們必須建立一個資料庫,表,並使用事務。

localStorage 相比,IndexedDB需要更多程式碼。在範例中,我使用了原生API與Promise包裝器,但我強烈建議使用第三方函式庫來幫助你。我推薦的是localForage,因為它使用了相同的localStorage API,但實作方式是逐步增強的,也就是說,如果你的瀏覽器支援IndexedDB,就會使用它;如果不支持,就會退回到localStorage

讓我們來寫程式碼,前往我們的使用者偏好範例吧!

<input>
<label>Dark theme</label><br>
登入後複製
let db;

function toggle(on) {
  if (on) {
    document.documentElement.classList.add('dark'); 
  } else {
    document.documentElement.classList.remove('dark');    
  }
}

async function save(on) {
  const tx = db.transaction('preferences', 'readwrite');
  const store = tx.objectStore('preferences');
  store.put({key: 'darkTheme', value: on});
  return tx.complete;
}

async function load() {
  const tx = db.transaction('preferences', 'readonly');
  const store = tx.objectStore('preferences');
  const data = await store.get('darkTheme');
  return data && data.value;
}

async function onChange(checkbox) {
  const value = checkbox.checked;
  toggle(value);
  await save(value);
}

function openDatabase() {
  return idb.openDB('my-db', 1, {
    upgrade(db) {
      db.createObjectStore('preferences', {keyPath: 'key'});
    },
  });
}

openDatabase()
  .then((_db) => {
    db = _db;
    return load();
  })
  .then((initialValue) => {
    toggle(initialValue);
    document.querySelector('#darkTheme').checked = initialValue;
  });
登入後複製

效果

了解瀏覽器中的不同儲存類型

idb 是我們使用的Promise包裝器,而不是使用基於事件的低階API。首先要注意的是,對資料庫的每次存取都是非同步的,這意味著我們不會阻塞主線程,與 localStorage 相比,這是一個主要優勢。

我們需要打開與資料庫的連接,以便在整個應用程式中都可以使用它進行讀寫。我們給資料庫起一個名字 my-db,一個模式版本 1,以及一個更新函數,以在版本之間應用更改,這與資料庫遷移非常相似。我們的資料庫架構很簡單:只有一個object store preferences。 object store 等效於SQL表,要寫入或讀取資料庫,必須使用事務,這是使用IndexedDB的乏味部分。看看示範中新的 saveload 功能。

毫無疑問,IndexedDB具有更多的開銷,並且與 localStorage 相比,學習曲線更陡峭。對於鍵值的情況,使用 localStorage 或第三方函式庫可能更有意義,它們將幫助我們提高效率。

<p>loading...</p>
登入後複製
let db;

async function loadPokemons() {
  const res = await fetch('https://pokeapi.co/api/v2/pokemon?limit=10');
  const data = await res.json();
  return data.results;
}

function removeLoading() {
  const elem = document.querySelector('#loading');
  if (elem) {
    elem.parentNode.removeChild(elem); 
  }
}

function appendPokemon(pokemon) {
  const node = document.createElement('li');
  const textnode = document.createTextNode(pokemon.name);
  node.appendChild(textnode);
  document.querySelector('#list').appendChild(node);
}

function clearList() {
  const list = document.querySelector('#list');
  while (list.firstChild) {
    list.removeChild(list.lastChild);
  }
}

function saveToCache(pokemons) {
  const tx = db.transaction('pokemons', 'readwrite');
  const store = tx.objectStore('pokemons');
  pokemons.forEach(pokemon => store.put(pokemon));
  return tx.complete;
}

function loadFromCache() {
  const tx = db.transaction('pokemons', 'readonly');
  const store = tx.objectStore('pokemons');
  return store.getAll();
}

function openDatabase() {
  return idb.openDB('my-db2', 1, {
    upgrade(db) {
      db.createObjectStore('pokemons', {keyPath: 'name'});
    },
  });
}

openDatabase()
  .then((_db) => {
    db = _db;
    return loadFromCache();
  })
  .then((cachedPokemons) => {
    if (cachedPokemons) {
      removeLoading();
      cachedPokemons.forEach(appendPokemon);
      console.log('loaded from cache!');
    }
    return loadPokemons();
  })
  .then((pokemons) => {
    removeLoading();
    saveToCache(pokemons);
    clearList();
    pokemons.forEach(appendPokemon);
    console.log('loaded from network!');
  });
登入後複製

效果

了解瀏覽器中的不同儲存類型

你可以在此資料庫中儲存數百兆甚至更多。您可以將所有Pokémon儲存在IndexedDB中,並使其離線甚至建立索引!這絕對是用於儲存應用程式資料的一種選擇。

我跳過了第三個範例的實現,因為與 localStorage 相比,IndexedDB在這種情況下沒有任何區別。即使使用 IndexedDB,使用者仍然不會與他人分享所選頁面,也不會將其作為書籤供將來使用。它們都不適合這個用例。

Cookies

使用cookies是一种独特的存储方式,这是唯一的与服务器共享的存储方式。Cookies作为每次请求的一部分被发送。它可以是当用户浏览我们的应用程序中的页面或当用户发送Ajax请求时。这样我们就可以在客户端和服务器之间建立一个共享状态,也可以在不同子域的多个应用程序之间共享状态。本文中介绍的其他存储选项无法实现。需要注意的是:每个请求都会发送 cookie,这意味着我们必须保持 cookie 较小,以保持适当的请求大小。

Cookies的最常见用途是身份验证,这不在本文的讨论范围之内。就像 localStorage 一样,cookie只能存储字符串。这些cookie被连接成一个以分号分隔的字符串,并在请求的cookie头中发送。你可以为每个cookie设置很多属性,比如过期、允许的域名、允许的页面等等。

在例子中,我展示了如何通过客户端来操作cookie,但也可以在你的服务器端应用程序中改变它们。

<input>
<label>Dark theme</label>
登入後複製
登入後複製
function getCookie(cname) {
  const name = cname + '=';
  const decoded = decodeURIComponent(document.cookie);
  const split = decoded.split(';');
  const relevantCookie = split.find((cookie) => cookie.indexOf(`${cname}=`) === 0);
  if (relevantCookie) {
    return relevantCookie.split('=')[1];
  }
  return null;
}

function toggle(on) {
  if (on) {
    document.documentElement.classList.add('dark'); 
  } else {
    document.documentElement.classList.remove('dark');    
  }
}

function save(on) {
  document.cookie = `dark_theme=${on.toString()}; max-age=31536000; SameSite=None; Secure`;
}

function load() {
  return getCookie('dark_theme') === 'true';
}

function onChange(checkbox) {
  const value = checkbox.checked;
  toggle(value);
  save(value);
}

const initialValue = load();
toggle(initialValue);
document.querySelector('#darkTheme').checked = initialValue;
登入後複製

效果还是跟前面一样

了解瀏覽器中的不同儲存類型

将用户的喜好保存在cookie中,如果服务器能够以某种方式利用它,就可以很好地满足用户的需求。例如,在主题用例中,服务器可以交付相关的CSS文件,并减少潜在的捆绑大小(在我们进行服务器端渲染的情况下)。另一个用例可能是在没有数据库的情况下,在多个子域应用之间共享这些偏好。

用JavaScript读写cookie并不像您想象的那么简单。要保存新的cookie,您需要设置 document.cookie ——在上面的示例中查看 save 函数。我设置了 dark_theme cookie,并给它添加了一个 max-age 属性,以确保它在关闭标签时不会过期。另外,我添加 SameSiteSecure 属性。这些都是必要的,因为CodePen使用iframe来运行这些例子,但在大多数情况下你并不需要它们。读取一个cookie需要解析cookie字符串。

Cookie字符串如下所示:

key1=value1;key2=value2;key3=value3
登入後複製

因此,首先,我们必须用分号分隔字符串。现在,我们有一个形式为 key1=value1 的Cookie数组,所以我们需要在数组中找到正确的元素。最后,我们将等号分开并获得新数组中的最后一个元素。有点繁琐,但一旦你实现了 getCookie 函数(或从我的例子中复制它:P),你就可以忘记它。

将应用程序数据保存在cookie中可能是个坏主意!它将大大增加请求的大小,并降低应用程序性能。此外,服务器无法从这些信息中获益,因为它是数据库中已有信息的陈旧版本。如果你使用cookies,请确保它们很小。

分页示例也不适合cookie,就像 localStorageIndexedDB 一样。当前页面是我们想要与他人共享的临时状态,这些方法都无法实现它。

URL storage

URL本身并不是存储设备,但它是创建可共享状态的好方法。实际上,这意味着将查询参数添加到当前URL中,这些参数可用于重新创建当前状态。最好的例子是搜索查询和过滤器。如果我们在CSS-Tricks上搜索术语flexbox,则URL将更新为https://css-tricks.com/?s=fle...。看看我们使用URL后,分享搜索查询有多简单?另一个好处是,你只需点击刷新按钮,就可以获得更新的查询结果,甚至可以将其收藏。

我们只能在URL中保存字符串,它的最大长度是有限的,所以我们没有那么多的空间。我们将不得不保持我们的状态小,没有人喜欢又长又吓人的网址。

同样,CodePen使用iframe运行示例,因此您看不到URL实际更改。不用担心,因为所有的碎片都在那里,所以你可以在任何你想要的地方使用它。

<input>
<label>Dark theme</label>
登入後複製
登入後複製
function toggle(on) {
  if (on) {
    document.documentElement.classList.add('dark'); 
  } else {
    document.documentElement.classList.remove('dark');    
  }
}

function save(on) {
  const params = new URLSearchParams(window.location.search);
  params.set('dark_theme', on.toString());
  history.pushState(null, null, `?${params.toString()}`);
}

function load() {
  const params = new URLSearchParams(window.location.search);
  return params.get('dark_theme') === 'true';
}

function onChange(checkbox) {
  const value = checkbox.checked;
  toggle(value);
  save(value);
}

const initialValue = load();
toggle(initialValue);
document.querySelector('#darkTheme').checked = initialValue;
登入後複製

效果还是一样

了解瀏覽器中的不同儲存類型

我们可以通过 window.location.search 访问查询字符串,幸运的是,可以使用 URLSearchParams 类对其进行解析,无需再应用任何复杂的字符串解析。当我们想读取当前值时,可以使用 get 函数,当我们想写时,可以使用 set。仅设置值是不够的,我们还需要更新URL。这可以使用 history.pushStatehistory.replaceState 来完成,取决于我们想要完成的行为。

我不建议将用户的偏好保存在URL中,因为我们必须将这个状态添加到用户访问的每一个URL中,而且我们无法保证;例如,如果用户点击了谷歌搜索的链接。

就像Cookie一样,由于空间太小,我们无法在URL中保存应用程序数据。而且即使我们真的设法存储它,网址也会很长,而且不吸引人点击。可能看起来像是钓鱼攻击的一种。

<p>Select page:</p>
<p>
  <button>0</button>
  <button>1</button>
  <button>3</button>
  <button>4</button>
  <button>5</button>
</p>
登入後複製
async function loadPokemons(page) {
  const res = await fetch(`https://pokeapi.co/api/v2/pokemon?limit=10&offset=${page * 10}`);
  const data = await res.json();
  return data.results;
}

function appendPokemon(pokemon) {
  const node = document.createElement('li');
  const textnode = document.createTextNode(pokemon.name);
  node.appendChild(textnode);
  document.querySelector('#list').appendChild(node);
}

function clearList() {
  const list = document.querySelector('#list');
  while (list.firstChild) {
    list.removeChild(list.lastChild);
  }
}

function savePage(page) {
  const params = new URLSearchParams(window.location.search);
  params.set('page', page.toString());
  history.pushState(null, null, `?${params.toString()}`);
}

function loadPage() {
  const params = new URLSearchParams(window.location.search);
  if (params.has('page')) {
    return parseInt(params.get('page'));
  }
  return 0;
}

async function updatePage(page) {
  clearList();
  savePage(page);
  const pokemons = await loadPokemons(page);
  pokemons.forEach(appendPokemon);
}

const page = loadPage();
updatePage(page);
登入後複製

效果

了解瀏覽器中的不同儲存類型

就像我们的分页例子一样,临时应用状态是最适合URL查询字符串的。同样,你无法看到URL的变化,但每次点击一个页面时,URL都会以 ?page=x 查询参数更新。当网页加载时,它会查找这个查询参数,并相应地获取正确的页面。现在,我们可以把这个网址分享给我们的朋友,让他们可以享受我们最喜欢的神奇宝贝。

Cache API

Cache API是网络级的存储,它用于缓存网络请求及其响应。Cache API非常适合service worker,service worker可以拦截每一个网络请求,使用 Cache API 它可以轻松地缓存这两个请求。service worker也可以将现有的缓存项作为网络响应返回,而不是从服务器上获取。这样,您可以减少网络负载时间,并使你的应用程序即使处于脱机状态也能正常工作。最初,它是为service worker创建的,但在现代浏览器中,Cache API也可以在窗口、iframe和worker上下文中使用。这是一个非常强大的API,可以极大地改善应用的用户体验。

就像IndexedDB一样,Cache API的存储不受限制,您可以存储数百兆字节,如果需要甚至可以存储更多。API是异步的,所以它不会阻塞你的主线程,而且它可以通过全局属性 caches 来访问。

Browser extension

如果你建立一个浏览器扩展,你有另一个选择来存储你的数据,我在进行扩展程序daily.dev时发现了它。如果你使用Mozilla的polyfill,它可以通过 chrome.storagebrowser.storage 获得。确保在你的清单中申请一个存储权限以获得访问权。

有两种类型的存储选项:local和sync。local存储是不言而喻的,它的意思是不共享,保存在本地。sync存储是作为谷歌账户的一部分同步的,你在任何地方用同一个账户安装扩展,这个存储都会被同步。两者都有相同的API,所以如果需要的话,来回切换超级容易。它是异步存储,因此不会像 localStorage 这样阻塞主线程。不幸的是,我不能为这个存储选项创建一个演示,因为它需要一个浏览器扩展,但它的使用非常简单,几乎和 localStorage 一样。有关确切实现的更多信息,请参阅Chrome文档。

结束

浏览器有许多选项可用于存储数据。根据Chrome团队的建议,我们的首选存储应该是IndexedDB,它是异步存储,有足够的空间来存储我们想要的任何东西。不鼓励使用 localStorage,但它比 IndexedDB 更易于使用。Cookies是与服务器共享客户端状态的一种好方法,但通常用于身份验证。

如果你想创建具有可共享状态的页面,如搜索页面,请使用URL的查询字符串来存储这些信息。最后,如果你建立一个扩展,一定要阅读关于 chrome.storage

更多编程相关知识,请访问:编程视频!!

以上是了解瀏覽器中的不同儲存類型的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:segmentfault.com
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!