Cookie是最早的客户端存储方式。
// Cookie特点
// - 大小限制:约4KB
// - 每次HTTP请求都会发送
// - 可以设置过期时间
// - 可以设置域名和路径
// - 可以设置HttpOnly和Secure
// 创建Cookie
document.cookie = "name=东巴文";
document.cookie = "age=25";
// 读取Cookie
console.log(document.cookie); // "name=东巴文; age=25"
// 注意:document.cookie不是字符串
// 赋值是添加,不是替换
// 设置过期时间
document.cookie = "name=东巴文; expires=Fri, 31 Dec 2025 23:59:59 GMT";
// 使用max-age(秒)
document.cookie = "name=东巴文; max-age=3600"; // 1小时后过期
// 设置路径
document.cookie = "name=东巴文; path=/";
// 设置域名
document.cookie = "name=东巴文; domain=.db-w.cn";
// 设置Secure(仅HTTPS)
document.cookie = "name=东巴文; secure";
// 设置HttpOnly(JavaScript无法读取)
// 只能通过服务器设置
// Set-Cookie: name=东巴文; HttpOnly
// 设置SameSite
document.cookie = "name=东巴文; SameSite=Strict";
const Cookie = {
set(name, value, options = {}) {
let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;
if (options.expires) {
cookie += `; expires=${options.expires.toUTCString()}`;
}
if (options.maxAge) {
cookie += `; max-age=${options.maxAge}`;
}
if (options.path) {
cookie += `; path=${options.path}`;
}
if (options.domain) {
cookie += `; domain=${options.domain}`;
}
if (options.secure) {
cookie += "; secure";
}
if (options.sameSite) {
cookie += `; SameSite=${options.sameSite}`;
}
document.cookie = cookie;
},
get(name) {
const cookies = document.cookie.split("; ");
for (const cookie of cookies) {
const [key, value] = cookie.split("=");
if (decodeURIComponent(key) === name) {
return decodeURIComponent(value);
}
}
return null;
},
remove(name, options = {}) {
const expires = new Date(0);
this.set(name, "", { ...options, expires });
},
has(name) {
return this.get(name) !== null;
},
getAll() {
const result = {};
const cookies = document.cookie.split("; ");
for (const cookie of cookies) {
if (cookie) {
const [key, value] = cookie.split("=");
result[decodeURIComponent(key)] = decodeURIComponent(value);
}
}
return result;
}
};
// 使用
Cookie.set("name", "东巴文", { maxAge: 3600, path: "/" });
console.log(Cookie.get("name")); // "东巴文"
Cookie.remove("name");
localStorage提供持久化的本地存储。
// 存储数据
localStorage.setItem("name", "东巴文");
localStorage.setItem("age", "25");
// 读取数据
console.log(localStorage.getItem("name")); // "东巴文"
console.log(localStorage.getItem("age")); // "25"
// 删除数据
localStorage.removeItem("name");
// 清空所有数据
localStorage.clear();
// 获取键名
localStorage.setItem("a", "1");
localStorage.setItem("b", "2");
console.log(localStorage.key(0)); // "a" 或 "b"
console.log(localStorage.length); // 2
// localStorage只能存储字符串
// 需要JSON序列化
const user = {
name: "东巴文",
email: "info@db-w.cn"
};
// 存储
localStorage.setItem("user", JSON.stringify(user));
// 读取
const stored = localStorage.getItem("user");
const parsedUser = stored ? JSON.parse(stored) : null;
console.log(parsedUser.name); // "东巴文"
const Storage = {
set(key, value) {
try {
localStorage.setItem(key, JSON.stringify(value));
return true;
} catch (e) {
console.error("存储失败:", e);
return false;
}
},
get(key, defaultValue = null) {
try {
const value = localStorage.getItem(key);
return value ? JSON.parse(value) : defaultValue;
} catch (e) {
console.error("读取失败:", e);
return defaultValue;
}
},
remove(key) {
localStorage.removeItem(key);
},
clear() {
localStorage.clear();
},
has(key) {
return localStorage.getItem(key) !== null;
}
};
// 使用
Storage.set("user", { name: "东巴文" });
console.log(Storage.get("user")); // { name: "东巴文" }
sessionStorage提供会话级别的存储。
// 与localStorage API相同
sessionStorage.setItem("token", "abc123");
console.log(sessionStorage.getItem("token")); // "abc123"
sessionStorage.removeItem("token");
sessionStorage.clear();
// 区别:
// - sessionStorage在页面关闭后清除
// - localStorage永久保存
// - sessionStorage在不同标签页间隔离
// - localStorage在同源页面间共享
// 场景1:表单数据临时保存
const form = document.querySelector("form");
// 输入时保存
form.addEventListener("input", function() {
const formData = new FormData(this);
const data = Object.fromEntries(formData);
sessionStorage.setItem("formData", JSON.stringify(data));
});
// 页面加载时恢复
const savedData = sessionStorage.getItem("formData");
if (savedData) {
const data = JSON.parse(savedData);
Object.entries(data).forEach(([name, value]) => {
const field = form.elements[name];
if (field) field.value = value;
});
}
// 场景2:一次性消息
function showFlashMessage(message) {
sessionStorage.setItem("flashMessage", message);
}
// 页面加载时显示
const flashMessage = sessionStorage.getItem("flashMessage");
if (flashMessage) {
alert(flashMessage);
sessionStorage.removeItem("flashMessage");
}
监听storage变化。
// storage事件在同源的其他页面触发
window.addEventListener("storage", function(e) {
console.log("键:", e.key);
console.log("旧值:", e.oldValue);
console.log("新值:", e.newValue);
console.log("URL:", e.url);
console.log("存储区域:", e.storageArea);
});
// 在另一个标签页执行
localStorage.setItem("name", "东巴文");
// 第一个标签页会收到storage事件
// 标签页通信示例
const TabMessenger = {
send(type, data) {
localStorage.setItem("message", JSON.stringify({
type,
data,
timestamp: Date.now()
}));
localStorage.removeItem("message"); // 触发事件
},
on(type, callback) {
window.addEventListener("storage", function(e) {
if (e.key === "message" && e.newValue) {
const message = JSON.parse(e.newValue);
if (message.type === type) {
callback(message.data);
}
}
});
}
};
// 使用
TabMessenger.on("notification", function(data) {
showNotification(data.message);
});
// 在另一个标签页发送
TabMessenger.send("notification", { message: "新消息到达" });
浏览器内置的NoSQL数据库。
// IndexedDB特点
// - 存储大量结构化数据
// - 支持索引和事务
// - 异步API
// - 同源策略
// 打开数据库
const request = indexedDB.open("MyDatabase", 1);
request.onerror = function() {
console.error("数据库打开失败");
};
request.onsuccess = function() {
const db = request.result;
console.log("数据库打开成功");
};
request.onupgradeneeded = function(e) {
const db = e.target.result;
// 创建对象存储(表)
if (!db.objectStoreNames.contains("users")) {
const store = db.createObjectStore("users", { keyPath: "id" });
// 创建索引
store.createIndex("name", "name", { unique: false });
store.createIndex("email", "email", { unique: true });
}
};
class IndexedDBHelper {
constructor(dbName, version) {
this.dbName = dbName;
this.version = version;
this.db = null;
}
open() {
return new Promise((resolve, reject) => {
const request = indexedDB.open(this.dbName, this.version);
request.onerror = () => reject(request.error);
request.onsuccess = () => {
this.db = request.result;
resolve(this.db);
};
request.onupgradeneeded = (e) => {
const db = e.target.result;
if (!db.objectStoreNames.contains("items")) {
db.createObjectStore("items", { keyPath: "id" });
}
};
});
}
add(storeName, data) {
return new Promise((resolve, reject) => {
const transaction = this.db.transaction(storeName, "readwrite");
const store = transaction.objectStore(storeName);
const request = store.add(data);
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
get(storeName, key) {
return new Promise((resolve, reject) => {
const transaction = this.db.transaction(storeName, "readonly");
const store = transaction.objectStore(storeName);
const request = store.get(key);
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
update(storeName, data) {
return new Promise((resolve, reject) => {
const transaction = this.db.transaction(storeName, "readwrite");
const store = transaction.objectStore(storeName);
const request = store.put(data);
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
delete(storeName, key) {
return new Promise((resolve, reject) => {
const transaction = this.db.transaction(storeName, "readwrite");
const store = transaction.objectStore(storeName);
const request = store.delete(key);
request.onsuccess = () => resolve();
request.onerror = () => reject(request.error);
});
}
getAll(storeName) {
return new Promise((resolve, reject) => {
const transaction = this.db.transaction(storeName, "readonly");
const store = transaction.objectStore(storeName);
const request = store.getAll();
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
}
// 使用
const db = new IndexedDBHelper("MyDB", 1);
async function demo() {
await db.open();
await db.add("items", { id: 1, name: "东巴文" });
await db.add("items", { id: 2, name: "db-w.cn" });
const item = await db.get("items", 1);
console.log(item); // { id: 1, name: "东巴文" }
await db.update("items", { id: 1, name: "新名称" });
await db.delete("items", 2);
const all = await db.getAll("items");
console.log(all);
}
demo();
各种存储方式的限制。
// 检测存储配额
if (navigator.storage && navigator.storage.estimate) {
navigator.storage.estimate().then(estimate => {
console.log("已使用:", estimate.usage, "字节");
console.log("配额:", estimate.quota, "字节");
console.log("使用率:", (estimate.usage / estimate.quota * 100).toFixed(2) + "%");
});
}
// 检测存储是否持久化
if (navigator.storage && navigator.storage.persist) {
navigator.storage.persist().then(persisted => {
if (persisted) {
console.log("存储已持久化");
}
});
}
| 存储方式 | 容量 | 过期时间 | 跨标签页 | 发送到服务器 |
|---|---|---|---|---|
| Cookie | ~4KB | 可设置 | 共享 | 是 |
| localStorage | ~5MB | 永久 | 共享 | 否 |
| sessionStorage | ~5MB | 会话 | 隔离 | 否 |
| IndexedDB | ~50MB+ | 永久 | 共享 | 否 |
function safeSetItem(key, value) {
try {
localStorage.setItem(key, value);
return true;
} catch (e) {
if (e.name === "QuotaExceededError") {
console.error("存储空间已满");
// 清理旧数据
localStorage.clear();
// 重试
try {
localStorage.setItem(key, value);
return true;
} catch (e) {
return false;
}
}
return false;
}
}
// 检测隐私模式
function isPrivateMode() {
try {
localStorage.setItem("test", "test");
localStorage.removeItem("test");
return false;
} catch (e) {
return true;
}
}
掌握了客户端存储后,让我们继续学习:
东巴文(db-w.cn) - 让编程学习更简单
🎯 东巴文寄语:客户端存储是现代Web应用的重要组成部分,根据需求选择合适的存储方式:Cookie用于会话、localStorage用于持久数据、IndexedDB用于大量结构化数据。在 db-w.cn,我们帮你掌握数据存储技术!