JavaScript Web Storage (localStorage / sessionStorage)

Web Storage API 提供了在瀏覽器中儲存資料的機制,比 Cookie 更簡單易用,儲存容量也更大(通常 5-10 MB)。

localStorage 與 sessionStorage

Web Storage 分為兩種:

特性localStoragesessionStorage
資料生命週期永久保存,除非手動清除分頁關閉時清除
作用範圍同源的所有分頁共享僅限當前分頁
儲存容量約 5-10 MB約 5-10 MB

兩者的 API 用法完全相同,只是資料的生命週期不同。

基本操作

儲存資料 - setItem()

// 儲存字串
localStorage.setItem('username', 'Mike');
sessionStorage.setItem('token', 'abc123');

// 也可以用屬性方式存取
localStorage.username = 'Mike';
localStorage['username'] = 'Mike';

讀取資料 - getItem()

var username = localStorage.getItem('username');
console.log(username);  // 'Mike'

// 如果 key 不存在,回傳 null
var notExist = localStorage.getItem('notExist');
console.log(notExist);  // null

// 屬性方式存取
var username = localStorage.username;

刪除資料 - removeItem()

localStorage.removeItem('username');

清除所有資料 - clear()

localStorage.clear();
sessionStorage.clear();

取得資料數量 - length

console.log(localStorage.length);

取得第 n 個 key - key()

// 取得第一個 key
var firstKey = localStorage.key(0);

儲存物件和陣列

Web Storage 只能儲存字串。如果要儲存物件或陣列,需要先轉換成 JSON 字串:

// 儲存物件
var user = {
    name: 'Mike',
    age: 30,
    email: 'mike@example.com'
};
localStorage.setItem('user', JSON.stringify(user));

// 讀取物件
var userStr = localStorage.getItem('user');
var user = JSON.parse(userStr);
console.log(user.name);  // 'Mike'
// 儲存陣列
var fruits = ['Apple', 'Banana', 'Orange'];
localStorage.setItem('fruits', JSON.stringify(fruits));

// 讀取陣列
var fruits = JSON.parse(localStorage.getItem('fruits'));
console.log(fruits[0]);  // 'Apple'
讀取不存在的 key 會得到 null,而 JSON.parse(null) 會回傳 null,所以記得做檢查。

遍歷所有資料

// 方法一:使用 for 迴圈
for (var i = 0; i < localStorage.length; i++) {
    var key = localStorage.key(i);
    var value = localStorage.getItem(key);
    console.log(key + ': ' + value);
}

// 方法二:使用 Object.keys()
Object.keys(localStorage).forEach(function(key) {
    console.log(key + ': ' + localStorage.getItem(key));
});

storage 事件

當 localStorage 的資料在其他分頁被修改時,會觸發 storage 事件。這可以用來在多個分頁之間同步資料:

window.addEventListener('storage', function(event) {
    console.log('Key: ' + event.key);
    console.log('舊值: ' + event.oldValue);
    console.log('新值: ' + event.newValue);
    console.log('來源: ' + event.url);
});
storage 事件只會在其他同源分頁觸發,不會在當前分頁觸發。

實用範例

記住使用者偏好設定

// 儲存使用者設定
function saveSettings(settings) {
    localStorage.setItem('userSettings', JSON.stringify(settings));
}

// 載入使用者設定
function loadSettings() {
    var settings = localStorage.getItem('userSettings');
    return settings ? JSON.parse(settings) : {
        theme: 'light',
        language: 'zh-TW',
        fontSize: 16
    };
}

// 使用
var settings = loadSettings();
console.log(settings.theme);

settings.theme = 'dark';
saveSettings(settings);

簡易購物車

var cart = {
    items: [],
    
    load: function() {
        var data = localStorage.getItem('cart');
        this.items = data ? JSON.parse(data) : [];
    },
    
    save: function() {
        localStorage.setItem('cart', JSON.stringify(this.items));
    },
    
    add: function(product) {
        this.items.push(product);
        this.save();
    },
    
    remove: function(index) {
        this.items.splice(index, 1);
        this.save();
    },
    
    clear: function() {
        this.items = [];
        localStorage.removeItem('cart');
    }
};

// 使用
cart.load();
cart.add({ id: 1, name: 'iPhone', price: 30000 });
cart.add({ id: 2, name: 'MacBook', price: 50000 });
console.log(cart.items);

表單自動儲存

var form = document.getElementById('myForm');
var formKey = 'formDraft';

// 載入暫存的表單資料
function loadDraft() {
    var draft = localStorage.getItem(formKey);
    if (draft) {
        var data = JSON.parse(draft);
        form.title.value = data.title || '';
        form.content.value = data.content || '';
    }
}

// 自動儲存表單
function saveDraft() {
    var data = {
        title: form.title.value,
        content: form.content.value
    };
    localStorage.setItem(formKey, JSON.stringify(data));
}

// 輸入時自動儲存
form.addEventListener('input', saveDraft);

// 送出後清除暫存
form.addEventListener('submit', function() {
    localStorage.removeItem(formKey);
});

// 頁面載入時還原
loadDraft();

檢查瀏覽器支援

function storageAvailable(type) {
    try {
        var storage = window[type];
        var test = '__storage_test__';
        storage.setItem(test, test);
        storage.removeItem(test);
        return true;
    } catch (e) {
        return false;
    }
}

if (storageAvailable('localStorage')) {
    // localStorage 可用
} else {
    // localStorage 不可用,可能是隱私模式
}
在某些瀏覽器的隱私/無痕模式下,Web Storage 可能無法使用或有限制。
特性Web StorageCookie
容量5-10 MB約 4 KB
自動傳送不會傳送到伺服器每次請求都會傳送
API簡單直覺較繁瑣
過期機制無(localStorage)/ 分頁關閉(sessionStorage)可設定過期時間

選擇建議:

  • 使用 Web Storage:純前端資料儲存,如使用者偏好、快取資料
  • 使用 Cookie:需要傳送到伺服器的資料,如身份驗證 token

安全性注意事項

  1. 不要儲存敏感資料:Web Storage 的資料可以被 JavaScript 讀取,容易受到 XSS 攻擊
  2. 同源政策:資料只能被同源的頁面存取,但同源的所有頁面都能存取
  3. 驗證資料:從 Storage 讀取的資料應該驗證後再使用
// 讀取資料時加上驗證
function getUser() {
    try {
        var data = localStorage.getItem('user');
        if (!data) return null;
        
        var user = JSON.parse(data);
        // 驗證資料格式
        if (user && typeof user.name === 'string') {
            return user;
        }
        return null;
    } catch (e) {
        return null;
    }
}