ساخت برنامه های وب آفلاین اول با وابستگی صفر: آموزش SRVRA-SYNC

در چشم انداز توسعه وب امروز ، قابلیت آفلاین فقط یک ویژگی خوب نیست-ضروری است. کاربران انتظار دارند که برنامه ها بدون در نظر گرفتن شرایط شبکه ، یکپارچه کار کنند.
با این حال ، اجرای عملکردهای آفلاین قوی به طور معمول به کتابخانه ها و چارچوب های پیچیده ای با وابستگی های بی شماری نیاز دارد ، فرآیند ساخت شما را پیچیده می کند و اندازه بسته نرم افزاری شما را افزایش می دهد.
SRVRA-SYNC را وارد کنید: یک کتابخانه مدیریت و هماهنگ سازی حالت JavaScript خالص که به وابستگی صفر نیاز دارد و بدون Node.js. کار می کند. این آموزش شما را از طریق ساختن یک برنامه وب آفلاین اول راهنمایی می کند که هنگام بازگشت اتصال ، داده ها را به صورت هوشمندانه همگام می کند.
چرا به صورت آفلاین اول مسائل
قبل از غواصی ، بیایید روشن کنیم که چرا معماری اول آفلاین بسیار مهم است:
تجربه کاربر بهبود یافته در شرایط غیرقابل اعتماد شبکه
عملکرد سریعتر با دسترسی به داده های محلی فوری
مقاومت در برابر وقفه های شبکه
عمر باتری بهتر با کاهش تلاش های ثابت اتصال
شروع با SRVRA-SYNC
بیایید با اضافه کردن SRVRA-SYNC به پروژه ما شروع کنیم. از آنجا که وابستگی صفر دارد ، می توانید آن را مستقیماً از طریق برچسب اسکریپت درج کنید:
Offline-First Todo App
حال ، بیایید منطق برنامه خود را در App.js ایجاد کنیم:
// اجزای اصلی را آغاز کنید
const eventBus = new SrvraEventBus();
const stateManager = new SrvraStateManager();
const dataSync = new SrvraDataSync({
// Adjust sync interval based on your app's needs
syncInterval: 5000,
// Optimize for offline usage
retryAttempts: 5,
enableDeltaUpdates: true
});
// وضعیت اولیه ما را تنظیم کنید
stateManager.setState('todos', []);
stateManager.setState('connectionStatus', 'online');
// عناصر UI
const todoForm = document.getElementById('todo-form');
const todoInput = document.getElementById('todo-input');
const todoList = document.getElementById('todo-list');
const connectionStatus = document.getElementById('connection-status');
// وضعیت شبکه را پیگیری کنید
window.addEventListener('online', () => {
stateManager.setState('connectionStatus', 'online');
connectionStatus.textContent="Online - Syncing...";
// Trigger sync when connection returns
dataSync.sync();
});
window.addEventListener('offline', () => {
stateManager.setState('connectionStatus', 'offline');
connectionStatus.textContent="Offline - Changes saved locally";
});
// صفحه نمایش اتصال را آغاز کنید
connectionStatus.textContent = navigator.onLine ? 'Online' : 'Offline - Changes saved locally';
// اضافه کردن اضافه کردن تودوهای جدید
todoForm.addEventListener('submit', (e) => {
e.preventDefault();
const todoText = todoInput.value.trim();
if (!todoText) return;
const todos = stateManager.getState('todos') || [];
const newTodo = {
id: Date.now().toString(),
text: todoText,
completed: false,
createdAt: Date.now(),
synced: false
};
// Update local state immediately
stateManager.setState('todos', [...todos, newTodo]);
// Publish event for sync handling
eventBus.publish('data-change', {
key: 'todos',
value: [...todos, newTodo],
timestamp: Date.now()
});
todoInput.value="";
});
// مشترک در تغییرات حالت برای به روزرسانی UI
stateManager.subscribe('todos', renderTodos);
// لیست TODO را ارائه دهید
function renderTodos(todos) {
todoList.innerHTML = '';
if (!todos || !todos.length) {
todoList.innerHTML = 'No todos yet. Add one above! ';
return;
}
todos.forEach(todo => {
const li = document.createElement('li');
li.dataset.id = todo.id;
li.className = todo.completed ? 'completed' : '';
// Add sync status indicator
const syncStatus = todo.synced ? '?' : '?';
li.innerHTML = `
${todo.text}
${syncStatus}
`;
// Add event listeners for toggle and delete
li.querySelector('.toggle-btn').addEventListener('click', () => toggleTodo(todo.id));
li.querySelector('.delete-btn').addEventListener('click', () => deleteTodo(todo.id));
todoList.appendChild(li);
});
}
// وضعیت تکمیل TODO را تغییر دهید
function toggleTodo(id) {
const todos = stateManager.getState('todos');
const updatedTodos = todos.map(todo => {
if (todo.id === id) {
return { ...todo, completed: !todo.completed, synced: false };
}
return todo;
});
stateManager.setState('todos', updatedTodos);
// Publish event for sync
eventBus.publish('data-change', {
key: 'todos',
value: updatedTodos,
timestamp: Date.now()
});
}
// حذف همه
function deleteTodo(id) {
const todos = stateManager.getState('todos');
const updatedTodos = todos.filter(todo => todo.id !== id);
stateManager.setState('todos', updatedTodos);
// Publish event for sync
eventBus.publish('data-change', {
key: 'todos',
value: updatedTodos,
timestamp: Date.now()
});
}
// کنترل هماهنگ سازی
eventBus.subscribe('sync-complete', (data) => {
const todos = stateManager.getState('todos');
// Mark successfully synced todos
const updatedTodos = todos.map(todo => {
if (!todo.synced) {
return { ...todo, synced: true };
}
return todo;
});
stateManager.setState('todos', updatedTodos);
connectionStatus.textContent="Online - Synced";
// After a moment, simplify the display
setTimeout(() => {
if (stateManager.getState('connectionStatus') === 'online') {
connectionStatus.textContent="Online";
}
}, 2000);
});
// برای خطاهای همگام سازی گوش دهید
eventBus.subscribe('sync-error', (data) => {
console.error('Sync error:', data.error);
connectionStatus.textContent="Sync failed - Will retry";
});
// با هر داده ذخیره شده اولیه را آغاز کنید
function initializeFromStorage() {
const storedTodos = localStorage.getItem('todos');
if (storedTodos) {
try {
const todos = JSON.parse(storedTodos);
stateManager.setState('todos', todos);
} catch (e) {
console.error('Error loading stored todos:', e);
}
}
}
// تغییر دولت در محلی در محلی
stateManager.subscribe('todos', (todos) => {
localStorage.setItem('todos', JSON.stringify(todos));
});
// اولیه سازی
initializeFromStorage();
تنظیم همگام سازی سرور
برای یک برنامه کامل آفلاین ، باید هماهنگ سازی سرور را تنظیم کنیم. در اینجا نحوه رسیدگی به ادغام سمت سرور آورده شده است:
// این به App.js شما اضافه می شود
function setupServerSync() {
// Configure sync endpoint
const syncEndpoint="https://api.example.com/sync";
// Override the default sendToServer method in SrvraDataSync
dataSync.sendToServer = async function(batch) {
try {
// Only attempt to send if online
if (!navigator.onLine) {
return { success: [], conflicts: [], errors: [] };
}
const response = await fetch(syncEndpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(batch)
});
if (!response.ok) {
throw new Error(`Server responded with ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Sync error:', error);
// Return empty results but don't fail - we'll retry later
return { success: [], conflicts: [], errors: [error] };
}
};
// Handle conflict resolution
eventBus.subscribe('conflict', (conflict) => {
// For this example, we'll use a "client wins" strategy for conflicts
return dataSync.handleConflict({
...conflict,
forcedStrategy: 'client-wins'
});
});
}
setupServerSync();
اول الگوهای آفلاین-اول با SRVRA-SYNC
حال بیایید برخی از الگوهای پیشرفته آفلاین را اجرا کنیم:
1. به روزرسانی های خوش بینانه UI
// این را به App.js اضافه کنید تا به روزرسانی های خوش بین UI را فعال کنید
function optimisticallyUpdateTodo(todoId, changes) {
const todos = stateManager.getState('todos');
// Immediately update the UI
const optimisticTodos = todos.map(todo => {
if (todo.id === todoId) {
return { ...todo, ...changes, synced: false };
}
return todo;
});
// Update state immediately
stateManager.setState('todos', optimisticTodos);
// Then attempt to sync
eventBus.publish('data-change', {
key: 'todos',
value: optimisticTodos,
timestamp: Date.now()
});
}
// استفاده مثال برای تغییر اولویت
function updateTodoPriority(todoId, priority) {
optimisticallyUpdateTodo(todoId, { priority });
}
5. استراتژی های حل و فصل مناقشه
// وضوح درگیری پیشرفته بر اساس انواع داده ها
dataSync.conflictResolver.registerCustomStrategy('smart-todo-merge', (conflict) => {
const serverTodo = conflict.serverValue;
const clientTodo = conflict.clientValue;
// If completed status differs, prefer the completed version
const completed = serverTodo.completed || clientTodo.completed;
// For text content, use the most recently edited version
const text = serverTodo.updatedAt > clientTodo.updatedAt
? serverTodo.text
: clientTodo.text;
return {
value: {
...clientTodo,
text,
completed,
synced: true
},
source: 'smart-merge',
metadata: {
mergedAt: Date.now(),
conflictResolution: 'smart-todo-merge'
}
};
});
// تنظیمات TODOS را برای استفاده از استراتژی سفارشی ما پیکربندی کنید
dataSync.conflictResolver.registerMergeRule('todos',
dataSync.conflictResolver.customStrategies.get('smart-todo-merge'));
3. مدیریت سهمیه ذخیره سازی
// این را برای مدیریت سهمیه های محلی اضافه کنید
async function checkStorageQuota() {
if ('storage' in navigator && 'estimate' in navigator.storage) {
const estimate = await navigator.storage.estimate();
const percentUsed = (estimate.usage / estimate.quota) * 100;
if (percentUsed > 80) {
// Alert user or clean up old data
const todos = stateManager.getState('todos');
// Clean up completed and synced todos older than 30 days
const thirtyDaysAgo = Date.now() - (30 * 24 * 60 * 60 * 1000);
const cleanedTodos = todos.filter(todo => {
return !(todo.completed && todo.synced && todo.createdAt < thirtyDaysAgo);
});
if (cleanedTodos.length < todos.length) {
stateManager.setState('todos', cleanedTodos);
console.log(`Cleaned up ${todos.length - cleanedTodos.length} old todos to save space.`);
}
}
}
}
// سهمیه هفتگی را بررسی کنید
setInterval(checkStorageQuota, 7 * 24 * 60 * 60 * 1000);
// همچنین در راه اندازی بررسی کنید
checkStorageQuota();
رسیدگی به اتصال مجدد شبکه هوشمندانه
جنبه مهم برنامه های آفلاین اول مدیریت اتصال مجدد به آرامی است:
// اضافه کردن مجدد اتصال مجدد پیچیده
let reconnectionAttempts = 0;
const MAX_BACKOFF = 60000; // Maximum 1 minute between retries
function handleReconnection() {
// Reset if we're online
if (navigator.onLine) {
reconnectionAttempts = 0;
dataSync.sync(); // Sync immediately when we reconnect
return;
}
// Exponential backoff for reconnection attempts
reconnectionAttempts++;
const backoff = Math.min(
1000 * Math.pow(2, reconnectionAttempts),
MAX_BACKOFF
);
connectionStatus.textContent = `Offline - Retrying in ${backoff/1000}s`;
setTimeout(() => {
// Check if we're back online
if (navigator.onLine) {
stateManager.setState('connectionStatus', 'online');
connectionStatus.textContent="Online - Syncing...";
dataSync.sync();
} else {
handleReconnection(); // Try again with increased backoff
}
}, backoff);
}
// شروع به کار در شبکه
if (!navigator.onLine) {
handleReconnection();
}
// Listen for network changes
window.addEventListener('online', () => {
stateManager.setState('connectionStatus', 'online');
connectionStatus.textContent="Online - Syncing...";
dataSync.sync();
});
مخزن GitHub:
srvra-sync https://github.com/sinhasantos/srvra-sync