pin_drop当前位置:知识文库 ❯ 图文

HTML5 API:HTML5 Web Storage API - 完整教程与代码示例

一、教程简介

Web Storage API 是 HTML5 提供的浏览器端键值对存储机制,包含 localStoragesessionStorage 两个存储对象。与传统的 Cookie 相比,Web Storage 提供了更大的存储容量(通常为 5-10MB)、更简洁的 API 和更安全的数据隔离,是现代 Web 应用中客户端数据持久化的首选方案。本教程将概述 Web Storage API 的整体架构、核心概念和基本用法,后续教程将分别深入讲解 sessionStoragelocalStorage


二、核心概念

Web Storage 与 Cookie 对比

特性 Cookie localStorage sessionStorage
容量 约 4KB 约 5-10MB 约 5-10MB
生命周期 可设过期时间 永久存储 会话结束即清除
请求携带 每次 HTTP 请求自动携带 不携带 不携带
作用域 同源 + 路径 同源(所有标签页共享) 同源 + 当前标签页
API 复杂度 需手动拼字符串 简洁的键值对 API 简洁的键值对 API
安全性 易受 CSRF 攻击 相对安全 相对安全

存储机制

代码示例

浏览器存储层次:
├── Cookie(4KB,自动发送)
├── Web Storage
│   ├── localStorage(持久化,同源共享)
│   └── sessionStorage(会话级,标签页隔离)
├── IndexedDB(大容量,结构化数据)
└── Cache API(请求/响应缓存)

同源策略

Web Storage 遵循同源策略,只有相同协议(protocol)、域名(host)和端口(port)的页面才能访问相同的存储数据:

代码示例

https://example.com/page1  ──┐
https://example.com/page2  ──┤── 共享同一份 localStorage
https://example.com:443/a  ──┘

http://example.com         ────── 不同源,无法访问
https://api.example.com    ────── 不同源,无法访问
https://example.com:8080   ────── 不同源,无法访问

Storage 事件

当同源的其他页面修改了 localStorage 时,当前页面会触发 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);
});

三、语法与用法

共用 API

localStoragesessionStorage 拥有完全相同的 API 接口:

代码示例

// 存储数据
storage.setItem(key, value);

// 读取数据
const value = storage.getItem(key);

// 删除指定键
storage.removeItem(key);

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

// 按索引获取键名
const key = storage.key(index);

// 获取存储条目数量
const length = storage.length;

基本用法示例

代码示例

// 设置数据
localStorage.setItem('username', '张三');
localStorage.setItem('age', '25');

// 读取数据
const username = localStorage.getItem('username'); // '张三'
const age = localStorage.getItem('age');           // '25'(字符串)

// 删除单条数据
localStorage.removeItem('username');

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

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

存储复杂数据

Web Storage 只能存储字符串,复杂数据需要序列化:

代码示例

// 存储对象
const user = { name: '张三', age: 25, hobbies: ['阅读', '编程'] };
localStorage.setItem('user', JSON.stringify(user));

// 读取对象
const storedUser = JSON.parse(localStorage.getItem('user'));
console.log(storedUser.name); // '张三'

// 存储数组
const tasks = [
    { id: 1, text: '学习 HTML5', done: false },
    { id: 2, text: '练习 CSS3', done: true }
];
localStorage.setItem('tasks', JSON.stringify(tasks));

// 读取数组
const storedTasks = JSON.parse(localStorage.getItem('tasks'));

四、代码示例

示例一:Web Storage 通用工具类

代码示例

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Web Storage API - 通用工具类</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body {
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
            background: #f0f2f5; min-height: 100vh; padding: 40px 20px;
        }
        .container { max-width: 720px; margin: 0 auto; }
        h1 { font-size: 26px; color: #1a1a2e; margin-bottom: 8px; }
        .desc { color: #666; font-size: 14px; margin-bottom: 28px; line-height: 1.6; }
        .card {
            background: #fff; border-radius: 12px; padding: 24px;
            margin-bottom: 20px; box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
        }
        .card h2 { font-size: 17px; color: #333; margin-bottom: 16px; }
        .btn-group { display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 16px; }
        button {
            padding: 8px 18px; border: none; border-radius: 6px;
            font-size: 13px; font-weight: 600; cursor: pointer; transition: all 0.2s;
        }
        .btn-primary { background: #4285f4; color: #fff; }
        .btn-success { background: #34a853; color: #fff; }
        .btn-danger { background: #ea4335; color: #fff; }
        .input-row { display: flex; gap: 10px; margin-bottom: 12px; }
        input {
            flex: 1; padding: 8px 12px; border: 1px solid #ddd;
            border-radius: 6px; font-size: 14px; outline: none;
        }
        input:focus { border-color: #4285f4; }
    </style>
</head>
<body>
    <div class="container">
        <h1>Web Storage 通用工具</h1>
        <p class="desc">体验 localStorage 和 sessionStorage 的基本操作</p>
        <div class="card">
            <h2>数据操作</h2>
            <div class="input-row">
                <input type="text" id="keyInput" placeholder="键名(Key)">
                <input type="text" id="valueInput" placeholder="值(Value)">
            </div>
            <div class="btn-group">
                <button class="btn-primary" onclick="setStorage('local')">存入 localStorage</button>
                <button class="btn-success" onclick="setStorage('session')">存入 sessionStorage</button>
                <button class="btn-danger" onclick="removeStorage()">删除数据</button>
            </div>
        </div>
    </div>
    <script>
        function setStorage(type) {
            const key = document.getElementById('keyInput').value.trim();
            const value = document.getElementById('valueInput').value.trim();
            if (!key) { alert('请输入键名'); return; }
            const storage = type === 'local' ? localStorage : sessionStorage;
            storage.setItem(key, value);
            alert('已存入 ' + type + 'Storage');
        }
        function removeStorage() {
            const key = document.getElementById('keyInput').value.trim();
            if (!key) { alert('请输入键名'); return; }
            localStorage.removeItem(key);
            sessionStorage.removeItem(key);
            alert('已删除键: ' + key);
        }
    </script>
</body>
</html>

示例二:Storage 事件跨标签页通信

代码示例

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Web Storage - 跨标签页通信</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body {
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
            background: #2d3436; min-height: 100vh;
            display: flex; justify-content: center; align-items: center; padding: 20px;
        }
        .container {
            background: #fff; border-radius: 16px; padding: 36px;
            max-width: 500px; width: 100%;
            box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
        }
        h1 { font-size: 22px; color: #2d3436; margin-bottom: 6px; }
        .desc { color: #636e72; font-size: 13px; margin-bottom: 24px; line-height: 1.6; }
        .chat-area {
            background: #f8f9fa; border-radius: 10px; padding: 16px;
            height: 300px; overflow-y: auto; margin-bottom: 16px;
        }
        .message {
            margin-bottom: 10px; padding: 8px 14px;
            border-radius: 10px; max-width: 80%; font-size: 14px; line-height: 1.5;
        }
        .message.sent { background: #0984e3; color: #fff; margin-left: auto; }
        .message.received { background: #dfe6e9; color: #2d3436; }
        .input-area { display: flex; gap: 10px; }
        input {
            flex: 1; padding: 10px 14px; border: 1px solid #ddd;
            border-radius: 8px; font-size: 14px; outline: none;
        }
        input:focus { border-color: #0984e3; }
        button {
            padding: 10px 20px; background: #0984e3; color: #fff;
            border: none; border-radius: 8px; font-size: 14px; font-weight: 600; cursor: pointer;
        }
        button:hover { background: #0770c2; }
    </style>
</head>
<body>
    <div class="container">
        <h1>跨标签页通信</h1>
        <p class="desc">在多个标签页中打开此页面,通过 localStorage 的 storage 事件实现消息同步。</p>
        <div class="chat-area" id="chatArea"></div>
        <div class="input-area">
            <input type="text" id="msgInput" placeholder="输入消息..." onkeydown="if(event.key==='Enter')sendMessage()">
            <button onclick="sendMessage()">发送</button>
        </div>
    </div>
    <script>
        const chatArea = document.getElementById('chatArea');
        const msgInput = document.getElementById('msgInput');
        const tabId = 'tab_' + Date.now() + '_' + Math.random().toString(36).substr(2, 6);
        function sendMessage() {
            const text = msgInput.value.trim();
            if (!text) return;
            const message = {
                id: Date.now(), tabId: tabId, text: text,
                time: new Date().toLocaleTimeString('zh-CN')
            };
            localStorage.setItem('chat_message', JSON.stringify(message));
            appendMessage(message, 'sent');
            msgInput.value = '';
        }
        window.addEventListener('storage', function (e) {
            if (e.key === 'chat_message' && e.newValue) {
                try {
                    const message = JSON.parse(e.newValue);
                    if (message.tabId !== tabId) appendMessage(message, 'received');
                } catch (err) { console.error('消息解析失败:', err); }
            }
        });
        function appendMessage(message, type) {
            const div = document.createElement('div');
            div.className = 'message ' + type;
            div.innerHTML = '<div>' + message.text + '</div><div style="font-size:11px;opacity:0.7;margin-top:4px;">' + message.time + '</div>';
            chatArea.appendChild(div);
            chatArea.scrollTop = chatArea.scrollHeight;
        }
    </script>
</body>
</html>

五、浏览器兼容性

浏览器 支持版本 存储容量
Chrome 4+ 约 10MB
Firefox 3.5+ 约 10MB
Safari 4+ 约 5MB
Edge 12+ 约 10MB
Opera 10.5+ 约 10MB
IE 8+ 约 5-10MB
iOS Safari 3.2+ 约 5MB
Android 2.1+ 视实现而定

六、注意事项与最佳实践

1. 数据安全

  • 不要存储敏感信息:Web Storage 数据可被同源页面和浏览器开发者工具直接查看

  • 防范 XSS 攻击:存储的数据如果直接插入 DOM,必须进行转义处理

  • 设置合理的存储策略:定期清理过期数据,避免存储无限增长

2. 存储限制

  • 容量有限:通常为 5-10MB,超出会抛出 QuotaExceededError

  • 写入可能失败:隐私模式下某些浏览器会限制或禁用 Web Storage

  • 及时捕获异常:写入操作应包裹在 try-catch 中

代码示例

function safeSetItem(key, value) {
    try {
        localStorage.setItem(key, value);
    } catch (e) {
        if (e.name === 'QuotaExceededError' ||
            e.name === 'NS_ERROR_DOM_QUOTA_REACHED') {
            console.error('存储空间已满');
            // 降级处理:清理旧数据或提示用户
        }
    }
}

3. 数据类型处理

  • 所有值都是字符串:存储数字、布尔值时注意类型转换

  • 使用 JSON 序列化:存储对象和数组时使用 JSON.stringify/JSON.parse

  • 处理 null 和 undefinedgetItem 返回 null 表示键不存在

4. 性能考虑

  • 避免频繁读写:Web Storage 是同步操作,会阻塞主线程

  • 大数据使用 IndexedDB:超过 1MB 的数据建议使用 IndexedDB

  • 批量操作优化:多次操作可合并为一次 JSON 序列化


七、代码规范示例

代码示例

// 推荐:封装 Web Storage 工具类
class StorageManager {
    constructor(type = 'local', prefix = '') {
        this.storage = type === 'local' ? localStorage : sessionStorage;
        this.prefix = prefix;
    }

    _getKey(key) {
        return this.prefix ? this.prefix + '_' + key : key;
    }

    set(key, value, expireMs = null) {
        const data = {
            value: value,
            timestamp: Date.now(),
            expire: expireMs ? Date.now() + expireMs : null
        };
        try {
            this.storage.setItem(this._getKey(key), JSON.stringify(data));
            return true;
        } catch (e) {
            console.error('Storage setItem 失败:', e);
            return false;
        }
    }

    get(key, defaultValue = null) {
        try {
            const raw = this.storage.getItem(this._getKey(key));
            if (raw === null) return defaultValue;
            const data = JSON.parse(raw);
            if (data.expire && Date.now() > data.expire) {
                this.remove(key);
                return defaultValue;
            }
            return data.value;
        } catch (e) {
            console.error('Storage getItem 失败:', e);
            return defaultValue;
        }
    }

    remove(key) {
        this.storage.removeItem(this._getKey(key));
    }

    has(key) {
        return this.get(key) !== null;
    }

    clear() {
        if (!this.prefix) {
            this.storage.clear();
            return;
        }
        const keysToRemove = [];
        for (let i = 0; i < this.storage.length; i++) {
            const k = this.storage.key(i);
            if (k.startsWith(this.prefix + '_')) keysToRemove.push(k);
        }
        keysToRemove.forEach(k => this.storage.removeItem(k));
    }

    getSize() {
        let total = 0;
        for (let i = 0; i < this.storage.length; i++) {
            const key = this.storage.key(i);
            const value = this.storage.getItem(key);
            total += key.length + value.length;
        }
        return (total * 2 / 1024).toFixed(2) + ' KB';
    }
}

// 使用示例
const store = new StorageManager('local', 'myapp');
store.set('user', { name: '张三', age: 25 });
store.set('token', 'abc123', 3600000); // 1小时过期
console.log(store.get('user'));
console.log(store.getSize());

八、常见问题与解决方案

常见问题

隐私模式下 Web Storage 不可用?

原因:部分浏览器在隐私模式下限制或禁用 Web Storage。
解决方案:使用检测函数判断存储是否可用,不可用时降级到内存对象或 Cookie。

存储空间超出限制怎么办?

解决方案:实现数据淘汰策略(如 LRU)、定期清理过期数据、压缩存储数据、大数据迁移到 IndexedDB。

JSON.parse 解析失败?

原因:存储的数据可能被手动修改或损坏。
解决方案:使用安全的 JSON 解析函数,捕获异常并返回默认值。

如何实现数据过期机制?

解决方案:在存储数据时附带时间戳和过期时间,读取时检查是否过期。可以使用封装的工具类自动处理过期逻辑。


九、总结

Web Storage API 提供了简洁高效的客户端键值对存储方案。localStorage 适合持久化数据(如用户偏好设置),sessionStorage 适合临时会话数据(如表单临时保存)。使用时需注意存储容量限制、数据安全(防范 XSS)、类型转换(所有值为字符串)和异常处理。通过封装工具类可以实现过期机制、前缀隔离、安全读写等高级功能,让 Web Storage 在实际项目中更加可靠易用。

标签: Web Storage localStorage sessionStorage 客户端存储 键值对存储 数据持久化

本文涉及AI创作

内容由AI创作,请仔细甄别

list快速访问

上一篇: HTML5 API:HTML5 Drag and Drop API - 完整教程与代码示例 下一篇: HTML5 API:HTML5 SessionStorage - 完整教程与代码示例

poll相关推荐