バックエンド開発では、ストレージは仕事の一般的な部分です。アプリケーション データはデータベースに保存され、ファイルはオブジェクト ストレージに保存され、一時データはキャッシュに保存されます...あらゆる種類のデータを保存する可能性は無限にあるように見えます。ただし、 データ ストレージはバックエンド に限定されず、フロントエンド (ブラウザ) にもデータを保存するための多くのオプションがあります。このストレージ方法を利用することでアプリケーションのパフォーマンスを向上させ、ユーザー設定を保存し、複数のセッションや異なるコンピューター間でもアプリケーションの状態を維持できます。
この記事では、ブラウザーにデータを保存するためのさまざまな可能性について説明します。各アプローチの長所と短所を理解するために、それぞれの 3 つの使用例を取り上げます。最終的には、自分のユースケースに最適なストレージを決定できるようになります。 ######はじめましょう!
localStorage API
localStorage は、ブラウザーで最も人気のあるストレージ オプションの 1 つであり、多くの開発者にとって最初の選択肢です。データはセッション間で保存され、サーバーと共有されることはなく、同じプロトコルとドメイン内のすべてのページで利用できます。ストレージ制限は最大 5MB です。 驚くべきことに、Google Chrome チームは、メインスレッドをブロックし、Web ワーカーやサービス ワーカーにアクセスできなくなるため、このオプションの使用を推奨していません。彼らは、より良いバージョンとして KV Storage という実験を開始しましたが、これは単なる実験であり、まだ進歩はないようです。
は window.localStorage
として利用可能で、UTF-16 文字列のみを保存できます。データを localStorage
に保存する前に、データが文字列に変換されていることを確認する必要があります。主な 3 つの関数は次のとおりです。
'value')
には sessionStorage
というツインがあることに言及する価値があります。唯一の違いは、sessionStorage
に保存されたデータは現在のセッションの間のみ保持されることですが、API は同じです。 これはシンプルすぎるので、誰もが使ったことがあると思います。
IndexedDB API
と比較して、IndexedDB にはより多くのコードが必要です。この例では、Promise ラッパーを使用してネイティブ API を使用しましたが、役立つサードパーティ ライブラリを使用することを強くお勧めします。私がお勧めするのは localForage です。これは同じ localStorage
API を使用するためですが、実装は徐々に強化されています。つまり、ブラウザが IndexedDB をサポートしている場合はそれが使用され、そうでない場合は # にフォールバックします。 ##ローカルストレージ###。 コードを書いて、ユーザー設定の例に進みましょう。
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false"><input>
<label>Dark theme</label><br></pre><div class="contentsignin">ログイン後にコピー</div></div>
<div class="code" style="position:relative; padding:0px; margin:0px;"><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 && 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;
});</pre><div class="contentsignin">ログイン後にコピー</div></div>
Effect
idb は、低レベルのイベントベース API を使用する代わりに使用する Promise ラッパーです。まず注意すべきことは、データベースへのすべてのアクセスが非同期であるということです。つまり、メイン スレッドがブロックされないということです。これは、
localStorage と比較して大きな利点です。 アプリケーション全体で読み取りと書き込みにデータベースを使用できるように、データベースへの接続を開く必要があります。データベースに名前
my-db
、スキーマ バージョン
、およびデータベースの移行と同様に、バージョン間の変更を適用する更新関数を付けます。私たちのデータベース アーキテクチャは非常にシンプルです。オブジェクト ストア preferences
は 1 つだけです。オブジェクト ストアは SQL テーブルに相当します。データベースに書き込んだりデータベースから読み取ったりするには、トランザクションを使用する必要があります。これは、IndexedDB を使用する際の面倒な部分です。デモで新しい save
および load
機能を確認してください。 IndexedDB は、
localStorage
に比べてオーバーヘッドが大きく、学習曲線が急であることは疑いの余地がありません。キーと値の場合は、
またはサードパーティ ライブラリを使用する方が効率的になる可能性があります。 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false"><p>loading...</p>
</pre><div class="contentsignin">ログイン後にコピー</div></div>
<ul>
</ul>
<div class="code" style="position:relative; padding:0px; margin:0px;"><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 => 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!');
});</pre><div class="contentsignin">ログイン後にコピー</div></div>
効果
3 番目の例の実装はスキップしました。この場合、IndexedDB は
localStorageと比べて違いがありません。 IndexedDB を使用しても、ユーザーは選択したページを他のユーザーと共有したり、将来使用するためにブックマークしたりすることはありません。どれもこの使用例には適していません。
使用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
属性,以确保它在关闭标签时不会过期。另外,我添加 SameSite
和 Secure
属性。这些都是必要的,因为CodePen使用iframe来运行这些例子,但在大多数情况下你并不需要它们。读取一个cookie需要解析cookie字符串。
Cookie字符串如下所示:
key1=value1;key2=value2;key3=value3
因此,首先,我们必须用分号分隔字符串。现在,我们有一个形式为 key1=value1
的Cookie数组,所以我们需要在数组中找到正确的元素。最后,我们将等号分开并获得新数组中的最后一个元素。有点繁琐,但一旦你实现了 getCookie
函数(或从我的例子中复制它:P),你就可以忘记它。
将应用程序数据保存在cookie中可能是个坏主意!它将大大增加请求的大小,并降低应用程序性能。此外,服务器无法从这些信息中获益,因为它是数据库中已有信息的陈旧版本。如果你使用cookies,请确保它们很小。
分页示例也不适合cookie,就像 localStorage
和 IndexedDB
一样。当前页面是我们想要与他人共享的临时状态,这些方法都无法实现它。
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.pushState
或 history.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非常适合service worker,service worker可以拦截每一个网络请求,使用 Cache API 它可以轻松地缓存这两个请求。service worker也可以将现有的缓存项作为网络响应返回,而不是从服务器上获取。这样,您可以减少网络负载时间,并使你的应用程序即使处于脱机状态也能正常工作。最初,它是为service worker创建的,但在现代浏览器中,Cache API也可以在窗口、iframe和worker上下文中使用。这是一个非常强大的API,可以极大地改善应用的用户体验。
就像IndexedDB一样,Cache API的存储不受限制,您可以存储数百兆字节,如果需要甚至可以存储更多。API是异步的,所以它不会阻塞你的主线程,而且它可以通过全局属性 caches
来访问。
如果你建立一个浏览器扩展,你有另一个选择来存储你的数据,我在进行扩展程序daily.dev时发现了它。如果你使用Mozilla的polyfill,它可以通过 chrome.storage
或 browser.storage
获得。确保在你的清单中申请一个存储权限以获得访问权。
有两种类型的存储选项:local和sync。local存储是不言而喻的,它的意思是不共享,保存在本地。sync存储是作为谷歌账户的一部分同步的,你在任何地方用同一个账户安装扩展,这个存储都会被同步。两者都有相同的API,所以如果需要的话,来回切换超级容易。它是异步存储,因此不会像 localStorage
这样阻塞主线程。不幸的是,我不能为这个存储选项创建一个演示,因为它需要一个浏览器扩展,但它的使用非常简单,几乎和 localStorage
一样。有关确切实现的更多信息,请参阅Chrome文档。
浏览器有许多选项可用于存储数据。根据Chrome团队的建议,我们的首选存储应该是IndexedDB,它是异步存储,有足够的空间来存储我们想要的任何东西。不鼓励使用 localStorage
,但它比 IndexedDB
更易于使用。Cookies是与服务器共享客户端状态的一种好方法,但通常用于身份验证。
如果你想创建具有可共享状态的页面,如搜索页面,请使用URL的查询字符串来存储这些信息。最后,如果你建立一个扩展,一定要阅读关于 chrome.storage
。
更多编程相关知识,请访问:编程视频!!
以上がブラウザのさまざまなストレージ タイプについて学ぶの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。