التخزين المحلي وتخزين الجلسة
مقدمة في واجهة Web Storage API
توفر واجهة Web Storage API آليات للمتصفحات لتخزين أزواج المفاتيح والقيم محليا على جهاز المستخدم. قبل وجود Web Storage، اعتمد المطورون على ملفات تعريف الارتباط (Cookies) لحفظ البيانات في المتصفح -- لكن ملفات تعريف الارتباط محدودة بحوالي 4 كيلوبايت، وترسل مع كل طلب HTTP، ومعقدة في التعامل معها. تحل واجهة Web Storage API هذه المشاكل من خلال تقديم كائنين للتخزين: localStorage وsessionStorage. كلاهما يسمح لك بتخزين كمية أكبر بكثير من البيانات (عادة حوالي 5 ميغابايت لكل أصل)، ولا ترسل مع طلبات HTTP، وتوفر واجهة برمجة JavaScript بسيطة ومتزامنة. فهم هذه الأدوات ضروري لبناء تطبيقات ويب حديثة تتذكر تفضيلات المستخدم وتخزن البيانات مؤقتا للاستخدام دون اتصال وتوفر تجربة مستخدم أكثر سلاسة.
localStorage مقابل sessionStorage
بينما يتشارك كل من localStorage و sessionStorage نفس أساليب الواجهة البرمجية، إلا أنهما يختلفان في نقطة حرجة: عمر البيانات.
- localStorage -- تستمر البيانات إلى أجل غير مسمى حتى يتم حذفها صراحة بواسطة التطبيق أو يمسح المستخدم بيانات المتصفح. إذا أغلقت المتصفح وأوقفت تشغيل الكمبيوتر وعدت بعد أسبوع، فإن البيانات لا تزال موجودة.
- sessionStorage -- البيانات متاحة فقط طوال مدة جلسة الصفحة. عندما يغلق المستخدم علامة التبويب أو نافذة المتصفح، تحذف البيانات تلقائيا. كل علامة تبويب أو نافذة لها sessionStorage منفصل خاص بها، حتى لو كانت تشير إلى نفس عنوان URL.
كلاهما محدد النطاق بالـ أصل (البروتوكول + النطاق + المنفذ)، مما يعني أن البيانات المخزنة على https://example.com لا يمكن الوصول إليها من https://other.com أو حتى http://example.com (بروتوكول مختلف).
مثال: الفرق الأساسي بين localStorage و sessionStorage
// localStorage يستمر عبر الجلسات
localStorage.setItem('theme', 'dark');
// أغلق المتصفح، أعد فتحه، وهذا لا يزال يعمل:
console.log(localStorage.getItem('theme')); // "dark"
// sessionStorage يمسح عند إغلاق علامة التبويب
sessionStorage.setItem('tempData', 'hello');
// أغلق علامة التبويب، افتح واحدة جديدة:
console.log(sessionStorage.getItem('tempData')); // null
طريقة setItem()
تخزن طريقة setItem(key, value) زوج مفتاح-قيمة. يجب أن يكون كل من المفتاح والقيمة سلاسل نصية. إذا كان المفتاح موجودا بالفعل، فإن setItem تكتب فوق القيمة السابقة دون تحذير. هذه الطريقة هي الطريقة الأساسية لكتابة البيانات في Web Storage.
مثال: تخزين القيم باستخدام setItem()
// تخزين قيم نصية بسيطة
localStorage.setItem('username', 'Ahmed');
localStorage.setItem('language', 'en');
localStorage.setItem('fontSize', '16');
// الكتابة فوق قيمة موجودة
localStorage.setItem('language', 'ar');
console.log(localStorage.getItem('language')); // "ar"
// sessionStorage يعمل بنفس الطريقة
sessionStorage.setItem('currentPage', '3');
sessionStorage.setItem('searchQuery', 'javascript tutorials');
localStorage.setItem('count', 42)، سيتم تحويله تلقائيا إلى السلسلة النصية "42". عند استرجاعه، ستحصل على سلسلة نصية وليس رقما. يجب عليك تحويله مرة أخرى باستخدام parseInt() أو Number().طريقة getItem()
تسترجع طريقة getItem(key) القيمة المرتبطة بالمفتاح المعطى. إذا لم يكن المفتاح موجودا، فإنها ترجع null -- وليس undefined، وليس سلسلة نصية فارغة، بل تحديدا null. هذا مهم للتحققات الشرطية.
مثال: استرجاع القيم باستخدام getItem()
// استرجاع القيم المخزنة
const username = localStorage.getItem('username');
console.log(username); // "Ahmed"
// التحقق من المفاتيح غير الموجودة
const missing = localStorage.getItem('nonExistentKey');
console.log(missing); // null
// نمط شائع: توفير قيمة افتراضية
const theme = localStorage.getItem('theme') || 'light';
console.log(theme); // "light" إذا لم يتم تخزين سمة
// استخدام مع المنطق الشرطي
const savedLanguage = localStorage.getItem('language');
if (savedLanguage !== null) {
applyLanguage(savedLanguage);
} else {
applyLanguage('en'); // افتراضي
}
طريقة removeItem()
تزيل طريقة removeItem(key) زوج مفتاح-قيمة واحد من التخزين. إذا لم يكن المفتاح موجودا، فإن الطريقة لا تفعل شيئا بصمت -- ولا تطرح خطأ. هذا يجعلها آمنة للاستدعاء دون التحقق أولا.
مثال: إزالة عناصر فردية
// تخزين بعض البيانات
localStorage.setItem('token', 'abc123');
localStorage.setItem('username', 'Ahmed');
// إزالة الرمز المميز
localStorage.removeItem('token');
console.log(localStorage.getItem('token')); // null
// اسم المستخدم لا يزال موجودا
console.log(localStorage.getItem('username')); // "Ahmed"
// إزالة مفتاح غير موجود آمنة
localStorage.removeItem('neverExisted'); // لا يوجد خطأ
طريقة clear()
تزيل طريقة clear() جميع أزواج المفاتيح والقيم من كائن التخزين. استخدمها بحذر لأنها تمسح كل شيء لذلك الأصل. هذا مفيد لتنفيذ ميزة "تسجيل الخروج" أو زر "إعادة تعيين جميع الإعدادات".
مثال: مسح جميع التخزين
// تخزين عناصر متعددة
localStorage.setItem('theme', 'dark');
localStorage.setItem('language', 'ar');
localStorage.setItem('fontSize', '18');
console.log(localStorage.length); // 3
// مسح كل شيء
localStorage.clear();
console.log(localStorage.length); // 0
console.log(localStorage.getItem('theme')); // null
clear() جميع العناصر للأصل الحالي، بما في ذلك البيانات المخزنة بواسطة نصوص برمجية أو مكتبات أخرى على نفس النطاق. في تطبيقات الإنتاج، فضل removeItem() للحذف المستهدف أو استخدم بادئات المفاتيح (مثل myApp_theme) لتحديد نطاق بياناتك وتجنب التعارضات.طريقة key() وخاصية length
ترجع طريقة key(index) اسم المفتاح في موضع الفهرس المعطى. مع خاصية length، يمكنك التكرار عبر جميع العناصر المخزنة. ترتيب المفاتيح غير مضمون أن يطابق الترتيب الذي أضيفت به، لذا لا تعتمد على مواضع فهرس محددة.
مثال: التكرار عبر التخزين
// تخزين بعض البيانات
localStorage.setItem('color', 'blue');
localStorage.setItem('size', 'large');
localStorage.setItem('mode', 'dark');
// التحقق من عدد العناصر المخزنة
console.log(localStorage.length); // 3
// الحصول على أسماء المفاتيح بالفهرس
console.log(localStorage.key(0)); // قد يكون "color" أو "size" أو "mode"
// التكرار عبر جميع العناصر
for (let i = 0; i < localStorage.length; i++) {
const keyName = localStorage.key(i);
const value = localStorage.getItem(keyName);
console.log(keyName + ': ' + value);
}
// الطريقة الحديثة: استخدام Object.keys
const allKeys = Object.keys(localStorage);
allKeys.forEach(function(key) {
console.log(key + ' = ' + localStorage.getItem(key));
});
تخزين الكائنات والمصفوفات باستخدام JSON
بما أن Web Storage يقبل السلاسل النصية فقط، لا يمكنك تخزين الكائنات أو المصفوفات مباشرة. محاولة تخزين كائن ستنتج السلسلة النصية "[object Object]"، وهي عديمة الفائدة. الحل هو استخدام JSON.stringify() عند التخزين وJSON.parse() عند الاسترجاع. هذا أحد أكثر الأنماط شيوعا في استخدام Web Storage.
مثال: العمل مع الكائنات والمصفوفات
// تخزين كائن -- الطريقة الخاطئة
localStorage.setItem('user', { name: 'Ahmed' });
console.log(localStorage.getItem('user')); // "[object Object]" -- عديم الفائدة!
// تخزين كائن -- الطريقة الصحيحة
const user = {
name: 'Ahmed',
email: 'ahmed@example.com',
preferences: {
theme: 'dark',
language: 'ar'
}
};
localStorage.setItem('user', JSON.stringify(user));
// استرجاع الكائن
const storedUser = JSON.parse(localStorage.getItem('user'));
console.log(storedUser.name); // "Ahmed"
console.log(storedUser.preferences.theme); // "dark"
// تخزين مصفوفة
const recentSearches = ['javascript', 'web storage', 'localStorage'];
localStorage.setItem('searches', JSON.stringify(recentSearches));
// استرجاع المصفوفة
const searches = JSON.parse(localStorage.getItem('searches'));
console.log(searches[0]); // "javascript"
console.log(searches.length); // 3
JSON.parse() في كتلة try-catch في كود الإنتاج. إذا كانت القيمة المخزنة قد تلفت أو تم تعديلها يدويا، فإن JSON.parse() ستطرح SyntaxError. البرمجة الدفاعية تمنع تطبيقك بالكامل من الانهيار بسبب بيانات تخزين سيئة.مثال: تحليل JSON الآمن
function getStoredObject(key, defaultValue) {
try {
const stored = localStorage.getItem(key);
if (stored === null) {
return defaultValue;
}
return JSON.parse(stored);
} catch (error) {
console.error('خطأ في تحليل البيانات المخزنة للمفتاح: ' + key, error);
return defaultValue;
}
}
// الاستخدام
const settings = getStoredObject('settings', { theme: 'light', fontSize: 16 });
console.log(settings.theme); // يستخدم القيمة المخزنة أو الافتراضية
حدث Storage
يتم إطلاق حدث storage على كائن window عندما يتم تعديل منطقة تخزين من علامة تبويب أو نافذة أخرى من نفس الأصل. هذا الحدث لا يتم إطلاقه في علامة التبويب التي أجرت التغيير -- فقط في علامات التبويب الأخرى. هذا مفيد للغاية لمزامنة الحالة عبر علامات تبويب متعددة، مثل الحفاظ على تسجيل دخول المستخدم أو مزامنة تغييرات السمة.
مثال: الاستماع لتغييرات التخزين عبر علامات التبويب
// الاستماع للتغييرات التي تتم في علامات تبويب أخرى
window.addEventListener('storage', function(event) {
console.log('تغير التخزين!');
console.log('المفتاح:', event.key); // أي مفتاح تغير
console.log('القيمة القديمة:', event.oldValue); // القيمة السابقة
console.log('القيمة الجديدة:', event.newValue); // القيمة الجديدة
console.log('العنوان:', event.url); // أي صفحة أجرت التغيير
console.log('منطقة التخزين:', event.storageArea);
// الاستجابة لتغييرات محددة
if (event.key === 'theme') {
document.body.className = event.newValue;
}
// عند استدعاء clear()، يكون المفتاح null
if (event.key === null) {
console.log('تم مسح جميع التخزين');
}
});
// في علامة تبويب أخرى، أطلق الحدث:
// localStorage.setItem('theme', 'dark');
حدود التخزين والحصة
يخصص كل متصفح حصة تخزين لكل أصل، عادة حوالي 5 ميغابايت لـ localStorage و5 ميغابايت لـ sessionStorage. يشير هذا الحد إلى الحجم الإجمالي لجميع أزواج المفاتيح والقيم مجتمعة، مقاسة بأحرف UTF-16 (لذا كل حرف يأخذ 2 بايت). عندما تتجاوز الحصة، يطرح المتصفح خطأ QuotaExceededError. يجب عليك دائما التعامل مع هذا الاحتمال بأناقة.
مثال: التعامل مع أخطاء حصة التخزين
function safeSetItem(key, value) {
try {
localStorage.setItem(key, value);
return true;
} catch (error) {
if (error.name === 'QuotaExceededError' ||
error.code === 22 ||
error.code === 1014) {
console.error('تم تجاوز حصة التخزين!');
// استراتيجية: إزالة العناصر القديمة لإفساح المجال
cleanOldData();
try {
localStorage.setItem(key, value);
return true;
} catch (retryError) {
console.error('لا يزال لا توجد مساحة بعد التنظيف');
return false;
}
}
throw error;
}
}
function cleanOldData() {
// إزالة العناصر ذات الطابع الزمني الأقدم من 7 أيام
const oneWeekAgo = Date.now() - (7 * 24 * 60 * 60 * 1000);
for (let i = localStorage.length - 1; i >= 0; i--) {
const key = localStorage.key(i);
if (key.startsWith('cache_')) {
const item = JSON.parse(localStorage.getItem(key));
if (item.timestamp < oneWeekAgo) {
localStorage.removeItem(key);
}
}
}
}
// تقدير الاستخدام الحالي
function getStorageUsage() {
let total = 0;
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
const value = localStorage.getItem(key);
total += key.length + value.length;
}
return (total * 2) / 1024; // الحجم التقريبي بالكيلوبايت
}
console.log('التخزين المستخدم: ~' + getStorageUsage().toFixed(2) + ' كيلوبايت');
التحقق من توفر التخزين
ليست كل المتصفحات أو أوضاع التصفح تدعم Web Storage. في وضع التصفح الخاص أو المتخفي، بعض المتصفحات تعطل localStorage بالكامل أو تضع حصة صفرية. Safari في الوضع الخاص، على سبيل المثال، يطرح خطأ عند محاولة استخدام setItem. تحقق دائما من التوفر قبل استخدام التخزين.
مثال: اكتشاف الميزات لـ Web Storage
function isStorageAvailable(type) {
let storage;
try {
storage = window[type];
const testKey = '__storage_test__';
storage.setItem(testKey, 'test');
storage.removeItem(testKey);
return true;
} catch (error) {
return (
error instanceof DOMException &&
error.name === 'QuotaExceededError' &&
storage &&
storage.length !== 0
);
}
}
// التحقق قبل الاستخدام
if (isStorageAvailable('localStorage')) {
console.log('localStorage متاح');
localStorage.setItem('ready', 'true');
} else {
console.log('localStorage غير متاح');
// بديل: استخدام كائن في الذاكرة
const fallbackStorage = {};
}
if (isStorageAvailable('sessionStorage')) {
console.log('sessionStorage متاح');
}
بناء مبدل السمات
أحد أكثر الاستخدامات العملية لـ localStorage هو حفظ تفضيلات واجهة المستخدم. يسمح مبدل السمات للمستخدمين بالاختيار بين الوضع الفاتح والداكن، مع تذكر تفضيلهم عبر الزيارات. هذا النمط يجمع بين localStorage والتعامل مع DOM لتقديم تجربة مستخدم مصقولة.
مثال: مبدل السمات الكامل
// تطبيق السمة المحفوظة عند تحميل الصفحة
function initializeTheme() {
const savedTheme = localStorage.getItem('theme') || 'light';
document.documentElement.setAttribute('data-theme', savedTheme);
updateToggleButton(savedTheme);
}
// التبديل بين الفاتح والداكن
function toggleTheme() {
const currentTheme = document.documentElement.getAttribute('data-theme');
const newTheme = currentTheme === 'light' ? 'dark' : 'light';
document.documentElement.setAttribute('data-theme', newTheme);
localStorage.setItem('theme', newTheme);
updateToggleButton(newTheme);
}
function updateToggleButton(theme) {
const button = document.getElementById('themeToggle');
if (button) {
button.textContent = theme === 'light' ? 'التبديل إلى الداكن' : 'التبديل إلى الفاتح';
button.setAttribute('aria-label',
'السمة الحالية: ' + theme + '. انقر للتبديل.');
}
}
// التهيئة عند تحميل الصفحة
document.addEventListener('DOMContentLoaded', initializeTheme);
// ربط معالج النقر
document.getElementById('themeToggle').addEventListener('click', toggleTheme);
// المزامنة عبر علامات التبويب
window.addEventListener('storage', function(event) {
if (event.key === 'theme') {
document.documentElement.setAttribute('data-theme', event.newValue);
updateToggleButton(event.newValue);
}
});
بناء سلة تسوق باستخدام localStorage
سلة التسوق هي حالة استخدام كلاسيكية لـ localStorage. تستمر السلة حتى لو أغلق المستخدم المتصفح عن طريق الخطأ، فلا يفقد اختياراته. هذا المثال يوضح تخزين وتحديث وإزالة كائنات معقدة في localStorage.
مثال: مدير سلة التسوق
const CartManager = {
STORAGE_KEY: 'shopping_cart',
getCart: function() {
const data = localStorage.getItem(this.STORAGE_KEY);
return data ? JSON.parse(data) : [];
},
saveCart: function(cart) {
localStorage.setItem(this.STORAGE_KEY, JSON.stringify(cart));
},
addItem: function(product) {
const cart = this.getCart();
const existing = cart.find(function(item) {
return item.id === product.id;
});
if (existing) {
existing.quantity += 1;
} else {
cart.push({
id: product.id,
name: product.name,
price: product.price,
quantity: 1
});
}
this.saveCart(cart);
return cart;
},
removeItem: function(productId) {
let cart = this.getCart();
cart = cart.filter(function(item) {
return item.id !== productId;
});
this.saveCart(cart);
return cart;
},
updateQuantity: function(productId, newQuantity) {
const cart = this.getCart();
const item = cart.find(function(item) {
return item.id === productId;
});
if (item) {
if (newQuantity <= 0) {
return this.removeItem(productId);
}
item.quantity = newQuantity;
this.saveCart(cart);
}
return cart;
},
getTotal: function() {
const cart = this.getCart();
return cart.reduce(function(sum, item) {
return sum + (item.price * item.quantity);
}, 0);
},
getItemCount: function() {
const cart = this.getCart();
return cart.reduce(function(count, item) {
return count + item.quantity;
}, 0);
},
clearCart: function() {
localStorage.removeItem(this.STORAGE_KEY);
}
};
// الاستخدام
CartManager.addItem({ id: 1, name: 'حاسب محمول', price: 999 });
CartManager.addItem({ id: 2, name: 'فأرة', price: 29 });
CartManager.addItem({ id: 1, name: 'حاسب محمول', price: 999 }); // الكمية تصبح 2
console.log(CartManager.getCart());
console.log('الإجمالي: $' + CartManager.getTotal()); // الإجمالي: $2027
console.log('العناصر: ' + CartManager.getItemCount()); // العناصر: 3
استمرار بيانات النماذج
لا شيء يحبط المستخدمين أكثر من فقدان نموذج طويل كانوا يملؤونه بسبب تحديث عرضي للصفحة أو تنقل. باستخدام sessionStorage، يمكنك حفظ مدخلات النموذج تلقائيا أثناء كتابة المستخدم واستعادتها إذا أعيد تحميل الصفحة. بما أن البيانات تحتاج فقط للبقاء ضمن الجلسة الحالية، فإن sessionStorage هو الخيار المثالي.
مثال: الحفظ التلقائي لبيانات النموذج
function initFormPersistence(formId) {
const form = document.getElementById(formId);
if (!form) return;
const storageKey = 'form_' + formId;
// استعادة البيانات المحفوظة عند تحميل الصفحة
const savedData = sessionStorage.getItem(storageKey);
if (savedData) {
const data = JSON.parse(savedData);
Object.keys(data).forEach(function(fieldName) {
const field = form.elements[fieldName];
if (field) {
if (field.type === 'checkbox') {
field.checked = data[fieldName];
} else if (field.type === 'radio') {
const radio = form.querySelector(
'input[name="' + fieldName + '"][value="' + data[fieldName] + '"]'
);
if (radio) radio.checked = true;
} else {
field.value = data[fieldName];
}
}
});
}
// حفظ البيانات أثناء الكتابة أو تغيير الحقول
form.addEventListener('input', function() {
const formData = {};
const elements = form.elements;
for (let i = 0; i < elements.length; i++) {
const el = elements[i];
if (el.name && el.type !== 'submit' && el.type !== 'button') {
if (el.type === 'checkbox') {
formData[el.name] = el.checked;
} else if (el.type === 'radio') {
if (el.checked) formData[el.name] = el.value;
} else {
formData[el.name] = el.value;
}
}
}
sessionStorage.setItem(storageKey, JSON.stringify(formData));
});
// مسح البيانات المحفوظة عند الإرسال الناجح
form.addEventListener('submit', function() {
sessionStorage.removeItem(storageKey);
});
}
// التهيئة لنموذج الاتصال
initFormPersistence('contactForm');
تخزين تفضيلات المستخدم
يجمع نظام تفضيلات المستخدم الشامل إعدادات متعددة في إدخال localStorage واحد. هذا النهج يبقي تخزينك منظما ويسهل إضافة تفضيلات جديدة في المستقبل. إليك مدير تفضيلات قوي مع قيم افتراضية والتحقق من الصحة.
مثال: مدير تفضيلات المستخدم
const PreferencesManager = {
STORAGE_KEY: 'user_preferences',
defaults: {
theme: 'light',
language: 'en',
fontSize: 16,
showNotifications: true,
autoSave: true,
itemsPerPage: 10
},
getAll: function() {
try {
const stored = localStorage.getItem(this.STORAGE_KEY);
if (stored) {
const parsed = JSON.parse(stored);
// الدمج مع الافتراضيات للتعامل مع تفضيلات جديدة أضيفت لاحقا
return Object.assign({}, this.defaults, parsed);
}
} catch (error) {
console.error('خطأ في قراءة التفضيلات:', error);
}
return Object.assign({}, this.defaults);
},
get: function(key) {
const all = this.getAll();
return all.hasOwnProperty(key) ? all[key] : undefined;
},
set: function(key, value) {
const all = this.getAll();
all[key] = value;
localStorage.setItem(this.STORAGE_KEY, JSON.stringify(all));
},
setMultiple: function(updates) {
const all = this.getAll();
Object.keys(updates).forEach(function(key) {
all[key] = updates[key];
});
localStorage.setItem(this.STORAGE_KEY, JSON.stringify(all));
},
reset: function() {
localStorage.setItem(this.STORAGE_KEY, JSON.stringify(this.defaults));
}
};
// الاستخدام
PreferencesManager.set('theme', 'dark');
PreferencesManager.set('fontSize', 20);
console.log(PreferencesManager.get('theme')); // "dark"
console.log(PreferencesManager.get('language')); // "en" (افتراضي)
PreferencesManager.setMultiple({
language: 'ar',
showNotifications: false
});
ملفات تعريف الارتباط مقابل Web Storage: مقارنة
فهم الاختلافات بين ملفات تعريف الارتباط و Web Storage يساعدك على اختيار الأداة المناسبة لكل موقف. إليك مقارنة شاملة لآليات التخزين الثلاث على جانب العميل:
- ملفات تعريف الارتباط (Cookies): محدودة بحوالي 4 كيلوبايت لكل ملف. ترسل مع كل طلب HTTP إلى الخادم، مما يضيف عبئا. يمكن تعيينها لتنتهي في تاريخ محدد. يمكن الوصول إليها من كل من العميل والخادم. تستخدم لرموز المصادقة ومعرفات الجلسة والتتبع من جانب الخادم.
- localStorage: يوفر حوالي 5 ميغابايت لكل أصل. لا يرسل إلى الخادم تلقائيا أبدا. يستمر حتى يتم حذفه صراحة. يمكن الوصول إليه فقط من JavaScript على جانب العميل. الأفضل لتفضيلات المستخدم والبيانات المخزنة مؤقتا وحالة التطبيق.
- sessionStorage: يوفر حوالي 5 ميغابايت لكل أصل. لا يرسل إلى الخادم تلقائيا أبدا. يمسح عند إغلاق علامة التبويب أو النافذة. كل علامة تبويب لها تخزين معزول خاص بها. الأفضل لبيانات النماذج المؤقتة وتقدم خطوات المعالج وحالة الجلسة الواحدة.
مثال: متى تستخدم كل نوع تخزين
// ملفات تعريف الارتباط -- لاحتياجات التواصل مع الخادم
// رموز المصادقة التي يحتاج الخادم للتحقق منها
document.cookie = 'session_id=abc123; Secure; SameSite=Strict; path=/';
// التخزين المحلي -- للبيانات المستمرة على جانب العميل
// تفضيلات المستخدم التي يجب أن تستمر عبر إعادة تشغيل المتصفح
localStorage.setItem('theme', 'dark');
localStorage.setItem('language', 'ar');
// استجابات API المخزنة مؤقتا لتقليل طلبات الشبكة
const cachedData = {
data: apiResponse,
timestamp: Date.now(),
expiresIn: 3600000 // ساعة واحدة
};
localStorage.setItem('cache_products', JSON.stringify(cachedData));
// تخزين الجلسة -- للبيانات المؤقتة لجلسة واحدة
// تقدم معالج النموذج متعدد الخطوات
sessionStorage.setItem('wizardStep', '3');
sessionStorage.setItem('wizardData', JSON.stringify(formData));
// الإشعارات لمرة واحدة التي لا يجب تكرارها في نفس الجلسة
sessionStorage.setItem('welcomeShown', 'true');
الأنماط العملية وأفضل الممارسات
إليك عدة أنماط مهمة يجب اتباعها عند العمل مع Web Storage في التطبيقات الحقيقية:
- حدد نطاق مفاتيحك -- استخدم بادئات مثل
myApp_themeلتجنب التعارضات مع نصوص برمجية أخرى على نفس النطاق. - أضف طوابع زمنية -- خزن التاريخ الذي تم فيه تخزين البيانات مؤقتا حتى تتمكن من انتهاء صلاحية البيانات القديمة.
- تعامل مع الأخطاء بأناقة -- استخدم دائما try-catch حول عمليات التخزين. قد يكون التخزين معطلا لدى المستخدمين أو قد يكونون في وضع التصفح الخاص.
- أبقِ البيانات صغيرة -- لا تعامل localStorage كقاعدة بيانات. خزن فقط ما تحتاجه ونظف البيانات القديمة دوريا.
- استخدم غلافا -- أنشئ وحدة أدوات تتعامل مع التسلسل ومعالجة الأخطاء وتحديد النطاق بشكل متسق عبر تطبيقك.
مثال: غلاف أداة التخزين الكامل
const StorageUtil = {
prefix: 'myApp_',
set: function(key, value, useSession) {
const storage = useSession ? sessionStorage : localStorage;
const fullKey = this.prefix + key;
const wrapper = {
value: value,
timestamp: Date.now()
};
try {
storage.setItem(fullKey, JSON.stringify(wrapper));
return true;
} catch (error) {
console.error('فشلت كتابة التخزين:', error);
return false;
}
},
get: function(key, maxAge, useSession) {
const storage = useSession ? sessionStorage : localStorage;
const fullKey = this.prefix + key;
try {
const raw = storage.getItem(fullKey);
if (raw === null) return null;
const wrapper = JSON.parse(raw);
// التحقق من انتهاء الصلاحية إذا تم تقديم maxAge (بالمللي ثانية)
if (maxAge && (Date.now() - wrapper.timestamp) > maxAge) {
storage.removeItem(fullKey);
return null; // منتهي الصلاحية
}
return wrapper.value;
} catch (error) {
console.error('فشلت قراءة التخزين:', error);
return null;
}
},
remove: function(key, useSession) {
const storage = useSession ? sessionStorage : localStorage;
storage.removeItem(this.prefix + key);
},
clearAll: function(useSession) {
const storage = useSession ? sessionStorage : localStorage;
const keysToRemove = [];
for (let i = 0; i < storage.length; i++) {
const key = storage.key(i);
if (key.startsWith(this.prefix)) {
keysToRemove.push(key);
}
}
keysToRemove.forEach(function(key) {
storage.removeItem(key);
});
}
};
// الاستخدام
StorageUtil.set('userProfile', { name: 'Ahmed', role: 'admin' });
StorageUtil.set('tempToken', 'xyz789', true); // sessionStorage
const profile = StorageUtil.get('userProfile');
console.log(profile.name); // "Ahmed"
// الحصول مع انتهاء صلاحية ساعة واحدة
const cachedResult = StorageUtil.get('apiData', 3600000);
if (cachedResult === null) {
// البيانات منتهية الصلاحية أو غير موجودة، اجلب بيانات جديدة
}
تمرين عملي
ابنِ تطبيق "ملاحظات" كامل باستخدام localStorage. يجب أن يسمح التطبيق للمستخدمين بإنشاء وقراءة وتحديث وحذف ملاحظات نصية. كل ملاحظة يجب أن تحتوي على معرف وعنوان ومحتوى وطابع زمني. خزن الملاحظات كمصفوفة من الكائنات في localStorage. نفذ الميزات التالية: (1) نموذج لإضافة ملاحظات جديدة بحقول العنوان والمحتوى. (2) قائمة تعرض جميع الملاحظات المحفوظة بعناوينها وتواريخ إنشائها. (3) زر حذف على كل ملاحظة يزيلها من التخزين. (4) ميزة تعديل تسمح للمستخدمين بتعديل الملاحظات الموجودة. (5) حقل بحث يصفي الملاحظات حسب العنوان أو المحتوى. (6) زر "مسح الكل" مع نافذة تأكيد. (7) عرض إجمالي استخدام التخزين في أسفل الصفحة. (8) إضافة معالجة أخطاء لحدود حصة التخزين. اختبر تطبيقك بإنشاء عدة ملاحظات وتحديث الصفحة للتحقق من الاستمرارية وجربه في نافذة متخفية للتحقق من أن معالجة الأخطاء تعمل بشكل صحيح.