ホームページ  >  記事  >  ウェブフロントエンド  >  ブラウザのさまざまなストレージ タイプについて学ぶ

ブラウザのさまざまなストレージ タイプについて学ぶ

青灯夜游
青灯夜游転載
2020-11-24 17:52:003902ブラウズ

ブラウザのさまざまなストレージ タイプについて学ぶ

バックエンド開発では、ストレージは仕事の一般的な部分です。アプリケーション データはデータベースに保存され、ファイルはオブジェクト ストレージに保存され、一時データはキャッシュに保存されます...あらゆる種類のデータを保存する可能性は無限にあるように見えます。ただし、 データ ストレージはバックエンド に限定されず、フロントエンド (ブラウザ) にもデータを保存するための多くのオプションがあります。このストレージ方法を利用することでアプリケーションのパフォーマンスを向上させ、ユーザー設定を保存し、複数のセッションや異なるコンピューター間でもアプリケーションの状態を維持できます。

この記事では、ブラウザーにデータを保存するためのさまざまな可能性について説明します。各アプローチの長所と短所を理解するために、それぞれの 3 つの使用例を取り上げます。最終的には、自分のユースケースに最適なストレージを決定できるようになります。 ######はじめましょう!

localStorage API

localStorage

は、ブラウザーで最も人気のあるストレージ オプションの 1 つであり、多くの開発者にとって最初の選択肢です。データはセッション間で保存され、サーバーと共有されることはなく、同じプロトコルとドメイン内のすべてのページで利用できます。ストレージ制限は最大 5MB です。 驚くべきことに、Google Chrome チームは、メインスレッドをブロックし、Web ワーカーやサービス ワーカーにアクセスできなくなるため、このオプションの使用を推奨していません。彼らは、より良いバージョンとして KV Storage という実験を開始しましたが、これは単なる実験であり、まだ進歩はないようです。

localStorage API

window.localStorage として利用可能で、UTF-16 文字列のみを保存できます。データを localStorage に保存する前に、データが文字列に変換されていることを確認する必要があります。主な 3 つの関数は次のとおりです。

    setItem('key',
  • 'value')
  • getItem(' key ')
  • removeItem('key')
  • これらはすべて同期しているため、使い方は簡単ですが、メインの糸。

localStorage

には sessionStorage というツインがあることに言及する価値があります。唯一の違いは、sessionStorage に保存されたデータは現在のセッションの間のみ保持されることですが、API は同じです。 これはシンプルすぎるので、誰もが使ったことがあると思います。

IndexedDB API

IndexedDB は、ブラウザーの最新のストレージ ソリューションです。ファイルや BLOB も含め、大量の構造化データを保存できます。他のデータベースと同様に、IndexedDB はデータにインデックスを付けてクエリを効率的に実行します。 IndexedDB の使用はさらに複雑で、データベース、テーブルを作成し、トランザクションを使用する必要があります。

localStorage

と比較して、IndexedDB にはより多くのコードが必要です。この例では、Promise ラッパーを使用してネイティブ API を使用しましたが、役立つサードパーティ ライブラリを使用することを強くお勧めします。私がお勧めするのは localForage です。これは同じ localStorage API を使用するためですが、実装は徐々に強化されています。つまり、ブラウザが IndexedDB をサポートしている場合はそれが使用され、そうでない場合は # にフォールバックします。 ##ローカルストレージ###。 コードを書いて、ユーザー設定の例に進みましょう。 <pre class="brush:php;toolbar:false">&lt;input&gt; &lt;label&gt;Dark theme&lt;/label&gt;&lt;br&gt;</pre> <pre class="brush:php;toolbar:false">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 &amp;&amp; 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) =&gt; {     db = _db;     return load();   })   .then((initialValue) =&gt; {     toggle(initialValue);     document.querySelector('#darkTheme').checked = initialValue;   });</pre>Effect

idbブラウザのさまざまなストレージ タイプについて学ぶ は、低レベルのイベントベース API を使用する代わりに使用する Promise ラッパーです。まず注意すべきことは、データベースへのすべてのアクセスが非同期であるということです。つまり、メイン スレッドがブロックされないということです。これは、

localStorage

と比較して大きな利点です。 アプリケーション全体で読み取りと書き込みにデータベースを使用できるように、データベースへの接続を開く必要があります。データベースに名前 my-db、スキーマ バージョン

1

、およびデータベースの移行と同様に、バージョン間の変更を適用する更新関数を付けます。私たちのデータベース アーキテクチャは非常にシンプルです。オブジェクト ストア preferences は 1 つだけです。オブジェクト ストアは SQL テーブルに相当します。データベースに書き込んだりデータベースから読み取ったりするには、トランザクションを使用する必要があります。これは、IndexedDB を使用する際の面倒な部分です。デモで新しい save および load 機能を確認してください。 IndexedDB は、localStorage に比べてオーバーヘッドが大きく、学習曲線が急であることは疑いの余地がありません。キーと値の場合は、

localStorage

またはサードパーティ ライブラリを使用する方が効率的になる可能性があります。 <pre class="brush:php;toolbar:false">&lt;p&gt;loading...&lt;/p&gt; </pre> <ul> </ul> <pre class="brush:php;toolbar:false">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 =&gt; 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) =&gt; {     db = _db;     return loadFromCache();   })   .then((cachedPokemons) =&gt; {     if (cachedPokemons) {       removeLoading();       cachedPokemons.forEach(appendPokemon);       console.log('loaded from cache!');     }     return loadPokemons();   })   .then((pokemons) =&gt; {     removeLoading();     saveToCache(pokemons);     clearList();     pokemons.forEach(appendPokemon);     console.log('loaded from network!');   });</pre>効果

このデータベースには数百メガバイト以上を保存できます。すべてのポケモンを IndexedDB に保存し、オフラインにしてインデックスを付けることもできます。これは間違いなくアプリケーション データを保存するためのオプションです。

ブラウザのさまざまなストレージ タイプについて学ぶ3 番目の例の実装はスキップしました。この場合、IndexedDB は

localStorage

と比べて違いがありません。 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 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はsegmentfault.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。