Web 应用中的存储方式
Cookie
Cookie 是存储在用户浏览器中的小型文本文件,用于在客户端和服务器之间传递数据。它是 Web 开发中常用的一种机制,用于在用户访问网站时跟踪和存储有关用户会话、偏好设置、状态等的信息。
使用
设置值
document.cookie = "name=oeschger";
document.cookie = "favorite_food=tripe";
alert(document.cookie);
// 显示:name=oeschger;favorite_food=tripe
读取值
// 得到名为 test2 的 cookie
document.cookie = "test1=Hello";
document.cookie = "test2=World";
const myCookie = document.cookie.replace(
/(?:(?:^|.*;\s*)test2\s*\=\s*([^;]*).*$)|^.*$/,
"$1"
);
alert(myCookie);
// 显示:World
特点
Cookie 是 Web 开发中常用的一种机制,用于在客户端(浏览器)和服务器之间传递数据。它具有以下主要特点:
- 小型容量:
每个 Cookie 的大小通常有限制,一般为几 KB,通常在 4KB 左右。由于容量有限,不适合存储大量的数据。 - 域限制:
Cookie 与特定的域名相关联,只能由该域名下的页面进行读取和修改。这样可以确保 Cookie 的数据仅在特定的网站内有效,不被其他网站访问。 - 路径限制:
可以设置 Cookie 的作用路径,限制哪些页面可以访问 Cookie。默认情况下,Cookie 对于设置它的页面以及该页面所在目录下的所有页面都是可见的。 - 安全性:
Cookie 中的数据可以被浏览器读取和修改,因此敏感数据应该避免直接存储在 Cookie 中。可以使用加密或其他安全措施来增强 Cookie 的安全性。 - 时效性:
Cookie 可以设置过期时间,控制 Cookie 的生命周期。一些 Cookie 是会话 Cookie,会在用户关闭浏览器时被删除,而其他 Cookie 可能会有更长的过期时间。 - 数据传递:
通过在 HTTP 请求的头部添加 Cookie,浏览器可以将存储在 Cookie 中的数据发送给服务器。这使得服务器能够根据用户的 Cookie 识别用户、维护用户会话状态、提供个性化体验等。 - 持久性:
由于 Cookie 是存储在用户浏览器中的文本文件,它们可以在用户下次访问网站时继续使用。因此,Cookie 可用于在用户访问同一网站时保留数据。 - 无状态协议补偿:
HTTP 是一种无状态协议,即服务器无法识别两次请求是否来自同一个用户。通过使用 Cookie,服务器可以在用户浏览器中存储一个唯一的标识符,从而在多个请求之间识别用户,实现会话跟踪。
Web Storage
Web Storage 包括 localStorage 和 sessionStorage
使用
// 保存数据到 localStorage
localStorage.setItem("key", "value");
// 从 localStorage 获取数据
let data = localStorage.getItem("key");
// 从 localStorage 删除保存的数据
localStorage.removeItem("key");
// 从 localStorage 删除所有保存的数据
localStorage.clear();
sessionStorage 的使用和 localStorage 一样
LocalStorage 和 SessionStorage 对比
LocalStorage 和 SessionStorage 都是 Web 存储 API,它们用于在客户端(浏览器)中存储数据。虽然它们在使用和行为上有相似之处,但它们在存储数据的时效性和作用域等方面有一些重要的区别。以下是对比它们的主要差异:
- 时效性:
- LocalStorage:存储在 LocalStorage 中的数据没有过期时间,除非用户手动清除或网站代码删除它们。数据会一直保留在客户端,即使浏览器被关闭或电脑重启。
- SessionStorage:存储在 SessionStorage 中的数据在当前会话期间有效。当用户关闭标签页或浏览器时,会话数据会被清除。会话期间,数据可以在不同页面之间共享。
- 作用域:
- LocalStorage:数据在同一个域名下始终有效,不同页面之间可以共享相同的 LocalStorage 数据。
- SessionStorage:数据在同一个标签页(页面会话)中有效,不同标签页之间无法共享 SessionStorage 数据。
- 存储大小限制:
- LocalStorage:通常可以存储更多的数据,一般限制在 5MB 到 10MB 之间(不同浏览器可能有所不同)。
- SessionStorage:一般也限制在 5MB 到 10MB 之间,但由于数据在会话结束时被清除,所以通常会拥有与 LocalStorage 相同的存储空间。
- 数据共享与隔离:
- LocalStorage:由于数据在同一个域名下共享,可能会导致不同页面之间共享相同的数据。这在某些场景下是有用的,但也需要小心管理以避免数据冲突。
- SessionStorage:数据只在当前会话期间有效,不同标签页之间无法共享数据。这使得 SessionStorage 在某些场景下更适合临时保存和隔离数据。
应该根据具体的需求来选择使用 LocalStorage 还是 SessionStorage。如果需要长期保存数据或数据在不同页面间共享,可以使用 LocalStorage。如果只需要在单个会话期间保存临时数据,并且不需要跨页面共享数据,那么 SessionStorage 是更合适的选择。
IndexedDB
IndexedDB 是一种底层 API,用于在客户端存储大量的结构化数据(也包括文件/二进制大型对象(blobs))。该 API 使用索引实现对数据的高性能搜索。
使用
// 打开或创建名为 "myDatabase" 的数据库
const request = indexedDB.open("myDatabase", 1);
// 数据库创建或升级时触发
request.onupgradeneeded = (event) => {
const db = event.target.result;
// 创建一个名为 "users" 的对象存储空间(相当于数据库表)
const usersStore = db.createObjectStore("users", {
keyPath: "id",
autoIncrement: true,
});
// 定义需要存储的数据结构
usersStore.createIndex("name", "name", { unique: false });
usersStore.createIndex("email", "email", { unique: true });
};
// 数据库打开成功后触发
request.onsuccess = (event) => {
const db = event.target.result;
// 获取事务并访问对象存储空间
const transaction = db.transaction("users", "readwrite");
const usersStore = transaction.objectStore("users");
// 存储数据
usersStore.add({ name: "John Doe", email: "john@example.com" });
usersStore.add({ name: "Alice Smith", email: "alice@example.com" });
// 查询数据
const emailIndex = usersStore.index("email");
const request = emailIndex.get("john@example.com");
request.onsuccess = (event) => {
const user = event.target.result;
if (user) {
console.log(user); // { id: 1, name: 'John Doe', email: 'john@example.com' }
} else {
console.log("User not found.");
}
};
};
// 处理数据库打开失败
request.onerror = (event) => {
console.error("Error opening database:", event.target.error);
};
这只是 IndexedDB 的简单示例,它还支持更复杂的查询、索引、范围查询等功能,可以满足更复杂的数据存储需求。IndexedDB 是一种强大的客户端存储解决方案,特别适合需要离线访问、大量数据存储和复杂查询的 Web 应用。这里就不详细展开来讲,如果想了解更多可以去 MDN 网站查看它的使用及底层原理。
特点
- 容量较大:
IndexedDB 可以存储大量数据,通常限制在 50MB 到数百 MB 之间(不同浏览器可能有所不同),远远超过传统的本地存储(如 LocalStorage)的容量限制。 - 异步 API:
IndexedDB API 是异步的,它使用 Promise 或类似的异步机制来处理数据的存储和检索。这样可以避免阻塞主线程,提高应用程序的响应性能。 - 事务支持:
IndexedDB 支持事务概念,可以在一个事务中执行多个数据操作,保证数据的一致性和完整性。事务可确保所有数据操作都要么全部成功,要么全部失败,以避免部分数据操作导致的数据不一致。 - 复杂查询:
IndexedDB 支持多种查询方式,可以通过索引来高效地检索数据。你可以创建索引以提高查询性能,并使用游标或范围查询等方式查询数据。 - 支持多个对象存储空间:
IndexedDB 允许在一个数据库中创建多个对象存储空间(类似于数据库表),每个对象存储空间可以存储不同类型的数据。这样可以更好地组织数据,使数据模型更灵活。 - 跨标签页和浏览器窗口支持:
与 LocalStorage 不同,IndexedDB 支持多个标签页或浏览器窗口之间共享数据。这使得 IndexedDB 更适合在复杂的 Web 应用中共享数据。 - 数据安全性:
IndexedDB 中的数据是相对安全的,因为数据只能被存储在用户的本地设备上,不会被发送到服务器,也不会受到跨站点请求伪造(CSRF)等网络攻击的影响。
Cache Storage
Cache Storage 是浏览器提供的一种 API,它是用于缓存资源文件的存储方式,在 PWA(Progressive Web App)中,Cache Storage 是一个重要的组成部分,它允许开发者将网站所需的资源(例如 HTML、CSS、JavaScript、图像等)保存在客户端的缓存中,以便在离线时仍然能够访问网站,并提供更快的加载速度和更好的用户体验。
使用
// 在 Service Worker 中缓存资源文件
self.addEventListener("install", (event) => {
event.waitUntil(
caches.open("my-cache").then((cache) => {
return cache.addAll([
"/",
"/index.html",
"/styles.css",
"/script.js",
"/images/logo.png",
// 可以添加其他需要缓存的资源文件
]);
})
);
});
// 拦截网络请求并从缓存中响应资源
self.addEventListener("fetch", (event) => {
event.respondWith(
caches.match(event.request).then((response) => {
// 如果缓存中有匹配的资源,则直接返回缓存中的响应
if (response) {
return response;
}
// 否则,从网络中获取资源,并将响应缓存起来
return fetch(event.request).then((response) => {
// 使用 clone() 方法,因为 response 是只能使用一次的对象
const clonedResponse = response.clone();
caches.open("my-cache").then((cache) => {
cache.put(event.request, clonedResponse);
});
return response;
});
})
);
});
上述代码是一个简单的 Service Worker 脚本,它在安装阶段将指定的资源文件缓存到名为 "my-cache" 的 Cache Storage 中。然后,在网络请求发生时,它会先检查缓存中是否存在匹配的资源,如果有,则直接返回缓存中的响应;如果没有,则从网络获取资源,并将响应缓存起来,以便以后离线时可以使用。
⚠️ 注意事项:Cache Storage 的使用需要配合 Service Worker,因为它们通常用于离线缓存和拦截网络请求
特点
- 离线访问:
Cache Storage 允许将网站所需的资源(如 HTML、CSS、JavaScript、图像等)缓存到客户端的缓存中,使得网站在离线时仍然可以访问这些资源。这为 PWA(Progressive Web App)提供了离线访问的能力,提升了用户体验。 - 快速加载:
通过缓存资源,可以实现更快的加载速度。当用户再次访问网站时,资源可以从缓存中直接加载,无需再次从服务器请求,减少了网络延迟和服务器负担,提高了页面的加载性能。 - 多级缓存:
Cache Storage 支持多级缓存,可以将资源分为不同的缓存级别,如缓存优先级高的资源和缓存优先级低的资源。这样可以更灵活地控制缓存策略,确保常用的资源总是可以快速地被访问。 - 缓存策略:
开发者可以通过控制 Service Worker 的缓存策略来定义资源的缓存行为。例如,可以指定资源的过期时间,强制重新请求最新的资源,或者使用新的资源更新旧的缓存等。 - 网络拦截:
通过 Service Worker,Cache Storage 可以拦截网络请求,并决定是否从缓存中提供资源,或者通过网络请求最新的资源。这为开发者提供了更大的灵活性和控制权。 - 跨会话缓存:
Cache Storage 中的缓存数据不受会话限制,即使用户关闭浏览器或重新启动设备,缓存数据仍然可以保留,从而使得缓存的资源可以在多个会话中共享,提供更持久的缓存效果。 - 资源管理:
开发者可以根据具体需求随时添加、更新或删除缓存中的资源,从而实现资源的动态管理和更新。
Memory Storage
Memory Storage(内存中存储数据)通常用于简单的状态管理,特别是在一些小型应用或简单的组件之间共享状态数据。内存中存储数据不涉及持久化,数据只在内存中存在,一旦页面刷新或关闭,数据将丢失。这使得它适用于临时存储数据或共享状态,但不适用于需要长期保持数据的场景。
使用
常见的状态管理,比如Vue
里面的data
,react
中的useState
。这里就不详细展开了,在我们日常开发中基本都是随处可见的内存存储数据的方式。
特点
- 临时存储:
Memory Storage 仅在当前会话期间有效,一旦用户关闭浏览器标签或窗口,数据将丢失。它适用于临时存储数据,例如在单个会话中共享状态或传递临时信息。 - 数据在内存中:
数据存储在浏览器的内存中,不会发送到服务器,因此不涉及网络请求。这使得读取和写入数据的速度非常快。 - 数据的生命周期与页面一致:
Memory Storage 的生命周期与网页的生命周期一致。当页面刷新或关闭时,存储在 Memory Storage 中的数据将被清除,不会被保留。 - 轻量级:
Memory Storage 不会像持久化存储(如 LocalStorage 或 IndexedDB)那样占用大量的磁盘空间,因为数据仅存储在浏览器的内存中。 - 不支持跨标签页和浏览器窗口共享数据:
每个标签页或浏览器窗口都有自己的独立的 Memory Storage,它们之间无法直接共享数据。如果需要在多个标签页或浏览器窗口之间共享数据,可以考虑使用其他持久化存储方案。 - 适用于简单状态管理:
Memory Storage 在某些场景下适用于简单的状态管理,特别是在小型应用或组件中共享状态数据。然而,对于复杂的状态管理需求,应该使用专业的状态管理工具,如 Redux 或 Vuex,以及持久化存储方案。
应用场景总结
- Cookie: 适合用于在客户端和服务器之间传递少量的用户身份信息、会话信息或用户偏好设置等。
- LocalStorage: 适合存储少量的键值对数据,例如用户配置、主题设置、用户喜好等。也可用于缓存一些静态资源,以提高页面加载速度。
- SessionStorage: 适合存储会话期间需要保留的数据,例如表单数据、临时状态等。仅在当前会话中有效,关闭页面后数据将丢失。
- IndexedDB: 适合存储大量的结构化数据,适用于需要离线访问和复杂查询的 Web 应用。适合较复杂的数据操作和数据查询需求。
- Cache Storage: 适合用于 PWA 中的离线访问和资源缓存,提供更快的页面加载速度和离线使用体验。
- Memory Storage: 适合简单的状态管理,特别是在小型应用或简单组件中共享状态数据,用于临时存储数据。
综合考虑存储容量、持久性、应用场景等因素,选择合适的存储方式对于开发前端应用至关重要。在实际应用中,可能会结合使用不同的存储方式,以满足复杂的数据管理需求。对于复杂的状态管理需求,可以考虑使用专业的状态管理工具,如 Redux(用于 React)或 Vuex(用于 Vue)。