Cookie和Storage详解:浏览器存储界的"双雄"之争

209 阅读11分钟

Cookie和Storage详解:浏览器存储界的"双雄"之争

骚话王又来分享知识了!今天咱们聊一个前端开发中经常打交道的话题——Cookie和Storage。这两个家伙在前端存储界可是响当当的"双雄",一个负责"小饼干",一个负责"大仓库"。

说起来,很多小伙伴在面试的时候都会被问到:"Cookie和localStorage有什么区别?"结果支支吾吾半天说不清楚,面试官一脸嫌弃,自己心里也直打鼓。别慌,今天骚话王就带你彻底搞懂这两个存储界的"老大哥"。

Cookie这家伙,虽然名字听起来很可爱,但它的历史可比localStorage要悠久得多。从1994年网景公司发明它开始,就一直在为Web世界默默付出。而Storage家族(localStorage和sessionStorage)则是HTML5时代的新宠,功能更强大,使用更灵活。

不过话说回来,虽然它们都是用来存储数据的,但各自的"性格"可大不一样。Cookie就像是一个"小管家",虽然容量有限,但能跟着HTTP请求到处跑;而Storage更像是一个"大仓库",容量大、功能强,但只能在本地"宅"着。

今天这篇文章,骚话王就带你深入浅出地了解这两个存储方案的特点、使用场景以及最佳实践。看完之后,保证你在面试官面前能侃侃而谈,再也不用担心被问得哑口无言了!

如果觉得有用就收藏点赞,咱们开始吧!

Cookie:Web世界的"小饼干"

名字的由来:一个有趣的典故

说到Cookie这个名字,背后还真有个挺有意思的故事。1994年,网景公司的程序员Lou Montulli在开发网景浏览器时,需要一种机制来在客户端保存状态信息。当时他正在吃饼干(cookie),灵机一动,觉得这个机制就像是在服务器和客户端之间传递的"小饼干"一样,于是就给它起了这个名字。

这个比喻其实很形象:服务器给客户端"发饼干",客户端"吃饼干"(保存信息),下次访问时再把"饼干屑"(状态信息)带回去给服务器。这样一来,服务器就能"记住"这个用户了。

历史渊源:从网景到现代Web

Cookie的诞生要追溯到1994年,那时候互联网还处于"石器时代"。网景公司为了解决电子商务网站需要"记住"用户购物车状态的问题,发明了这个机制。

最初的设计很简单:服务器在HTTP响应头中设置一个特殊的字段,浏览器接收到后会自动保存,并在后续的请求中自动发送回去。这个看似简单的机制,却彻底改变了Web的发展方向。

要知道,在Cookie出现之前,Web是无状态的,每次请求都是独立的,服务器根本不知道这个请求是来自哪个用户。有了Cookie之后,Web才有了"记忆"能力,这才有了后来的用户登录、购物车、个性化推荐等功能。

技术原理:HTTP协议的好搭档

Cookie本质上是一个HTTP头部字段,它的工作流程是这样的:

  1. 服务器设置:服务器在HTTP响应头中通过Set-Cookie字段发送Cookie
  2. 浏览器保存:浏览器接收到后,将Cookie保存在本地
  3. 自动发送:后续对同一域名的请求中,浏览器会自动在请求头中带上Cookie
  4. 服务器读取:服务器从请求头中读取Cookie,获取用户状态

这个机制虽然简单,但却是Web状态管理的基础。没有它,现在的互联网可能还停留在"每次都要重新登录"的原始状态。

Cookie的特点:小身材,大作用

Cookie虽然个头小(通常只有4KB左右),但它的作用可一点都不小:

  • 跨请求持久化:能够在不同请求之间保持状态
  • 自动传输:浏览器会自动在请求中携带Cookie
  • 域名隔离:每个域名只能访问自己的Cookie
  • 过期机制:可以设置过期时间,实现自动清理

这些特点让Cookie成为了Web开发中不可或缺的工具,特别是在用户认证、会话管理、个性化设置等场景下,Cookie简直就是"神器"一般的存在。

Cookie的详细机制:不只是简单的键值对

1. Cookie的组成部分

一个完整的Cookie包含多个属性,不仅仅是简单的键值对:

name=value; expires=date; path=path; domain=domain; secure; httponly
  • name=value:Cookie的核心,存储的实际数据
  • expires:过期时间,决定Cookie的生命周期
  • path:路径限制,控制Cookie在哪些页面可用
  • domain:域名限制,控制Cookie在哪些域名下可用
  • secure:安全标志,只在HTTPS连接下传输
  • httponly:HTTP专用标志,防止JavaScript访问
2. 过期机制:Cookie的"保质期"

Cookie的过期机制有三种方式:

会话Cookie(Session Cookie)

// 不设置expires,浏览器关闭就消失
document.cookie = "username=骚话王";

持久Cookie(Persistent Cookie)

// 设置具体的过期时间
const expires = new Date();
expires.setTime(expires.getTime() + (7 * 24 * 60 * 60 * 1000)); // 7天后过期
document.cookie = `username=骚话王; expires=${expires.toUTCString()}`;

Max-Age方式

// 设置最大存活时间(秒)
document.cookie = "username=骚话王; max-age=604800"; // 7天
3. 域名和路径限制:安全的第一道防线

域名限制

// 只在当前域名下有效
document.cookie = "username=骚话王";

// 在子域名下也有效
document.cookie = "username=骚话王; domain=.example.com";

路径限制

// 只在当前路径下有效
document.cookie = "username=骚话王";

// 在整个网站下有效
document.cookie = "username=骚话王; path=/";

// 只在特定路径下有效
document.cookie = "username=骚话王; path=/admin";

Cookie的JavaScript操作:手把手教你玩转

1. 设置Cookie:不只是简单的赋值

基础设置

// 最简单的设置方式
document.cookie = "username=骚话王";

带属性的设置

// 设置带过期时间的Cookie
function setCookie(name, value, days) {
    const expires = new Date();
    expires.setTime(expires.getTime() + (days * 24 * 60 * 60 * 1000));
    document.cookie = `${name}=${value}; expires=${expires.toUTCString()}; path=/`;
}

// 使用示例
setCookie("username", "骚话王", 7); // 保存7天

安全设置

// 设置安全的Cookie(只在HTTPS下传输)
document.cookie = "sessionId=abc123; secure; httponly";
2. 读取Cookie:解析字符串的艺术

基础读取

// 获取所有Cookie
console.log(document.cookie);
// 输出: "username=骚话王; theme=dark; language=zh-CN"

解析特定Cookie

function getCookie(name) {
    const nameEQ = name + "=";
    const ca = document.cookie.split(';');
    
    for(let i = 0; i < ca.length; i++) {
        let c = ca[i];
        while (c.charAt(0) === ' ') c = c.substring(1, c.length);
        if (c.indexOf(nameEQ) === 0) {
            return c.substring(nameEQ.length, c.length);
        }
    }
    return null;
}

// 使用示例
const username = getCookie("username"); // "骚话王"

现代方法(推荐)

// 使用URLSearchParams解析(更简洁)
function getCookie(name) {
    const value = `; ${document.cookie}`;
    const parts = value.split(`; ${name}=`);
    if (parts.length === 2) return parts.pop().split(';').shift();
    return null;
}
3. 删除Cookie:让Cookie"消失"的技巧

设置过期时间为过去

function deleteCookie(name) {
    document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;
}

// 使用示例
deleteCookie("username");

设置空值

function deleteCookie(name) {
    document.cookie = `${name}=; max-age=0; path=/;`;
}

Cookie的实际应用场景:不只是存储数据

1. 用户认证:Web安全的基础
// 登录成功后设置认证Cookie
function login(username, password) {
    // 验证逻辑...
    if (isValidUser) {
        const token = generateToken(username);
        document.cookie = `authToken=${token}; expires=${getExpiryDate(7)}; secure; httponly`;
        return true;
    }
    return false;
}

// 检查用户是否已登录
function isLoggedIn() {
    const token = getCookie("authToken");
    return token !== null;
}
2. 购物车功能:电商网站的标配
// 添加商品到购物车
function addToCart(productId, quantity) {
    const cart = getCart();
    cart[productId] = (cart[productId] || 0) + quantity;
    saveCart(cart);
}

// 获取购物车数据
function getCart() {
    const cartData = getCookie("cart");
    return cartData ? JSON.parse(decodeURIComponent(cartData)) : {};
}

// 保存购物车数据
function saveCart(cart) {
    const cartData = encodeURIComponent(JSON.stringify(cart));
    document.cookie = `cart=${cartData}; expires=${getExpiryDate(30)}; path=/`;
}
3. 用户偏好设置:个性化体验
// 保存用户主题偏好
function setTheme(theme) {
    document.cookie = `theme=${theme}; expires=${getExpiryDate(365)}; path=/`;
    applyTheme(theme);
}

// 保存语言设置
function setLanguage(lang) {
    document.cookie = `language=${lang}; expires=${getExpiryDate(365)}; path=/`;
    applyLanguage(lang);
}

Cookie的限制和注意事项:别踩坑

1. 容量限制:4KB的"紧箍咒"
// 错误示例:存储大量数据
const largeData = "很长的字符串..."; // 超过4KB
document.cookie = `data=${largeData}`; // 可能被截断或拒绝

// 正确做法:压缩或分片存储
const compressedData = compressData(largeData);
document.cookie = `data=${compressedData}`;
2. 数量限制:每个域名最多50个
// 避免创建过多Cookie
// 错误做法:为每个设置项创建单独的Cookie
document.cookie = "setting1=value1";
document.cookie = "setting2=value2";
document.cookie = "setting3=value3";
// ... 可能超过50个

// 正确做法:合并相关设置
const settings = {
    setting1: "value1",
    setting2: "value2",
    setting3: "value3"
};
document.cookie = `settings=${JSON.stringify(settings)}`;
3. 安全考虑:防止XSS和CSRF攻击
// 设置HttpOnly防止XSS攻击
document.cookie = "sessionId=abc123; httponly";

// 设置Secure确保只在HTTPS下传输
document.cookie = "authToken=xyz789; secure; httponly";

// 设置SameSite防止CSRF攻击
document.cookie = "sessionId=abc123; samesite=strict";

Storage:HTML5时代的"大仓库"

从Cookie到Storage:Web存储的进化史

如果说Cookie是Web存储界的"老前辈",那么Storage就是HTML5时代的"新贵"。2009年,HTML5规范正式引入了Web Storage API,为前端开发者提供了更强大、更灵活的存储方案。

Storage家族有两个"兄弟":localStorage和sessionStorage。它们就像是两个不同性格的"仓库管理员":一个负责长期存储,一个负责临时保管。这种设计让开发者可以根据不同的需求选择合适的存储方案。

localStorage:持久化的"大仓库"

1. 基本特点:比Cookie更"豪横"

localStorage相比Cookie,简直就是"鸟枪换炮":

  • 容量大:通常5-10MB,比Cookie的4KB大了1000多倍
  • 永久存储:除非手动删除,否则数据永远不会过期
  • 同源策略:只能在同域名、同协议、同端口下访问
  • 同步操作:所有操作都是同步的,不会阻塞主线程
2. 基本操作:简单到"一把梭"

设置数据

// 基础设置
localStorage.setItem('username', '骚话王');

// 存储对象(需要JSON序列化)
const user = {
    name: '骚话王',
    age: 25,
    preferences: {
        theme: 'dark',
        language: 'zh-CN'
    }
};
localStorage.setItem('user', JSON.stringify(user));

读取数据

// 基础读取
const username = localStorage.getItem('username'); // "骚话王"

// 读取对象(需要JSON反序列化)
const userStr = localStorage.getItem('user');
const user = userStr ? JSON.parse(userStr) : null;

// 读取不存在的键
const notExist = localStorage.getItem('nonexistent'); // null

删除数据

// 删除特定项
localStorage.removeItem('username');

// 清空所有数据
localStorage.clear();

检查数据是否存在

// 检查键是否存在
if (localStorage.getItem('username') !== null) {
    console.log('用户名已保存');
}

// 或者使用hasOwnProperty
if (localStorage.hasOwnProperty('username')) {
    console.log('用户名已保存');
}
3. 高级用法:不只是简单的键值对

遍历所有数据

// 获取所有键
const keys = Object.keys(localStorage);
console.log('所有存储的键:', keys);

// 遍历所有数据
for (let i = 0; i < localStorage.length; i++) {
    const key = localStorage.key(i);
    const value = localStorage.getItem(key);
    console.log(`${key}: ${value}`);
}

批量操作

// 批量设置
const data = {
    theme: 'dark',
    language: 'zh-CN',
    fontSize: '14px',
    notifications: 'enabled'
};

Object.entries(data).forEach(([key, value]) => {
    localStorage.setItem(key, value);
});

// 批量读取
const settings = {};
Object.keys(data).forEach(key => {
    settings[key] = localStorage.getItem(key);
});

数据版本控制

// 设置数据版本
localStorage.setItem('dataVersion', '1.0');
localStorage.setItem('userData', JSON.stringify(userData));

// 检查版本并迁移数据
function migrateData() {
    const currentVersion = localStorage.getItem('dataVersion');
    if (currentVersion !== '1.1') {
        // 执行数据迁移
        const oldData = JSON.parse(localStorage.getItem('userData'));
        const newData = transformData(oldData);
        
        localStorage.setItem('userData', JSON.stringify(newData));
        localStorage.setItem('dataVersion', '1.1');
    }
}

sessionStorage:会话级的"临时仓库"

1. 与localStorage的区别:临时vs永久

sessionStorage就像是localStorage的"临时版本":

  • 生命周期:只在当前会话期间有效,关闭标签页就消失
  • 作用域:只在当前标签页有效,不同标签页之间不共享
  • API相同:操作方式与localStorage完全一样
2. 使用场景:适合临时数据

表单数据暂存

// 保存表单数据,防止意外刷新丢失
function saveFormData(formData) {
    sessionStorage.setItem('formData', JSON.stringify(formData));
}

// 页面加载时恢复表单数据
function restoreFormData() {
    const savedData = sessionStorage.getItem('formData');
    if (savedData) {
        const formData = JSON.parse(savedData);
        fillForm(formData);
    }
}

// 表单提交成功后清除
function clearFormData() {
    sessionStorage.removeItem('formData');
}

页面状态管理

// 保存页面滚动位置
window.addEventListener('scroll', () => {
    sessionStorage.setItem('scrollPosition', window.scrollY);
});

// 页面加载时恢复滚动位置
window.addEventListener('load', () => {
    const savedPosition = sessionStorage.getItem('scrollPosition');
    if (savedPosition) {
        window.scrollTo(0, parseInt(savedPosition));
    }
});

多步骤流程

// 多步骤注册流程
function saveStepData(step, data) {
    sessionStorage.setItem(`step${step}`, JSON.stringify(data));
}

function getStepData(step) {
    const data = sessionStorage.getItem(`step${step}`);
    return data ? JSON.parse(data) : null;
}

function clearAllSteps() {
    for (let i = 1; i <= 5; i++) {
        sessionStorage.removeItem(`step${i}`);
    }
}

Storage的实际应用:从简单到复杂

1. 用户偏好设置:比Cookie更强大
// 用户设置管理器
class UserSettings {
    constructor() {
        this.defaults = {
            theme: 'light',
            language: 'zh-CN',
            fontSize: '14px',
            autoSave: true,
            notifications: true
        };
    }

    // 获取设置
    get(key) {
        const value = localStorage.getItem(`setting_${key}`);
        return value !== null ? value : this.defaults[key];
    }

    // 设置值
    set(key, value) {
        localStorage.setItem(`setting_${key}`, value);
        this.applySetting(key, value);
    }

    // 应用设置
    applySetting(key, value) {
        switch(key) {
            case 'theme':
                document.body.className = `theme-${value}`;
                break;
            case 'fontSize':
                document.body.style.fontSize = value;
                break;
            // 其他设置...
        }
    }

    // 重置所有设置
    reset() {
        Object.keys(this.defaults).forEach(key => {
            localStorage.removeItem(`setting_${key}`);
            this.applySetting(key, this.defaults[key]);
        });
    }
}

// 使用示例
const settings = new UserSettings();
settings.set('theme', 'dark');
2. 离线数据缓存:PWA的基础
// 离线数据管理器
class OfflineDataManager {
    constructor() {
        this.cacheKey = 'offline_cache';
        this.maxAge = 24 * 60 * 60 * 1000; // 24小时
    }

    // 缓存数据
    cache(key, data) {
        const cacheData = {
            data: data,
            timestamp: Date.now()
        };
        
        const allCache = this.getAllCache();
        allCache[key] = cacheData;
        
        localStorage.setItem(this.cacheKey, JSON.stringify(allCache));
    }

    // 获取缓存数据
    get(key) {
        const allCache = this.getAllCache();
        const cacheData = allCache[key];
        
        if (!cacheData) return null;
        
        // 检查是否过期
        if (Date.now() - cacheData.timestamp > this.maxAge) {
            this.remove(key);
            return null;
        }
        
        return cacheData.data;
    }

    // 获取所有缓存
    getAllCache() {
        const cacheStr = localStorage.getItem(this.cacheKey);
        return cacheStr ? JSON.parse(cacheStr) : {};
    }

    // 删除缓存
    remove(key) {
        const allCache = this.getAllCache();
        delete allCache[key];
        localStorage.setItem(this.cacheKey, JSON.stringify(allCache));
    }

    // 清理过期缓存
    cleanup() {
        const allCache = this.getAllCache();
        const now = Date.now();
        
        Object.keys(allCache).forEach(key => {
            if (now - allCache[key].timestamp > this.maxAge) {
                delete allCache[key];
            }
        });
        
        localStorage.setItem(this.cacheKey, JSON.stringify(allCache));
    }
}
3. 游戏存档系统:本地游戏数据
// 游戏存档管理器
class GameSaveManager {
    constructor(gameId) {
        this.gameId = gameId;
        this.saveKey = `game_save_${gameId}`;
    }

    // 保存游戏进度
    saveGame(gameData) {
        const saveData = {
            gameData: gameData,
            saveTime: new Date().toISOString(),
            version: '1.0'
        };
        
        localStorage.setItem(this.saveKey, JSON.stringify(saveData));
    }

    // 加载游戏进度
    loadGame() {
        const saveStr = localStorage.getItem(this.saveKey);
        if (!saveStr) return null;
        
        const saveData = JSON.parse(saveStr);
        return saveData.gameData;
    }

    // 检查是否有存档
    hasSave() {
        return localStorage.getItem(this.saveKey) !== null;
    }

    // 删除存档
    deleteSave() {
        localStorage.removeItem(this.saveKey);
    }

    // 获取存档信息
    getSaveInfo() {
        const saveStr = localStorage.getItem(this.saveKey);
        if (!saveStr) return null;
        
        const saveData = JSON.parse(saveStr);
        return {
            saveTime: saveData.saveTime,
            version: saveData.version
        };
    }
}

// 使用示例
const gameSave = new GameSaveManager('myGame');
gameSave.saveGame({
    level: 5,
    score: 1250,
    inventory: ['sword', 'shield', 'potion']
});

Storage的限制和最佳实践:避免"翻车"

1. 存储限制:不是无限大的
// 检查存储空间
function checkStorageQuota() {
    const testKey = 'storage_test';
    const testValue = 'x'.repeat(1024 * 1024); // 1MB
    
    try {
        localStorage.setItem(testKey, testValue);
        localStorage.removeItem(testKey);
        return true;
    } catch (e) {
        return false;
    }
}

// 估算存储大小
function getStorageSize() {
    let size = 0;
    for (let key in localStorage) {
        if (localStorage.hasOwnProperty(key)) {
            size += localStorage[key].length + key.length;
        }
    }
    return size;
}
2. 错误处理:防止"爆仓"
// 安全的存储操作
function safeSetItem(key, value) {
    try {
        localStorage.setItem(key, value);
        return true;
    } catch (e) {
        if (e.name === 'QuotaExceededError') {
            // 存储空间不足,清理旧数据
            cleanupOldData();
            try {
                localStorage.setItem(key, value);
                return true;
            } catch (e2) {
                console.error('存储失败:', e2);
                return false;
            }
        }
        console.error('存储错误:', e);
        return false;
    }
}

// 清理旧数据
function cleanupOldData() {
    const keys = Object.keys(localStorage);
    const sortedKeys = keys.sort((a, b) => {
        const timeA = localStorage.getItem(`${a}_timestamp`) || 0;
        const timeB = localStorage.getItem(`${b}_timestamp`) || 0;
        return timeA - timeB;
    });
    
    // 删除最旧的数据
    for (let i = 0; i < Math.floor(sortedKeys.length / 2); i++) {
        localStorage.removeItem(sortedKeys[i]);
    }
}
3. 数据安全:防止"泄密"
// 敏感数据加密存储
function encryptData(data, password) {
    // 简单的加密示例(实际项目中应使用更安全的加密方法)
    return btoa(JSON.stringify(data) + password);
}

function decryptData(encryptedData, password) {
    try {
        const decoded = atob(encryptedData);
        const dataStr = decoded.slice(0, -password.length);
        return JSON.parse(dataStr);
    } catch (e) {
        return null;
    }
}

// 存储敏感数据
function saveSensitiveData(key, data, password) {
    const encrypted = encryptData(data, password);
    localStorage.setItem(key, encrypted);
}

// 读取敏感数据
function loadSensitiveData(key, password) {
    const encrypted = localStorage.getItem(key);
    if (!encrypted) return null;
    
    return decryptData(encrypted, password);
}

Cookie vs Storage:前端存储界的"华山论剑"

核心差异对比:谁是真正的"王者"

经过前面的详细讲解,相信大家对Cookie和Storage都有了深入的了解。现在让我们来一场"华山论剑",看看这两个存储方案到底谁更胜一筹。

1. 基础特性对比:从"身材"到"性格"
特性CookielocalStoragesessionStorage
存储容量4KB左右5-10MB5-10MB
生命周期可设置过期时间永久存储会话期间
作用域同域名下所有页面同域名下所有页面仅当前标签页
自动传输每次HTTP请求自动携带不自动传输不自动传输
服务器访问服务器可读取仅客户端访问仅客户端访问
API复杂度需要手动解析字符串简单的键值对API简单的键值对API
2. 性能对比:速度与效率的较量

Cookie的性能特点:

// Cookie每次请求都会自动携带,增加网络开销
// 假设一个页面有10个Cookie,每个4KB,那么每次请求都会额外传输40KB数据
// 对于高频请求的API,这个开销是相当可观的

// 示例:检查Cookie对请求大小的影响
function checkCookieOverhead() {
    const cookies = document.cookie.split(';');
    let totalSize = 0;
    
    cookies.forEach(cookie => {
        totalSize += cookie.trim().length;
    });
    
    console.log(`当前Cookie总大小: ${totalSize} 字节`);
    return totalSize;
}

Storage的性能特点:

// Storage只在需要时访问,不会增加网络开销
// 但需要注意同步操作可能阻塞主线程

// 示例:批量操作性能对比
function performanceTest() {
    const testData = Array.from({length: 1000}, (_, i) => `data${i}`);
    
    // Cookie方式(慢)
    console.time('Cookie操作');
    testData.forEach((data, index) => {
        document.cookie = `item${index}=${data}`;
    });
    console.timeEnd('Cookie操作');
    
    // Storage方式(快)
    console.time('Storage操作');
    testData.forEach((data, index) => {
        localStorage.setItem(`item${index}`, data);
    });
    console.timeEnd('Storage操作');
}
3. 安全性对比:谁更"安全可靠"

Cookie的安全机制:

// Cookie有多种安全属性
document.cookie = "sessionId=abc123; secure; httponly; samesite=strict";

// secure: 只在HTTPS下传输
// httponly: 防止JavaScript访问,防XSS
// samesite: 防止CSRF攻击

Storage的安全考虑:

// Storage没有内置的安全机制,需要开发者自己实现
// 敏感数据应该加密存储

function secureStorage(key, data, password) {
    const encrypted = btoa(JSON.stringify(data) + password);
    localStorage.setItem(key, encrypted);
}

function secureRetrieval(key, password) {
    const encrypted = localStorage.getItem(key);
    if (!encrypted) return null;
    
    try {
        const decoded = atob(encrypted);
        const dataStr = decoded.slice(0, -password.length);
        return JSON.parse(dataStr);
    } catch (e) {
        return null;
    }
}

实际应用场景对比:谁更适合什么场景

1. 用户认证场景:Cookie的"主场"

为什么认证要用Cookie?

// 认证信息需要自动传输到服务器
function login(username, password) {
    // 验证成功后设置认证Cookie
    const token = generateAuthToken(username);
    
    // 设置安全的认证Cookie
    document.cookie = `authToken=${token}; expires=${getExpiryDate(7)}; secure; httponly; samesite=strict`;
    
    // 服务器可以通过请求头自动获取到authToken
    // 无需前端手动在每次请求中添加
}

// 如果使用localStorage存储认证信息,需要手动添加到请求头
function apiRequest(url, data) {
    const token = localStorage.getItem('authToken');
    
    fetch(url, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${token}` // 需要手动添加
        },
        body: JSON.stringify(data)
    });
}
2. 用户偏好设置:Storage的"天下"

为什么偏好设置要用Storage?

// 用户设置通常较大,且不需要服务器处理
const userPreferences = {
    theme: 'dark',
    language: 'zh-CN',
    fontSize: '16px',
    notifications: true,
    autoSave: true,
    sidebarCollapsed: false,
    // ... 更多设置项
};

// 使用localStorage存储(推荐)
localStorage.setItem('userPreferences', JSON.stringify(userPreferences));

// 如果使用Cookie存储,会遇到容量限制
// 4KB的限制会让很多设置项无法保存
3. 购物车功能:混合使用的"智慧"

购物车的存储策略:

// 临时购物车数据用sessionStorage(页面刷新不丢失)
function addToCart(productId, quantity) {
    // 临时购物车
    const sessionCart = getSessionCart();
    sessionCart[productId] = (sessionCart[productId] || 0) + quantity;
    sessionStorage.setItem('cart', JSON.stringify(sessionCart));
    
    // 同步到持久化存储
    localStorage.setItem('persistentCart', JSON.stringify(sessionCart));
}

// 浏览历史:localStorage
function addToHistory(productId) {
    const history = JSON.parse(localStorage.getItem('browseHistory') || '[]');
    if (!history.includes(productId)) {
        history.unshift(productId);
        history.splice(10); // 只保留最近10个
        localStorage.setItem('browseHistory', JSON.stringify(history));
    }
}

选择指南:如何做出正确的选择

1. 什么时候选择Cookie?

选择Cookie的场景:

  • ✅ 需要服务器读取的数据(如用户认证、会话管理)
  • ✅ 数据量较小(< 4KB)
  • ✅ 需要自动传输到服务器的数据
  • ✅ 需要设置过期时间的数据
  • ✅ 需要跨子域名共享的数据

Cookie的最佳实践:

// 认证信息
document.cookie = "sessionId=abc123; secure; httponly; samesite=strict";

// 用户标识
document.cookie = "userId=12345; expires=Thu, 18 Dec 2024 12:00:00 UTC; path=/";

// 语言偏好(小数据)
document.cookie = "lang=zh-CN; expires=Thu, 18 Dec 2024 12:00:00 UTC; path=/";
2. 什么时候选择localStorage?

选择localStorage的场景:

  • ✅ 纯客户端数据,不需要服务器处理
  • ✅ 数据量较大(> 4KB)
  • ✅ 需要永久存储的数据
  • ✅ 用户偏好设置
  • ✅ 离线缓存数据

localStorage的最佳实践:

// 用户设置
localStorage.setItem('userSettings', JSON.stringify({
    theme: 'dark',
    fontSize: '16px',
    notifications: true
}));

// 离线缓存
localStorage.setItem('cachedData', JSON.stringify(largeDataObject));

// 游戏存档
localStorage.setItem('gameProgress', JSON.stringify(gameState));
3. 什么时候选择sessionStorage?

选择sessionStorage的场景:

  • ✅ 临时数据,会话结束后不需要保留
  • ✅ 表单数据暂存
  • ✅ 页面状态管理
  • ✅ 多步骤流程数据

sessionStorage的最佳实践:

// 表单数据暂存
sessionStorage.setItem('formData', JSON.stringify(formValues));

// 页面滚动位置
sessionStorage.setItem('scrollPosition', window.scrollY);

// 多步骤流程
sessionStorage.setItem('step1Data', JSON.stringify(step1Data));

混合使用策略:取长补短的"组合拳"

在实际项目中,很少有只使用一种存储方案的情况。聪明的开发者会根据不同的需求选择合适的存储方案,甚至组合使用。

1. 电商网站的存储策略
// 用户认证:Cookie
document.cookie = "authToken=xyz789; secure; httponly; samesite=strict";

// 用户偏好:localStorage
localStorage.setItem('userPreferences', JSON.stringify({
    theme: 'dark',
    language: 'zh-CN',
    currency: 'CNY'
}));

// 购物车:sessionStorage(临时)+ localStorage(持久化)
function addToCart(productId, quantity) {
    // 临时购物车
    const sessionCart = getSessionCart();
    sessionCart[productId] = (sessionCart[productId] || 0) + quantity;
    sessionStorage.setItem('cart', JSON.stringify(sessionCart));
    
    // 同步到持久化存储
    localStorage.setItem('persistentCart', JSON.stringify(sessionCart));
}

// 浏览历史:localStorage
function addToHistory(productId) {
    const history = JSON.parse(localStorage.getItem('browseHistory') || '[]');
    if (!history.includes(productId)) {
        history.unshift(productId);
        history.splice(10); // 只保留最近10个
        localStorage.setItem('browseHistory', JSON.stringify(history));
    }
}
2. 社交媒体的存储策略
// 用户认证:Cookie
document.cookie = "sessionToken=abc123; secure; httponly";

// 用户设置:localStorage
localStorage.setItem('socialSettings', JSON.stringify({
    autoPlay: false,
    notifications: true,
    privacyLevel: 'friends'
}));

// 草稿内容:sessionStorage
function saveDraft(content) {
    sessionStorage.setItem('postDraft', content);
}

// 离线内容缓存:localStorage
function cachePost(postId, postData) {
    const cache = JSON.parse(localStorage.getItem('postCache') || '{}');
    cache[postId] = {
        data: postData,
        timestamp: Date.now()
    };
    localStorage.setItem('postCache', JSON.stringify(cache));
}

如果觉得有用就收藏点赞,咱们下期再见!