LocalStorage storage: remember the user's last "taste preference"
Basic Concepts of LocalStorage
LocalStorage is part of the Web Storage API, which allows storing key-value pair data in the browser. Compared to cookies, LocalStorage offers a larger storage capacity (typically 5MB), and the data is not sent to the server with HTTP requests. Data in LocalStorage remains in the browser until explicitly deleted or cleared by the user.
Key features of LocalStorage include:
- Storage capacity: Approximately 5MB
- Scope: Restricted by the same-origin policy
- Lifetime: Persistent storage unless manually cleared
- Synchronous operations: All operations are synchronous
Business Scenarios for Taste Preferences
In e-commerce websites or content platforms, user taste preferences (such as product category preferences or content type preferences) are important personalized data. Remembering these preferences can significantly enhance the user experience, eliminating the need for users to reset them each time they visit.
Typical taste preference scenarios include:
- Food delivery websites remembering users' preferred cuisines (e.g., Sichuan, Cantonese)
- News websites remembering users' preferred news categories (e.g., sports, technology)
- Video platforms remembering users' viewing preferences (e.g., movies, documentaries)
Core Code for Implementing Taste Preference Storage
Storing Preference Data
When users select or change their preferences, we can store the preference data in LocalStorage:
// User selects "Sichuan cuisine" as their taste preference
function savePreference(preference) {
try {
localStorage.setItem('foodPreference', JSON.stringify(preference));
console.log('Preference saved successfully');
} catch (e) {
console.error('Storage failed:', e);
// Handle cases like insufficient storage space
if (e.name === 'QuotaExceededError') {
alert('Insufficient storage space. Please clear some data.');
}
}
}
// Example usage
savePreference({ type: 'Sichuan', spiceLevel: 'High' });
Reading Preference Data
When the page loads, we can check if there are stored preferences in LocalStorage:
function loadPreference() {
try {
const preference = localStorage.getItem('foodPreference');
if (preference) {
return JSON.parse(preference);
}
return null;
} catch (e) {
console.error('Failed to read preference:', e);
return null;
}
}
// Example usage
const userPreference = loadPreference();
if (userPreference) {
console.log('Loaded user preference:', userPreference);
// Initialize UI based on preference
initializeUI(userPreference);
} else {
console.log('No stored preferences found');
// Show default settings or preference selection interface
showPreferenceSelector();
}
Handling Preference Changes
Users may change their preferences at any time, and we need to handle these changes and update storage:
// Listen for preference change events
preferenceForm.addEventListener('submit', (event) => {
event.preventDefault();
const newPreference = {
type: event.target.cuisine.value,
spiceLevel: event.target.spice.value
};
savePreference(newPreference);
applyPreference(newPreference);
});
// Apply preferences to the interface
function applyPreference(preference) {
// Filter items or content based on preference
filterItems(preference.type);
// Update UI to reflect current preferences
updatePreferenceUI(preference);
// Additional business logic
if (preference.spiceLevel === 'High') {
recommendSpicyItems();
}
}
Data Structure Design
For complex preferences, good data structure design is essential:
// Example of a more complex preference data structure
const advancedPreference = {
// Basic preferences
basic: {
cuisine: 'Sichuan',
spiceLevel: 5, // Scale of 1-5
avoidIngredients: ['coriander', 'scallions']
},
// Advanced preferences
advanced: {
cookingStyle: ['dry pot', 'braised'],
priceRange: {
min: 30,
max: 100
}
},
// Metadata
meta: {
lastUpdated: new Date().toISOString(),
version: '1.1'
}
};
// Store complex preferences
localStorage.setItem('advancedFoodPreference', JSON.stringify(advancedPreference));
Managing Multiple Preferences
When managing multiple independent preferences:
// Define preference key constants
const PREFERENCE_KEYS = {
FOOD: 'foodPreference',
NEWS: 'newsCategory',
THEME: 'uiTheme'
};
// Unified management of multiple preferences
class PreferenceManager {
static get(key) {
const data = localStorage.getItem(key);
return data ? JSON.parse(data) : null;
}
static set(key, value) {
localStorage.setItem(key, JSON.stringify(value));
}
static remove(key) {
localStorage.removeItem(key);
}
static clearAll() {
localStorage.clear();
}
}
// Example usage
PreferenceManager.set(PREFERENCE_KEYS.FOOD, { type: 'Cantonese' });
const theme = PreferenceManager.get(PREFERENCE_KEYS.THEME);
Error Handling and Compatibility
Robust error handling is important:
function safeGetPreference(key) {
try {
// Check if LocalStorage is available
if (!window.localStorage) {
console.warn('Browser does not support LocalStorage');
return null;
}
const item = localStorage.getItem(key);
return item ? JSON.parse(item) : null;
} catch (error) {
// Handle various possible errors
switch (error.name) {
case 'SecurityError':
console.error('Cannot access LocalStorage due to browser security settings');
break;
case 'SyntaxError':
console.error('Stored data is not valid JSON');
break;
default:
console.error('Unknown error while reading preference:', error);
}
return null;
}
}
Performance Optimization Considerations
For frequently accessed preference data:
// Use in-memory cache to reduce LocalStorage access
let preferenceCache = null;
function getCachedPreference(key) {
if (!preferenceCache) {
preferenceCache = safeGetPreference(key);
}
return preferenceCache;
}
function updateCachedPreference(key, value) {
preferenceCache = value;
PreferenceManager.set(key, value);
}
// Listen for storage events to sync across tabs
window.addEventListener('storage', (event) => {
if (event.key === PREFERENCE_KEYS.FOOD) {
preferenceCache = event.newValue ? JSON.parse(event.newValue) : null;
applyPreference(preferenceCache);
}
});
Practical Application Example
Complete React component example:
import React, { useState, useEffect } from 'react';
function PreferenceSelector() {
const [preference, setPreference] = useState(null);
const [loading, setLoading] = useState(true);
// Load saved preferences
useEffect(() => {
const savedPref = loadPreference();
if (savedPref) {
setPreference(savedPref);
}
setLoading(false);
}, []);
// Save preference changes
const handlePreferenceChange = (newPref) => {
setPreference(newPref);
savePreference(newPref);
};
if (loading) return <div>Loading...</div>;
return (
<div className="preference-selector">
<h3>Your Taste Preferences</h3>
{!preference ? (
<InitialPreferenceForm onSubmit={handlePreferenceChange} />
) : (
<PreferenceEditor
currentPreference={preference}
onSave={handlePreferenceChange}
/>
)}
</div>
);
}
// Subcomponent example
function PreferenceEditor({ currentPreference, onSave }) {
const [formData, setFormData] = useState(currentPreference);
const handleSubmit = (e) => {
e.preventDefault();
onSave(formData);
};
return (
<form onSubmit={handleSubmit}>
<label>
Cuisine Preference:
<select
value={formData.type}
onChange={(e) => setFormData({...formData, type: e.target.value})}
>
<option value="Sichuan">Sichuan</option>
<option value="Cantonese">Cantonese</option>
<option value="Hunan">Hunan</option>
</select>
</label>
<label>
Spice Level:
<input
type="range"
min="1"
max="5"
value={formData.spiceLevel}
onChange={(e) => setFormData({...formData, spiceLevel: e.target.value})}
/>
</label>
<button type="submit">Save Preferences</button>
</form>
);
}
Security and Privacy Considerations
Important considerations when handling user preferences:
- Sensitive Information: Do not store passwords, personally identifiable information, or other sensitive data.
- Data Encryption: Consider simple encryption for important preferences.
- User Control: Provide options for users to clear their preferences.
// Simple encryption example (use more secure methods in production)
const simpleCrypto = {
encrypt: (text) => btoa(unescape(encodeURIComponent(text))),
decrypt: (text) => decodeURIComponent(escape(atob(text)))
};
// Encrypted storage
function saveSecurePreference(key, value) {
const encrypted = simpleCrypto.encrypt(JSON.stringify(value));
localStorage.setItem(key, encrypted);
}
// Decrypted retrieval
function loadSecurePreference(key) {
const encrypted = localStorage.getItem(key);
if (!encrypted) return null;
try {
return JSON.parse(simpleCrypto.decrypt(encrypted));
} catch (e) {
console.error('Decryption failed', e);
return null;
}
}
Testing and Debugging
Verify that preference storage works correctly:
// Example test cases
function testPreferenceStorage() {
// Test basic storage
const testPref = { type: 'Test', value: 123 };
savePreference('testKey', testPref);
const retrieved = loadPreference('testKey');
console.assert(
JSON.stringify(retrieved) === JSON.stringify(testPref),
'Basic storage test failed'
);
// Test storage limits
try {
const hugeData = new Array(5 * 1024 * 1024).join('a');
localStorage.setItem('hugeTest', hugeData);
console.error('Storage limit test failed - should throw QuotaExceededError');
} catch (e) {
console.assert(
e.name === 'QuotaExceededError',
'Storage limit test failed - wrong error type'
);
}
// Clean up test data
localStorage.removeItem('testKey');
console.log('Testing complete');
}
// Run tests
testPreferenceStorage();
Comparison with Other Storage Solutions
Comparison of LocalStorage with other client-side storage options:
Feature | LocalStorage | SessionStorage | Cookies | IndexedDB |
---|---|---|---|---|
Capacity | ~5MB | ~5MB | ~4KB | Large (50MB+) |
Lifetime | Persistent | Session-only | Configurable | Persistent |
Server Access | No | No | With HTTP requests | No |
Data Structure | Key-value | Key-value | String | Object store |
Sync/Async | Synchronous | Synchronous | Synchronous | Asynchronous |
Use Cases | Simple prefs | Temporary data | Small IDs/tokens | Complex app data |
Advanced Patterns and Best Practices
- Version Control: Preference data structures may change; include version numbers.
const PREFERENCE_SCHEMA = {
version: '1.0',
data: {
// Actual preference data
}
};
- Data Migration: Handle old data when data structures change.
function migratePreference(oldData) {
if (!oldData.version) {
// Migrate from v0 to v1
return {
version: '1.0',
data: {
type: oldData.cuisineType || 'Unknown',
level: oldData.spice || 3
}
};
}
return oldData;
}
- Expiration Policy: Although LocalStorage is persistent, you can implement logical expiration.
function isPreferenceExpired(pref, days = 365) {
if (!pref?.meta?.lastUpdated) return true;
const lastUpdated = new Date(pref.meta.lastUpdated);
const expirationDate = new Date();
expirationDate.setDate(expirationDate.getDate() - days);
return lastUpdated < expirationDate;
}
Browser Compatibility Handling
Handle special cases for different browsers:
// Check if LocalStorage is available
function isLocalStorageAvailable() {
const testKey = 'test';
try {
localStorage.setItem(testKey, testKey);
localStorage.removeItem(testKey);
return true;
} catch (e) {
return false;
}
}
// Fallback solution
function getFallbackStorage() {
return {
setItem: (key, value) => {
document.cookie = `${key}=${value}; path=/; max-age=31536000`; // 1 year
},
getItem: (key) => {
const match = document.cookie.match(new RegExp(`(^| )${key}=([^;]+)`));
return match ? match[2] : null;
}
};
}
const storage = isLocalStorageAvailable() ? localStorage : getFallbackStorage();
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn