کشف کنید که چگونه API واکشی پسزمینه فرانتاند، مدیریت دانلودهای حجیم را در برنامههای وب متحول میکند و انتقالهای قابل اعتماد و آفلاین را برای کاربران جهانی تضمین مینماید.
تسلط بر دانلودهای حجیم: راهنمای جهانی برای API واکشی پسزمینه فرانتاند
در دنیای متصل امروز، از برنامههای وب به طور فزایندهای انتظار میرود که وظایف پیچیدهای را مدیریت کنند، از جمله انتقال کارآمد و قابل اعتماد فایلهای حجیم. چه یک فیلم با کیفیت بالا باشد، چه یک بهروزرسانی نرمافزاری بزرگ، یک کتابخانه کامل از کتابهای الکترونیکی، یا یک مجموعه داده حیاتی برای یک برنامه سازمانی، کاربران در سراسر جهان خواهان تجربیات یکپارچه هستند، صرف نظر از شرایط شبکه یا الگوهای استفاده از دستگاهشان. به طور سنتی، مدیریت دانلودهای حجیم در وب با چالشهایی همراه بوده است. خروج کاربر از یک تب یا تجربه یک قطعی لحظهای شبکه میتوانست فوراً یک دانلود طولانی را به خطر اندازد و منجر به ناامیدی و هدر رفتن پهنای باند شود. اینجاست که API قدرتمند واکشی پسزمینه فرانتاند وارد عمل میشود و راهحلی قوی ارائه میدهد که نحوه مدیریت انتقال فایلهای پایدار و در مقیاس بزرگ توسط برنامههای وب را متحول میکند.
این راهنمای جامع به عمق API واکشی پسزمینه میپردازد و عملکردهای اصلی، پیادهسازیهای عملی و بهترین شیوههای آن را بررسی میکند. ما بررسی خواهیم کرد که چگونه این API، با بهرهگیری از قدرت سرویس ورکرها، به توسعهدهندگان این امکان را میدهد تا برنامههای وب واقعاً انعطافپذیر و کاربرپسندی بسازند که قادر به مدیریت عملیات دادهای قابل توجه در پسزمینه هستند و تجربه کاربران را در محیطهای مختلف جهانی بهبود میبخشند.
چالش همیشگی دانلودهای حجیم در وب
پیش از ظهور قابلیتهای پیشرفته وب، توسعهدهندگان فرانتاند با موانع قابل توجهی در پیادهسازی دانلود فایلهای حجیم مواجه بودند. ماهیت بیحالت وب و محیط سندباکس مرورگر، با وجود ارائه امنیت، اغلب محدودیتهایی را ایجاد میکرد که عملیات قابل اعتماد و طولانیمدت را دشوار میساخت. بیایید چالشهای سنتی را با جزئیات بیشتری بررسی کنیم:
وابستگی به تب مرورگر: یک اتصال شکننده
یکی از مهمترین محدودیتهای دانلودهای سنتی وب، وابستگی ذاتی آنها به یک تب فعال مرورگر است. هنگامی که کاربر دانلودی را آغاز میکرد، فرآیند به طور جداییناپذیری به تب خاصی که از آنجا شروع شده بود، مرتبط بود. اگر کاربر به طور تصادفی تب را میبست، به صفحه دیگری میرفت یا حتی به برنامه دیگری سوئیچ میکرد، دانلود معمولاً به طور ناگهانی متوقف میشد. این امر تجربهای بسیار شکننده ایجاد میکرد، به ویژه برای فایلهای بزرگی که ممکن بود دقایق یا حتی ساعتها طول بکشد تا کامل شوند. تصور کنید کاربری در یک فرودگاه بینالمللی شلوغ، متصل به وایفای متناوب، در تلاش برای دانلود یک فیلم برای پرواز طولانی خود است. یک قطعی کوتاه سیگنال یا بسته شدن ناخواسته تب به معنای شروع مجدد دانلود از ابتدا بود که باعث اتلاف وقت و داده میشد. این وابستگی فقط یک ناراحتی نبود؛ بلکه یک مانع اساسی برای ساخت برنامههای وب واقعاً قوی بود که میتوانستند با تجربیات اپلیکیشنهای بومی رقابت کنند.
ناپایداری شبکه: واقعیت جهانی
شرایط شبکه در سراسر جهان بسیار متفاوت است. در حالی که برخی مناطق از اینترنت سریع و پایدار برخوردارند، بسیاری از کاربران، به ویژه در اقتصادهای در حال توسعه یا مناطق روستایی، با اتصالات کند، غیرقابل اعتماد یا غالباً قطع شده دست و پنجه نرم میکنند. دانلودهای HTTP سنتی از دیدگاه مرورگر فاقد مکانیزمهای تلاش مجدد ذاتی یا قابلیتهای هوشمندانه برای ادامه دانلودهای ناقص هستند (اگرچه سرورها میتوانند از آن پشتیبانی کنند، کلاینت اغلب وضعیت خود را از دست میدهد). یک اختلال کوتاه شبکه، که در بسیاری از نقاط جهان رایج است، میتوانست یک دانلود را برای همیشه متوقف کند و کاربر را ملزم به راهاندازی مجدد دستی آن کند. این نه تنها کاربران را ناامید میکند، بلکه هزینههای دادهای غیرضروری را نیز تحمیل میکند، اگر از اتصالات تعرفهای استفاده کنند، که یک سناریوی رایج برای کاربران تلفن همراه در سراسر جهان است. عدم انعطافپذیری در برابر نوسانات شبکه مدتهاست که یک نقطه ضعف برای توسعهدهندگان وب با هدف دسترسی و پوشش جهانی بوده است.
مشکلات تجربه کاربری: انتظار و عدم قطعیت
برای دانلودهای حجیم، یک جنبه حیاتی از تجربه کاربری، گزارش شفاف پیشرفت است. کاربران میخواهند بدانند چقدر دانلود شده، چقدر باقی مانده و زمان تخمینی برای تکمیل چقدر است. مدیران دانلود سنتی مرورگر بازخورد اولیه را ارائه میدهند، اما ادغام یکپارچه آن در رابط کاربری یک برنامه وب اغلب پیچیده یا محدود بود. علاوه بر این، مجبور کردن کاربران به باز نگه داشتن یک تب فقط برای نظارت بر دانلود، تجربه کاربری ضعیفی ایجاد میکند. این کار منابع سیستم را اشغال میکند، مانع از تعامل آنها با محتوای دیگر میشود و باعث میشود برنامه کمتر حرفهای به نظر برسد. کاربران انتظار دارند یک وظیفه را آغاز کنند و اعتماد داشته باشند که در پسزمینه تکمیل خواهد شد، و به آنها اجازه میدهد به کار خود ادامه دهند یا حتی مرورگر خود را ببندند.
گزارشدهی و کنترل محدود پیشرفت
در حالی که مرورگرها پیشرفت دانلود اولیه را ارائه میدهند، دریافت بهروزرسانیهای دقیق و آنی در خود برنامه وب برای دانلودهای سنتی دشوار بود. توسعهدهندگان اغلب به نظرسنجی (polling) یا ترفندهای پیچیده سمت سرور متوسل میشدند که پیچیدگی و سربار را افزایش میداد. علاوه بر این، کاربران پس از شروع دانلود کنترل کمی داشتند. توقف، ادامه یا لغو دانلود در اواسط راه معمولاً یک عملیات همه یا هیچ بود که توسط مدیر دانلود پیشفرض مرورگر مدیریت میشد، نه از طریق رابط کاربری سفارشی برنامه وب. این عدم کنترل برنامهنویسی، پیچیدگی ویژگیهای مدیریت دانلودی را که توسعهدهندگان میتوانستند ارائه دهند، محدود میکرد.
سربار مدیریت منابع برای توسعهدهندگان
برای توسعهدهندگان، مدیریت دانلودهای حجیم به طور سنتی به معنای سروکار داشتن با تعداد زیادی از موارد خاص بود: مدیریت خطاهای شبکه، پیادهسازی منطق تلاش مجدد، مدیریت وضعیت فایلهای ناقص و تضمین یکپارچگی دادهها. این اغلب منجر به کدهای پیچیده و مستعد خطا میشد که نگهداری و مقیاسپذیری آن دشوار بود. ساخت ویژگیهای دانلود قوی از ابتدا، به ویژه آنهایی که نیاز به پایداری در پسزمینه دارند، یک چالش مهندسی قابل توجه بود و منابع را از توسعه هسته برنامه منحرف میکرد. نیاز به یک راهحل استاندارد و در سطح مرورگر واضح بود.
معرفی API واکشی پسزمینه فرانتاند
API واکشی پسزمینه (Background Fetch API) یک ویژگی مدرن پلتفرم وب است که برای مقابله مستقیم با این چالشهای دیرینه طراحی شده است. این API یک روش قوی و استاندارد برای برنامههای وب فراهم میکند تا دانلود (و آپلود) فایلهای حجیم را در پسزمینه آغاز و مدیریت کنند، حتی زمانی که کاربر از صفحه خارج میشود یا مرورگر را میبندد. این API بر روی سرویس ورکرها ساخته شده و از توانایی آنها برای کار مستقل از رشته اصلی مرورگر و حفظ وضعیت در طول جلسات بهره میبرد.
این API چیست؟ (ارتباط با سرویس ورکر)
در هسته خود، API واکشی پسزمینه با واگذاری مسئولیت یک عملیات واکشی به یک سرویس ورکر کار میکند. یک سرویس ورکر یک فایل جاوا اسکریپت است که مرورگر آن را در پسزمینه، جدا از صفحه وب اصلی، اجرا میکند. این به عنوان یک پروکسی قابل برنامهریزی عمل میکند، درخواستهای شبکه را رهگیری میکند، منابع را کش میکند و در این زمینه، وظایف پسزمینه را مدیریت میکند. هنگامی که شما یک واکشی پسزمینه را آغاز میکنید، در واقع به مرورگر، از طریق سرویس ورکر خود، میگویید: «لطفاً این فایلها را به طور قابل اعتماد دانلود کن و وقتی تمام شد یا اگر مشکلی پیش آمد به من اطلاع بده.» سپس سرویس ورکر کنترل را به دست میگیرد و درخواستهای شبکه، تلاشهای مجدد و پایداری را مدیریت میکند و رشته اصلی و جلسه فعال کاربر را از این نگرانیها آزاد میکند.
مزایای کلیدی واکشی پسزمینه
API واکشی پسزمینه چندین مزیت تحولآفرین برای برنامههای وب با هدف ارائه تجربهای جهانی و با کارایی بالا ارائه میدهد:
- قابلیت اطمینان: دانلودها حتی اگر کاربر تب را ببندد، به صفحه دیگری برود یا اتصال شبکه را از دست بدهد، ادامه مییابند. سیستم عامل مرورگر واکشی را مدیریت میکند و مکانیزمهای تلاش مجدد قوی را فراهم میکند.
- تجربه کاربری بهبود یافته: کاربران میتوانند دانلودهای حجیم را آغاز کرده و با اطمینان به مرور وب ادامه دهند یا مرورگر خود را ببندند، با علم به اینکه دانلود در پسزمینه تکمیل خواهد شد. اعلانهای پیشرفت را میتوان از طریق اعلانهای بومی سیستم ارائه داد.
- قابلیتهای آفلاین: پس از دانلود، محتوا میتواند به صورت آفلاین در دسترس قرار گیرد، که برای برنامههایی مانند پخشکنندههای رسانه، پلتفرمهای آموزشی و نمایشگرهای اسناد، به ویژه در مناطقی با دسترسی محدود یا بدون اینترنت، حیاتی است.
- کنترل دقیق: توسعهدهندگان دسترسی برنامهنویسی برای نظارت بر پیشرفت دانلود، مدیریت وضعیتهای موفقیت/شکست و حتی لغو واکشیهای در حال انجام را مستقیماً از برنامه وب خود به دست میآورند.
- کاهش مصرف منابع: با واگذاری وظایف سنگین دانلود به سرویس ورکر و پشته شبکه زیربنایی مرورگر، رشته اصلی پاسخگو باقی میماند و عملکرد کلی برنامه بهبود مییابد.
- بهبود تدریجی (Progressive Enhancement): این امکان را به توسعهدهندگان میدهد تا در جایی که پشتیبانی میشود، تجربهای برتر ارائه دهند، در حالی که برای مرورگرهایی که هنوز این API را پیادهسازی نکردهاند، یک جایگزین مناسب فراهم میکند.
مفاهیم اصلی: BackgroundFetchManager، BackgroundFetchRegistration، BackgroundFetchEvent
برای استفاده مؤثر از API واکشی پسزمینه، درک اجزای اصلی آن ضروری است:
-
BackgroundFetchManager: این نقطه ورود به API است که از طریقnavigator.serviceWorker.ready.then(registration => registration.backgroundFetch)در دسترس است. این به شما امکان میدهد واکشیهای پسزمینه جدید را آغاز کرده و اطلاعات مربوط به موارد موجود را بازیابی کنید. -
BackgroundFetchRegistration: نشاندهنده یک عملیات واکشی پسزمینه واحد است. هنگامی که یک واکشی را آغاز میکنید، یک شیءBackgroundFetchRegistrationدریافت میکنید. این شیء جزئیاتی در مورد واکشی ارائه میدهد، مانند شناسه، حجم کل، بایتهای دانلود شده، وضعیت، و به شما امکان میدهد با آن تعامل داشته باشید (به عنوان مثال، لغو). همچنین رویدادهایی را به سرویس ورکر ارسال میکند. -
BackgroundFetchEvent: اینها رویدادهایی هستند که در سرویس ورکر هنگام تغییر وضعیت یک واکشی پسزمینه فعال میشوند. رویدادهای کلیدی شاملbackgroundfetchsuccess(زمانی که همه منابع دانلود میشوند)،backgroundfetchfail(زمانی که واکشی پس از اتمام تلاشهای مجدد با شکست مواجه میشود)،backgroundfetchabort(زمانی که واکشی به صورت دستی لغو میشود) وbackgroundfetchprogress(برای بهروزرسانیهای دورهای در مورد پیشرفت دانلود) هستند.
نحوه عملکرد واکشی پسزمینه: نگاهی عمیق به مکانیزم
درک گردش کار API واکشی پسزمینه برای پیادهسازی مؤثر آن بسیار مهم است. این شامل یک تلاش هماهنگ بین رشته اصلی (جاوا اسکریپت صفحه وب شما) و سرویس ورکر است.
آغاز یک واکشی پسزمینه از رشته اصلی
فرآیند از رشته اصلی شروع میشود، معمولاً در پاسخ به یک اقدام کاربر، مانند کلیک بر روی دکمه «دانلود فیلم» یا «همگامسازی دادههای آفلاین». ابتدا، باید اطمینان حاصل کنید که سرویس ورکر شما فعال و آماده است. این کار معمولاً با انتظار برای navigator.serviceWorker.ready انجام میشود.
هنگامی که ثبتنام سرویس ورکر در دسترس قرار گرفت، به مدیر backgroundFetch دسترسی پیدا کرده و متد fetch() آن را فراخوانی میکنید:
async function startLargeDownload(fileUrl, downloadId, title) {
if ('serviceWorker' in navigator && 'BackgroundFetchManager' in window) {
try {
const registration = await navigator.serviceWorker.ready;
const bgFetch = await registration.backgroundFetch.fetch(
downloadId, // A unique ID for this fetch
[fileUrl], // An array of Request objects or URLs to fetch
{
title: title, // Title to display in system UI/notifications
icons: [{ // Optional: Icons for system UI
src: '/images/download-icon-128.png',
sizes: '128x128',
type: 'image/png'
}],
downloadTotal: 1024 * 1024 * 500 // Optional: Total expected bytes for progress calculation (e.g., 500 MB)
}
);
console.log('Background fetch started:', bgFetch.id);
// Add event listeners to the registration object for main thread updates
bgFetch.addEventListener('progress', () => {
console.log(`Progress for ${bgFetch.id}: ${bgFetch.downloaded} of ${bgFetch.downloadTotal}`);
// Update UI here if the tab is open
});
bgFetch.addEventListener('success', () => {
console.log(`Download ${bgFetch.id} completed successfully!`);
// Notify user, update UI
});
bgFetch.addEventListener('fail', () => {
console.error(`Download ${bgFetch.id} failed.`);
// Notify user about failure
});
bgFetch.addEventListener('abort', () => {
console.warn(`Download ${bgFetch.id} was aborted.`);
});
return bgFetch;
} catch (error) {
console.error('Error starting background fetch:', error);
}
} else {
console.warn('Background Fetch API not supported.');
// Fallback to traditional download methods
window.open(fileUrl, '_blank');
}
}
// Example Usage:
// startLargeDownload('/path/to/my/large-movie.mp4', 'movie-hd-001', 'My Awesome Movie HD');
بیایید پارامترهای متد `fetch()` را بررسی کنیم:
- `id` (رشته، الزامی): یک شناسه منحصر به فرد برای این عملیات واکشی پسزمینه. این شناسه برای بازیابی واکشی در آینده و جلوگیری از واکشیهای تکراری حیاتی است. این باید در بین تمام واکشیهای پسزمینه فعال برای مبدأ شما منحصر به فرد باشد.
-
`requests` (آرایهای از اشیاء `Request` یا URL ها، الزامی): آرایهای که منابع مورد نیاز برای دانلود را مشخص میکند. شما میتوانید URL های ساده را به صورت رشته یا اشیاء پیچیدهتر
Requestبرای سفارشیسازی هدرهای HTTP، متدها و غیره ارسال کنید. برای دانلودهای چند قسمتی یا واکشی داراییهای مرتبط، این آرایه میتواند شامل چندین ورودی باشد. -
`options` (شیء، اختیاری): یک شیء برای پیکربندی واکشی پسزمینه. ویژگیهای کلیدی عبارتند از:
- `title` (رشته): یک عنوان قابل خواندن برای انسان برای دانلود، که اغلب در اعلانهای سیستم یا رابط کاربری دانلود مرورگر نمایش داده میشود. برای درک کاربر بسیار مهم است.
- `icons` (آرایهای از اشیاء): آرایهای از اشیاء تصویر، که هر کدام دارای ویژگیهای `src`، `sizes` و `type` هستند. این آیکونها توسط سیستم عامل برای نمایش بصری دانلود استفاده میشوند.
- `downloadTotal` (عدد): تعداد کل بایتهای مورد انتظار برای دانلود. این مورد بسیار توصیه میشود زیرا به مرورگر اجازه میدهد تا یک نوار پیشرفت دقیق در اعلانهای سیستم نمایش دهد. اگر ارائه نشود، پیشرفت به صورت یک چرخنده نامشخص نمایش داده میشود.
- `uploadTotal` (عدد): مشابه `downloadTotal`، اما برای آپلودهای پسزمینه (اگرچه این راهنما بر دانلودها تمرکز دارد، API از هر دو پشتیبانی میکند).
- `start_url` (رشته): یک URL اختیاری که اگر کاربر روی اعلان سیستم مرتبط با این واکشی پسزمینه کلیک کند، باید به آن هدایت شود.
مدیریت رویدادهای واکشی پسزمینه در سرویس ورکر
جادوی واقعی در سرویس ورکر اتفاق میافتد. پس از آغاز، پشته شبکه مرورگر کنترل را به دست میگیرد، اما سرویس ورکر شما مسئول واکنش به رویدادهای چرخه حیات واکشی پسزمینه است. این رویدادها فرصتهایی برای ذخیره دادههای دانلود شده، اطلاعرسانی به کاربر یا مدیریت خطاها فراهم میکنند. سرویس ورکر شما باید شنوندگان رویداد را برای این رویدادهای خاص ثبت کند:
// service-worker.js
self.addEventListener('backgroundfetchsuccess', async (event) => {
const bgFetch = event.registration;
console.log(`Background fetch ${bgFetch.id} completed successfully.`);
// Access downloaded records
const records = await bgFetch.matchAll(); // Get all fetched responses
// For simplicity, let's assume a single file download
const response = await records[0].responseReady; // Wait for the response to be ready
if (response.ok) {
// Store the downloaded content, e.g., in Cache API or IndexedDB
const cache = await caches.open('my-downloads-cache');
await cache.put(bgFetch.id, response);
console.log(`File for ${bgFetch.id} cached.`);
// Send a notification to the user
await self.registration.showNotification(bgFetch.title || 'Download Complete',
{
body: `${bgFetch.title || 'Your download'} is ready! Click to open.`,
icon: bgFetch.icons ? bgFetch.icons[0].src : '/images/default-icon.png',
data: { url: bgFetch.start_url || '/' } // Optional: URL to open on click
}
);
} else {
console.error(`Failed to get a successful response for ${bgFetch.id}`);
await self.registration.showNotification(bgFetch.title || 'Download Failed',
{
body: `There was an issue with ${bgFetch.title || 'your download'}.`,
icon: '/images/error-icon.png',
}
);
}
// Clean up the background fetch registration once handled
bgFetch.update({ status: 'completed' }); // Mark as completed
bgFetch.abort(); // Optional: Abort to clean up internal browser state if no longer needed
});
self.addEventListener('backgroundfetchfail', async (event) => {
const bgFetch = event.registration;
console.error(`Background fetch ${bgFetch.id} failed. Reason: ${bgFetch.failureReason}`);
await self.registration.showNotification(bgFetch.title || 'Download Failed',
{
body: `Unfortunately, ${bgFetch.title || 'your download'} could not be completed. Reason: ${bgFetch.failureReason || 'Unknown'}`,
icon: bgFetch.icons ? bgFetch.icons[0].src : '/images/error-icon.png',
}
);
// Implement retry logic or alert user to network issues
// Consider storing info in IndexedDB to display to user when app is next opened
});
self.addEventListener('backgroundfetchabort', async (event) => {
const bgFetch = event.registration;
console.warn(`Background fetch ${bgFetch.id} was aborted.`);
// Inform user if necessary, clean up any associated data
await self.registration.showNotification(bgFetch.title || 'Download Aborted',
{
body: `${bgFetch.title || 'Your download'} was cancelled.`,
icon: bgFetch.icons ? bgFetch.icons[0].src : '/images/warning-icon.png',
}
);
});
self.addEventListener('backgroundfetchclick', async (event) => {
const bgFetch = event.registration;
console.log(`Background fetch ${bgFetch.id} notification clicked.`);
// User clicked on the notification
if (bgFetch.start_url) {
clients.openWindow(bgFetch.start_url);
} else {
// Or open a specific page to show downloads
clients.openWindow('/downloads');
}
});
// For progress updates, the 'progress' event is also fired in the Service Worker,
// but often the main thread handles this if it's active for UI updates.
// If the main thread is not active, the Service Worker can still use this event
// for logging or more complex background processing before the 'success' event.
self.addEventListener('backgroundfetchprogress', (event) => {
const bgFetch = event.registration;
console.log(`Service Worker: Progress for ${bgFetch.id}: ${bgFetch.downloaded} of ${bgFetch.downloadTotal}`);
// You might not want to send a notification on every progress update
// but rather use it to update IndexedDB or for internal logic.
});
بیایید هر رویداد سرویس ورکر را توضیح دهیم:
-
backgroundfetchsuccess: زمانی فعال میشود که تمام درخواستهای موجود در واکشی پسزمینه با موفقیت تکمیل شوند. این رویداد حیاتی برای سرویس ورکر شما جهت پردازش محتوای دانلود شده است. شما معمولاً ازevent.registration.matchAll()برای دریافت آرایهای از اشیاءResponseمتناظر با درخواستهای اصلی استفاده میکنید. از آنجا، میتوانید این پاسخها را با استفاده از Cache API برای دسترسی آفلاین ذخیره کنید، یا آنها را در IndexedDB برای ذخیرهسازی دادههای ساختاریافتهتر پایدار کنید. پس از پردازش، عمل خوبی است که از طریق یک اعلان سیستمی به کاربر اطلاع دهید و احتمالاً ثبتنام واکشی پسزمینه را پاکسازی کنید. -
backgroundfetchfail: زمانی فعال میشود که هر یک از درخواستهای درون واکشی پسزمینه پس از تمام شدن تلاشهای مجدد با شکست مواجه شود. این رویداد به سرویس ورکر شما اجازه میدهد تا خطاها را به آرامی مدیریت کند، کاربر را از شکست مطلع سازد و احتمالاً مراحل عیبیابی را پیشنهاد دهد. ویژگیevent.registration.failureReasonاطلاعات بیشتری در مورد دلیل شکست واکشی ارائه میدهد (به عنوان مثال، 'aborted'، 'bad-status'، 'quota-exceeded'، 'network-error'، 'none'). -
backgroundfetchabort: زمانی فعال میشود که واکشی پسزمینه به صورت برنامهنویسی توسط برنامه (چه از رشته اصلی و چه از سرویس ورکر) با استفاده ازbgFetch.abort()لغو شود، یا اگر کاربر آن را از طریق رابط کاربری مرورگر لغو کند. این رویداد برای پاکسازی و اطلاعرسانی به کاربر مبنی بر توقف عملیات است. -
backgroundfetchclick: زمانی فعال میشود که کاربر روی یک اعلان سیستمی ایجاد شده توسط واکشی پسزمینه کلیک کند. این به سرویس ورکر شما اجازه میدهد با باز کردن یک صفحه خاص در برنامه شما (به عنوان مثال، بخش «دانلودها») که کاربر میتواند به محتوای تازه دانلود شده خود دسترسی پیدا کند، پاسخ دهد. -
backgroundfetchprogress: به صورت دورهای در سرویس ورکر فعال میشود تا پیشرفت در حال انجام دانلود را گزارش دهد. در حالی که این رویداد درBackgroundFetchRegistrationرشته اصلی نیز در دسترس است، سرویس ورکر میتواند از آن برای ثبت وقایع در پسزمینه، بهروزرسانی ذخیرهسازی پایدار با پیشرفت، یا حتی برای منطق پیشرفتهتر در صورتی که برنامه اصلی فعال نباشد، استفاده کند. با این حال، برای بهروزرسانیهای دقیق رابط کاربری، اغلب کارآمدتر است که به این رویداد مستقیماً در شیءBackgroundFetchRegistrationبازگشتی به رشته اصلی گوش دهید، به شرطی که تب باز بماند.
نظارت بر پیشرفت و وضعیت
شیء BackgroundFetchRegistration پنجره شما به وضعیت و پیشرفت یک واکشی پسزمینه در حال انجام یا تکمیل شده است. هم رشته اصلی و هم سرویس ورکر میتوانند به این اطلاعات دسترسی داشته باشند. در رشته اصلی، شما این شیء را مستقیماً هنگام فراخوانی fetch() دریافت میکنید. در سرویس ورکر، این به عنوان event.registration در رویدادهای واکشی پسزمینه در دسترس است.
ویژگیهای کلیدی `BackgroundFetchRegistration` عبارتند از:
- `id` (رشته): شناسه منحصر به فردی که هنگام شروع واکشی ارائه شده است.
- `downloadTotal` (عدد): تعداد کل بایتهای مورد انتظار برای دانلود، همانطور که در `options` مشخص شده است (یا 0 اگر مشخص نشده باشد).
- `downloaded` (عدد): تعداد فعلی بایتهای دانلود شده تاکنون.
- `uploadTotal` (عدد): تعداد کل بایتهای مورد انتظار برای آپلود (در صورت وجود).
- `uploaded` (عدد): تعداد فعلی بایتهای آپلود شده تاکنون (در صورت وجود).
- `result` (رشته): 'success'، 'failure' یا 'aborted' پس از تکمیل واکشی. قبل از تکمیل، `null` است.
- `failureReason` (رشته): جزئیات بیشتری را در صورتی که `result` برابر با 'failure' باشد، ارائه میدهد (به عنوان مثال، 'network-error'، 'quota-exceeded').
- `direction` (رشته): 'download' یا 'upload'.
- `status` (رشته): 'pending'، 'succeeded'، 'failed'، 'aborted'. این وضعیت فعلی واکشی است.
شما همچنین میتوانید واکشیهای پسزمینه موجود را با استفاده از `BackgroundFetchManager` بازیابی کنید:
-
`registration.backgroundFetch.get(id)`: یک
BackgroundFetchRegistrationخاص را با شناسه آن بازیابی میکند. - `registration.backgroundFetch.getIds()`: یک Promise را برمیگرداند که به آرایهای از تمام شناسههای واکشی پسزمینه فعال که توسط سرویس ورکر شما مدیریت میشوند، حل میشود.
// Main thread or Service Worker:
async function checkExistingDownloads() {
if ('serviceWorker' in navigator && 'BackgroundFetchManager' in window) {
const registration = await navigator.serviceWorker.ready;
const ids = await registration.backgroundFetch.getIds();
console.log('Active background fetch IDs:', ids);
for (const id of ids) {
const bgFetch = await registration.backgroundFetch.get(id);
if (bgFetch) {
console.log(`Fetch ID: ${bgFetch.id}, Status: ${bgFetch.status}, Progress: ${bgFetch.downloaded}/${bgFetch.downloadTotal}`);
// Attach event listeners if the current page didn't initiate it
// (useful for re-opening app and seeing ongoing fetches)
bgFetch.addEventListener('progress', () => { /* update UI */ });
bgFetch.addEventListener('success', () => { /* handle success */ });
// etc.
}
}
}
}
// checkExistingDownloads();
موارد استفاده عملی و مثالهای جهانی
API واکشی پسزمینه امکانات فراوانی را برای برنامههای وب باز میکند و آنها را مقاومتر، کاربرپسندتر و قادر به رقابت با برنامههای بومی در مقیاس جهانی میسازد. در اینجا چند مورد استفاده قانعکننده آورده شده است:
مصرف رسانه آفلاین (فیلم، موسیقی، پادکست)
تصور کنید کاربری در یک روستای دورافتاده در هند، جایی که دسترسی به اینترنت پراکنده و گران است، میخواهد مستندهای آموزشی یا یک آلبوم موسیقی را دانلود کند. یا یک مسافر تجاری در یک پرواز طولانی بر فراز اقیانوس اطلس، که مایل است فیلمهای از پیش دانلود شده را بدون اتکا به وایفای نامطمئن داخل پرواز تماشا کند. پلتفرمهای پخش رسانه میتوانند از واکشی پسزمینه برای اجازه دادن به کاربران برای قرار دادن فایلهای ویدیویی بزرگ، کل سری پادکستها یا آلبومهای موسیقی در صف دانلود استفاده کنند. این دانلودها میتوانند بیصدا در پسزمینه ادامه پیدا کنند، حتی اگر کاربر برنامه را ببندد، و برای مصرف آفلاین آماده باشند. این امر به طور قابل توجهی تجربه کاربری را برای مخاطبان جهانی که با چالشهای اتصال متنوعی روبرو هستند، افزایش میدهد.
همگامسازی و پشتیبانگیری فایلهای بزرگ (ذخیرهسازی ابری)
راهحلهای ذخیرهسازی ابری، ویرایشگرهای اسناد آنلاین و سیستمهای مدیریت داراییهای دیجیتال اغلب با فایلهای بزرگ - تصاویر با وضوح بالا، فایلهای پروژه ویدیویی یا صفحات گسترده پیچیده - سروکار دارند. کاربری در برزیل که یک فایل طراحی بزرگ را در یک پلتفرم مشارکتی آپلود میکند، یا تیمی در آلمان که یک پوشه پروژه را همگامسازی میکند، اغلب با مشکلات قطع اتصال مواجه میشود. واکشی پسزمینه میتواند تضمین کند که این آپلودها و دانلودهای حیاتی به طور قابل اعتماد تکمیل میشوند. اگر آپلود قطع شود، مرورگر میتواند به طور خودکار آن را از سر بگیرد و همگامسازی دادههای یکپارچه و آرامش خاطر را برای کاربرانی که با اطلاعات ارزشمند سروکار دارند، فراهم کند.
بهروزرسانی داراییهای برنامه وب پیشرونده (PWA)
PWAها برای ارائه تجربیاتی شبیه به اپلیکیشن طراحی شدهاند و بخشی از آن شامل بهروز ماندن است. برای PWAهایی با داراییهای آفلاین قابل توجه (مانند کتابخانههای تصویر بزرگ، پایگاههای داده گسترده سمت کلاینت یا فریمورکهای رابط کاربری پیچیده)، بهروزرسانی این داراییها میتواند یک عملیات پسزمینه قابل توجه باشد. به جای مجبور کردن کاربر به ماندن در صفحه «در حال بارگیری بهروزرسانیها»، واکشی پسزمینه میتواند این دانلودهای دارایی را بیصدا انجام دهد. کاربر میتواند به تعامل با نسخه موجود PWA ادامه دهد و هنگامی که داراییهای جدید آماده شدند، سرویس ورکر میتواند به طور یکپارچه آنها را جایگزین کند و یک تجربه بهروزرسانی بدون اصطکاک را فراهم کند.
دانلود و بهروزرسانی بازیها
بازیهای آنلاین، حتی آنهایی که مبتنی بر مرورگر هستند، به طور فزایندهای غنی از ویژگیها هستند و اغلب به دانلود داراییهای قابل توجهی (بافتها، فایلهای صوتی، دادههای سطح) نیاز دارند. یک گیمر در کره جنوبی که منتظر یک بهروزرسانی جدید بازی است یا کاربری در کانادا که یک بازی کاملاً جدید مبتنی بر مرورگر را دانلود میکند، نمیخواهد به یک تب باز وابسته باشد. واکشی پسزمینه به توسعهدهندگان بازی امکان میدهد این دانلودهای اولیه بزرگ و بهروزرسانیهای بعدی را به طور کارآمد مدیریت کنند. کاربران میتوانند دانلود را آغاز کنند، مرورگر خود را ببندند و بعداً به یک بازی کاملاً بهروز شده یا نصب شده بازگردند، که تجربه بازی را برای عناوین مبتنی بر وب به شدت بهبود میبخشد.
همگامسازی دادههای سازمانی
برای سازمانهای بزرگی که در مناطق زمانی و مناطق مختلف فعالیت میکنند، همگامسازی دادهها از اهمیت بالایی برخوردار است. تصور کنید یک تیم فروش در آفریقای جنوبی نیاز به دانلود یک کاتالوگ جامع محصول با هزاران تصویر و مشخصات برای ارائه به مشتریان به صورت آفلاین دارد، یا یک شرکت مهندسی در ژاپن که فایلهای CAD عظیم را همگامسازی میکند. واکشی پسزمینه یک مکانیزم قابل اعتماد برای این انتقالهای دادهای حیاتی فراهم میکند و تضمین میکند که کارمندان همیشه به آخرین اطلاعات دسترسی دارند، حتی زمانی که از راه دور یا در مناطقی با زیرساخت اینترنت محدود کار میکنند.
پیادهسازی واکشی پسزمینه: یک راهنمای گام به گام
بیایید یک مثال پیادهسازی دقیقتر را بررسی کنیم که منطق رشته اصلی و سرویس ورکر را برای مدیریت دانلود یک فایل بزرگ ترکیب میکند.
۱. سرویس ورکر خود را ثبت کنید
ابتدا، اطمینان حاصل کنید که سرویس ورکر شما ثبت و فعال شده است. این کد معمولاً در فایل جاوا اسکریپت اصلی برنامه شما قرار میگیرد:
// main.js
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/service-worker.js', { scope: '/' })
.then(registration => {
console.log('Service Worker registered with scope:', registration.scope);
})
.catch(error => {
console.error('Service Worker registration failed:', error);
});
});
}
۲. واکشی را از رشته اصلی آغاز کنید
هنگامی که کاربر تصمیم به دانلود یک فایل بزرگ میگیرد، منطق اصلی برنامه شما واکشی پسزمینه را فعال میکند. بیایید تابعی ایجاد کنیم که این کار را انجام دهد و یک جایگزین برای مرورگرهای پشتیبانی نشده تضمین کند.
// main.js (continued)
async function initiateLargeFileDownload(fileUrl, filename, fileSize) {
const downloadId = `download-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
const downloadTitle = `Downloading ${filename}`;
if ('serviceWorker' in navigator && 'BackgroundFetchManager' in window) {
try {
const registration = await navigator.serviceWorker.ready;
const bgFetch = await registration.backgroundFetch.fetch(
downloadId,
[{ url: fileUrl, headers: { 'Accept-Encoding': 'identity' } }], // Use Request object for more control
{
title: downloadTitle,
icons: [
{ src: '/images/download-icon-96.png', sizes: '96x96', type: 'image/png' },
{ src: '/images/download-icon-128.png', sizes: '128x128', type: 'image/png' }
],
downloadTotal: fileSize // Ensure this is accurate!
}
);
console.log('Background fetch initiated:', bgFetch.id);
// Attach event listeners for real-time UI updates if the tab is active
bgFetch.addEventListener('progress', (event) => {
const currentFetch = event.registration;
const percentage = Math.round((currentFetch.downloaded / currentFetch.downloadTotal) * 100);
console.log(`Main Thread: ${currentFetch.id} Progress: ${percentage}% (${currentFetch.downloaded} of ${currentFetch.downloadTotal})`);
updateDownloadProgressUI(currentFetch.id, percentage, currentFetch.downloaded, currentFetch.downloadTotal, 'downloading');
});
bgFetch.addEventListener('success', (event) => {
const currentFetch = event.registration;
console.log(`Main Thread: ${currentFetch.id} succeeded.`);
updateDownloadProgressUI(currentFetch.id, 100, currentFetch.downloaded, currentFetch.downloadTotal, 'succeeded');
showToastNotification(`'${filename}' download complete!`);
// The service worker will handle storage and actual file availability
});
bgFetch.addEventListener('fail', (event) => {
const currentFetch = event.registration;
console.error(`Main Thread: ${currentFetch.id} failed. Reason: ${currentFetch.failureReason}`);
updateDownloadProgressUI(currentFetch.id, 0, 0, currentFetch.downloadTotal, 'failed', currentFetch.failureReason);
showToastNotification(`'${filename}' download failed: ${currentFetch.failureReason}`, 'error');
});
bgFetch.addEventListener('abort', (event) => {
const currentFetch = event.registration;
console.warn(`Main Thread: ${currentFetch.id} aborted.`);
updateDownloadProgressUI(currentFetch.id, 0, 0, currentFetch.downloadTotal, 'aborted');
showToastNotification(`'${filename}' download aborted.`, 'warning');
});
// Store the background fetch ID in local storage or IndexedDB
// so that the app can re-attach to it if the user closes and re-opens the tab
storeOngoingDownload(downloadId, filename, fileSize);
} catch (error) {
console.error('Failed to initiate background fetch:', error);
fallbackDownload(fileUrl, filename);
}
} else {
console.warn('Background Fetch API not supported. Using fallback download.');
fallbackDownload(fileUrl, filename);
}
}
function updateDownloadProgressUI(id, percentage, downloaded, total, status, reason = '') {
const element = document.getElementById(`download-item-${id}`);
if (element) {
element.querySelector('.progress-bar').style.width = `${percentage}%`;
element.querySelector('.status-text').textContent = `${status.toUpperCase()}: ${percentage}% (${formatBytes(downloaded)} / ${formatBytes(total)}) ${reason ? `(${reason})` : ''}`;
// Add more complex UI updates, e.g., showing pause/cancel buttons
} else {
// Create a new UI element if this is a new download or app was just opened
createDownloadUIElement(id, percentage, downloaded, total, status, reason);
}
}
function createDownloadUIElement(id, percentage, downloaded, total, status, reason) {
const downloadsContainer = document.getElementById('downloads-list');
const itemHtml = `
${id.split('-')[0]} File
${status.toUpperCase()}: ${percentage}% (${formatBytes(downloaded)} / ${formatBytes(total)}) ${reason ? `(${reason})` : ''}
`;
downloadsContainer.insertAdjacentHTML('beforeend', itemHtml);
}
async function abortDownload(id) {
if ('serviceWorker' in navigator && 'BackgroundFetchManager' in window) {
const registration = await navigator.serviceWorker.ready;
const bgFetch = await registration.backgroundFetch.get(id);
if (bgFetch) {
await bgFetch.abort();
console.log(`Aborted fetch ${id} from UI.`);
}
}
}
function fallbackDownload(url, filename) {
const link = document.createElement('a');
link.href = url;
link.download = filename;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
showToastNotification(`Downloading '${filename}' via browser. Please keep tab open.`);
}
function showToastNotification(message, type = 'info') {
// Implement a simple UI toast notification system
console.log(`Toast (${type}): ${message}`);
}
function formatBytes(bytes, decimals = 2) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}
function storeOngoingDownload(id, filename, fileSize) {
// Using localStorage for simplicity, but IndexedDB is better for robust storage
let ongoingDownloads = JSON.parse(localStorage.getItem('ongoingDownloads') || '[]');
ongoingDownloads.push({ id, filename, fileSize, status: 'pending', downloaded: 0, total: fileSize });
localStorage.setItem('ongoingDownloads', JSON.stringify(ongoingDownloads));
}
async function loadAndMonitorExistingDownloads() {
if (!('serviceWorker' in navigator && 'BackgroundFetchManager' in window)) return;
const registration = await navigator.serviceWorker.ready;
const ids = await registration.backgroundFetch.getIds();
const storedDownloads = JSON.parse(localStorage.getItem('ongoingDownloads') || '[]');
for (const stored of storedDownloads) {
if (ids.includes(stored.id)) {
const bgFetch = await registration.backgroundFetch.get(stored.id);
if (bgFetch) {
// Re-attach listeners and update UI for existing fetches
const percentage = Math.round((bgFetch.downloaded / bgFetch.downloadTotal) * 100);
updateDownloadProgressUI(bgFetch.id, percentage, bgFetch.downloaded, bgFetch.downloadTotal, bgFetch.status);
bgFetch.addEventListener('progress', (event) => {
const currentFetch = event.registration;
const percentage = Math.round((currentFetch.downloaded / currentFetch.downloadTotal) * 100);
updateDownloadProgressUI(currentFetch.id, percentage, currentFetch.downloaded, currentFetch.downloadTotal, 'downloading');
});
// Re-attach success, fail, abort listeners as well
bgFetch.addEventListener('success', (event) => { /* ... */ });
bgFetch.addEventListener('fail', (event) => { /* ... */ });
bgFetch.addEventListener('abort', (event) => { /* ... */ });
}
} else {
// This download might have completed or failed while the app was closed
// Check bgFetch.result if available from a previous session, update UI accordingly
console.log(`Download ${stored.id} not found in active fetches, likely completed or failed.`);
// Potentially remove from local storage or mark as completed/failed
}
}
}
// Call this on app load to resume UI for ongoing downloads
// window.addEventListener('load', loadAndMonitorExistingDownloads);
نکته در مورد هدرهای درخواست: مثال از headers: { 'Accept-Encoding': 'identity' } استفاده میکند. این یک روش رایج هنگام کار با دانلودهایی است که به صورت خام ذخیره میشوند، و تضمین میکند که سرور کدگذاریهای محتوا (مانند gzip) را که ممکن است نیاز به بازگشایی در سمت کلاینت قبل از ذخیره داشته باشند، اعمال نمیکند. اگر سرور از قبل فایلهای فشردهنشده ارسال میکند یا اگر قصد دارید آنها را از حالت فشرده خارج کنید، این ممکن است ضروری نباشد.
۳. مدیریت رویدادها در سرویس ورکر
فایل `service-worker.js` شما شامل شنوندگان رویداد همانطور که قبلاً توضیح داده شد، خواهد بود. بیایید منطق ذخیره و اطلاعرسانی را اصلاح کنیم.
// service-worker.js
// Cache names for downloads and potentially for site assets
const CACHE_NAME_DOWNLOADS = 'my-large-downloads-v1';
self.addEventListener('install', (event) => {
self.skipWaiting(); // Activate new service worker immediately
console.log('Service Worker installed.');
});
self.addEventListener('activate', (event) => {
event.waitUntil(clients.claim()); // Take control of existing clients
console.log('Service Worker activated.');
});
// backgroundfetchsuccess: Store content and notify user
self.addEventListener('backgroundfetchsuccess', async (event) => {
const bgFetch = event.registration;
console.log(`SW: Background fetch ${bgFetch.id} succeeded.`);
let downloadSuccessful = true;
try {
const records = await bgFetch.matchAll();
const cache = await caches.open(CACHE_NAME_DOWNLOADS);
for (const record of records) {
const response = await record.responseReady;
if (response.ok) {
// Use a unique cache key, e.g., the original URL or bgFetch.id + a counter
await cache.put(record.request.url, response.clone()); // Clone is important as response can only be consumed once
console.log(`SW: Stored ${record.request.url} in cache.`);
} else {
console.error(`SW: Failed to get successful response for ${record.request.url}. Status: ${response.status}`);
downloadSuccessful = false;
// Potentially remove partially downloaded files or mark as failed
break; // Stop processing if one part failed
}
}
if (downloadSuccessful) {
await self.registration.showNotification(bgFetch.title || 'Download Complete',
{
body: `${bgFetch.title || 'Your download'} is now available offline!`,
icon: bgFetch.icons ? bgFetch.icons[0].src : '/images/default-icon.png',
badge: '/images/badge-icon.png', // Optional: Small icon for taskbar/status bar
data: { bgFetchId: bgFetch.id, type: 'download-complete' },
actions: [
{ action: 'open-download', title: 'Open', icon: '/images/open-icon.png' },
{ action: 'delete-download', title: 'Delete', icon: '/images/delete-icon.png' }
]
}
);
// Optional: Update IndexedDB to mark download as complete
} else {
// Handle scenario where not all parts succeeded
await self.registration.showNotification(bgFetch.title || 'Download Partial/Failed',
{
body: `Part of ${bgFetch.title || 'your download'} could not be completed. Please check.`,
icon: '/images/error-icon.png',
}
);
}
} catch (error) {
console.error(`SW: Error during backgroundfetchsuccess for ${bgFetch.id}:`, error);
downloadSuccessful = false;
await self.registration.showNotification(bgFetch.title || 'Download Error',
{
body: `An unexpected error occurred with ${bgFetch.title || 'your download'}.`,
icon: '/images/error-icon.png',
}
);
}
// After handling, cleanup the background fetch registration
// The spec recommends not calling abort() immediately after success/fail
// if you want to keep the registration active for monitoring or historical data.
// However, if the download is truly done and its data stored, you might clear it.
// For this example, let's consider it handled.
});
// backgroundfetchfail: Notify user about failure
self.addEventListener('backgroundfetchfail', async (event) => {
const bgFetch = event.registration;
console.error(`SW: Background fetch ${bgFetch.id} failed. Reason: ${bgFetch.failureReason}`);
await self.registration.showNotification(bgFetch.title || 'Download Failed',
{
body: `Unfortunately, ${bgFetch.title || 'your download'} could not be completed. Reason: ${bgFetch.failureReason || 'Unknown'}`,
icon: bgFetch.icons ? bgFetch.icons[0].src : '/images/error-icon.png',
badge: '/images/error-badge.png',
data: { bgFetchId: bgFetch.id, type: 'download-failed' }
}
);
// Optional: Update IndexedDB to mark download as failed, potentially offering a retry option
});
// backgroundfetchabort: Notify user about cancellation
self.addEventListener('backgroundfetchabort', async (event) => {
const bgFetch = event.registration;
console.warn(`SW: Background fetch ${bgFetch.id} was aborted.`);
// Optionally remove partial downloads from cache/IndexedDB
await self.registration.showNotification(bgFetch.title || 'Download Aborted',
{
body: `${bgFetch.title || 'Your download'} was cancelled.`,
icon: bgFetch.icons ? bgFetch.icons[0].src : '/images/warning-icon.png',
data: { bgFetchId: bgFetch.id, type: 'download-aborted' }
}
);
});
// notificationclick: Handle user interaction with notifications
self.addEventListener('notificationclick', (event) => {
const notification = event.notification;
const primaryClient = clients.matchAll({ type: 'window', includeUncontrolled: true }).then(clientList => {
for (const client of clientList) {
if (client.url.startsWith(self.location.origin) && 'focus' in client) {
return client.focus();
}
}
return clients.openWindow(notification.data.url || '/downloads');
});
event.waitUntil(primaryClient);
// Handle notification actions (e.g., 'Open', 'Delete')
if (event.action === 'open-download') {
event.waitUntil(clients.openWindow('/downloads'));
} else if (event.action === 'delete-download') {
// Implement logic to delete the downloaded file from cache/IndexedDB
// and update the main thread UI if active.
const bgFetchIdToDelete = notification.data.bgFetchId;
// Example: Delete from Cache API
caches.open(CACHE_NAME_DOWNLOADS).then(cache => {
cache.delete(bgFetchIdToDelete); // Or the specific URL associated with the ID
console.log(`SW: Deleted download for ${bgFetchIdToDelete} from cache.`);
});
notification.close();
}
});
// backgroundfetchprogress: Use for internal logic or less frequent updates if main thread is not active
self.addEventListener('backgroundfetchprogress', (event) => {
const bgFetch = event.registration;
console.log(`SW: Progress for ${bgFetch.id}: ${bgFetch.downloaded} of ${bgFetch.downloadTotal}`);
// Here you could update IndexedDB with progress for persistent state,
// but typically, progress notifications to the user are handled by the OS/browser.
});
۴. نمایش پیشرفت به کاربر (رشته اصلی و اعلانها)
همانطور که در کد رشته اصلی نشان داده شد، `bgFetch.addEventListener('progress', ...)` برای بهروزرسانی رابط کاربری برنامه در حالی که تب باز است، حیاتی است. برای عملیات پسزمینه، اعلانهای بومی سیستم مرورگر (که توسط `self.registration.showNotification()` در سرویس ورکر فعال میشوند) بهروزرسانیهای پیشرفت و هشدارها را ارائه میدهند، حتی زمانی که مرورگر بسته یا کوچک شده است. این رویکرد دوگانه یک تجربه کاربری عالی را بدون توجه به تعامل فعال آنها با برنامه تضمین میکند.
طراحی رابط کاربری شما برای نمایش زیبا پیشرفت دانلود، اجازه دادن به کاربران برای لغو واکشیها و نشان دادن وضعیت دانلودهای تکمیل شده یا ناموفق، حیاتی است. یک بخش اختصاصی «دانلودها» را در PWA خود در نظر بگیرید که کاربران بتوانند تمام فعالیتهای واکشی پسزمینه خود را مرور کنند.
۵. بازیابی محتوای دانلود شده
هنگامی که یک واکشی پسزمینه موفقیتآمیز است و سرویس ورکر محتوا را ذخیره کرده است (به عنوان مثال، در Cache API یا IndexedDB)، برنامه اصلی شما به راهی برای دسترسی به آن نیاز دارد. برای محتوای ذخیره شده در Cache API، میتوانید از caches.match() یا caches.open() استاندارد برای بازیابی شیء `Response` استفاده کنید. برای IndexedDB، از API آن برای جستجوی دادههای ذخیره شده خود استفاده میکنید.
// main.js (example for retrieving cached content)
async function getDownloadedFile(originalUrl) {
if ('caches' in window) {
const cache = await caches.open(CACHE_NAME_DOWNLOADS);
const response = await cache.match(originalUrl);
if (response) {
console.log(`Retrieved ${originalUrl} from cache.`);
// Now you can work with the response, e.g., create an Object URL for display
const blob = await response.blob();
return URL.createObjectURL(blob);
} else {
console.log(`${originalUrl} not found in cache.`);
return null;
}
}
return null;
}
// Example: Display a downloaded video
// const videoUrl = await getDownloadedFile('/path/to/my/large-movie.mp4');
// if (videoUrl) {
// const videoElement = document.getElementById('my-video-player');
// videoElement.src = videoUrl;
// videoElement.play();
// }
ملاحظات پیشرفته و بهترین شیوهها
برای ساخت یک تجربه واقعاً قوی و کاربرپسند با API واکشی پسزمینه، این موضوعات پیشرفته و بهترین شیوهها را در نظر بگیرید:
مدیریت خطا و مکانیزمهای تلاش مجدد
API به طور ذاتی منطق تلاش مجدد را فراهم میکند، اما برنامه شما باید برای سناریوهای مختلف شکست آماده باشد. هنگامی که یک رویداد `backgroundfetchfail` رخ میدهد، ویژگی `event.registration.failureReason` بسیار ارزشمند است. دلایل ممکن عبارتند از `'network-error'`، `'bad-status'` (به عنوان مثال، یک پاسخ HTTP 404 یا 500)، `'quota-exceeded'` (اگر مرورگر فضای ذخیرهسازی تمام کند)، یا `'aborted'`. سرویس ورکر شما میتواند:
- ثبت خطاها: جزئیات خطا را به سرویس تجزیه و تحلیل یا ثبت وقایع خود ارسال کنید تا عملکرد را نظارت کرده و نقاط شکست رایج را در سطح جهانی شناسایی کنید.
- اطلاعرسانی به کاربر: دلیل شکست را به وضوح از طریق اعلانهای پایدار به کاربر اطلاع دهید.
- منطق تلاش مجدد: برای `network-error`، ممکن است به کاربر پیشنهاد دهید اتصال خود را بررسی کند. برای `bad-status`، ممکن است توصیه کنید با پشتیبانی تماس بگیرد. برای `quota-exceeded`، پیشنهاد دهید فضا را خالی کند. یک مکانیزم تلاش مجدد هوشمند (مانند عقبنشینی نمایی) را در صورت لزوم پیادهسازی کنید، هرچند مرورگر تلاشهای مجدد اولیه را به صورت داخلی مدیریت میکند.
- پاکسازی: فایلهای ناقص یا دادههای موقت مرتبط با واکشیهای ناموفق را برای آزاد کردن فضا حذف کنید.
بازخورد رابط کاربری و اعلانها
ارتباط مؤثر با کاربر از اهمیت بالایی برخوردار است. این شامل:
- نوارهای پیشرفت: نوارهای پیشرفت پویا در صفحه وب هنگام فعال بودن، و اعلانهای سطح سیستم (با مشخص کردن `downloadTotal`) برای پیشرفت در پسزمینه.
- نشانگرهای وضعیت: آیکونها یا متنهای واضح که «در حال دانلود»، «متوقف شده»، «ناموفق»، «تکمیل شده» یا «لغو شده» را نشان میدهند.
- اعلانهای قابل اقدام: از اقدامات اعلان (`actions` در `showNotification`) استفاده کنید تا به کاربران اجازه دهید مستقیماً از اعلان سیستم یک دانلود را «باز»، «حذف» یا «دوباره تلاش» کنند و راحتی را افزایش دهید.
- لیست دانلود پایدار: یک بخش اختصاصی در PWA شما (به عنوان مثال، '/downloads') که کاربران بتوانند وضعیت تمام واکشیهای پسزمینه گذشته و در حال انجام را مشاهده کنند، موارد ناموفق را دوباره شروع کنند یا محتوای دانلود شده را مدیریت کنند. این امر به ویژه برای کاربرانی در مناطقی با اتصالات ناپایدار که ممکن است به طور مکرر دانلودها را بازبینی کنند، مهم است.
مدیریت پهنای باند و منابع
مراقب پهنای باند کاربر باشید، به خصوص در مناطقی که داده گران یا محدود است. API واکشی پسزمینه برای کارآمد بودن طراحی شده است، اما میتوانید با موارد زیر بیشتر بهینهسازی کنید:
- احترام به ترجیحات کاربر:
navigator.connection.effectiveTypeیاnavigator.connection.saveDataرا برای تعیین شرایط شبکه و ترجیح صرفهجویی در داده کاربر بررسی کنید. دانلودهای با کیفیت پایینتر را ارائه دهید یا قبل از انتقالهای بزرگ در شبکههای کند یا تعرفهای، تأییدیه بخواهید. - دستهبندی درخواستها: برای چندین فایل کوچک، اغلب کارآمدتر است که آنها را در یک عملیات واکشی پسزمینه گروهبندی کنید تا اینکه چندین واکشی فردی را آغاز کنید.
- اولویتبندی: اگر چندین فایل را دانلود میکنید، اولویت دادن به محتوای حیاتی را در نظر بگیرید.
- مدیریت سهمیه دیسک: از سهمیههای ذخیرهسازی مرورگر آگاه باشید. `failureReason` با مقدار `quota-exceeded` زمانی فعال میشود که سعی کنید بیش از حد دانلود کنید. استراتژیهایی برای مدیریت ذخیرهسازی پیادهسازی کنید، مانند اجازه دادن به کاربران برای پاک کردن دانلودهای قدیمی.
ذخیرهسازی آفلاین (IndexedDB، Cache API)
API واکشی پسزمینه درخواست شبکه را مدیریت میکند، اما شما مسئول ذخیره اشیاء `Response` بازیابی شده هستید. دو مکانیزم اصلی عبارتند از:
-
Cache API: ایدهآل برای ذخیره داراییهای استاتیک، فایلهای رسانهای، یا هر پاسخی که میتواند مستقیماً به یک URL نگاشت شود. استفاده از آن با
caches.open().put(request, response)ساده است. - IndexedDB: یک API قدرتمند و سطح پایین برای ذخیرهسازی سمت کلاینت مقادیر زیادی از دادههای ساختاریافته. از این برای طرحهای داده پیچیدهتر، فرادادههای مرتبط با دانلودها، یا زمانی که به قابلیتهای جستجوی قوی نیاز دارید، استفاده کنید. به عنوان مثال، ذخیره فرادادههای یک ویدیوی دانلود شده (عنوان، طول، توضیحات، تاریخ دانلود) در کنار دادههای باینری آن (به عنوان یک Blob). کتابخانههایی مانند Dexie.js میتوانند تعاملات IndexedDB را ساده کنند.
اغلب، ترکیبی از هر دو مفید است: Cache API برای محتوای خام دانلود شده، و IndexedDB برای مدیریت فرادادهها، وضعیتهای دانلود و لیستی از تمام واکشیها.
پیامدهای امنیتی
همانند تمام API های قدرتمند وب، امنیت از اهمیت بالایی برخوردار است:
- فقط HTTPS: سرویس ورکرها، و به تبع آن API واکشی پسزمینه، به یک زمینه امن (HTTPS) نیاز دارند. این امر یکپارچگی دادهها را تضمین میکند و از حملات مرد میانی جلوگیری میکند.
- سیاست همان مبدأ: در حالی که میتوانید منابع را از مبدأهای مختلف واکشی کنید، خود سرویس ورکر در محدودیتهای سیاست همان مبدأ وبسایت شما عمل میکند. در مورد محتوایی که دانلود میکنید و نحوه مدیریت آن محتاط باشید.
- اعتبارسنجی محتوا: همیشه محتوای دانلود شده را، به خصوص اگر توسط کاربر ایجاد شده یا از منابع نامعتبر آمده است، قبل از پردازش یا نمایش، اعتبارسنجی کنید.
سازگاری مرورگر و جایگزینها
API واکشی پسزمینه یک ویژگی نسبتاً جدید و قدرتمند است. از اواخر سال ۲۰۲۳ / اوایل ۲۰۲۴، عمدتاً در مرورگرهای مبتنی بر کرومیوم (کروم، اج، اپرا، سامسونگ اینترنت) به خوبی پشتیبانی میشود. فایرفاکس و سافاری هنوز آن را پیادهسازی نکردهاند یا در حال بررسی آن هستند. برای یک مخاطب جهانی، پیادهسازی جایگزینهای قوی بسیار مهم است:
- تشخیص ویژگی: همیشه قبل از تلاش برای استفاده از API، `'serviceWorker' in navigator` و `'BackgroundFetchManager' in window` را بررسی کنید.
- دانلودهای سنتی: اگر واکشی پسزمینه پشتیبانی نمیشود، به شروع یک دانلود مرورگر استاندارد بازگردید (به عنوان مثال، با ایجاد یک تگ `<a>` با ویژگی `download` و فعال کردن یک کلیک). به کاربر اطلاع دهید که باید تب را باز نگه دارد.
- بهبود تدریجی: برنامه خود را طوری طراحی کنید که عملکرد اصلی بدون واکشی پسزمینه کار کند و API صرفاً تجربه را برای مرورگرهای پشتیبانی شده بهبود بخشد.
تست و اشکالزدایی
اشکالزدایی سرویس ورکرها و فرآیندهای پسزمینه میتواند چالشبرانگیز باشد. از ابزارهای توسعهدهنده مرورگر استفاده کنید:
- Chrome DevTools: تب «Application» بخشهایی برای سرویس ورکرها (نظارت بر ثبتنام، شروع/توقف، ارسال رویدادها)، ذخیرهسازی کش و IndexedDB فراهم میکند. واکشیهای پسزمینه نیز تحت یک بخش اختصاصی «Background Services» یا «Application» قابل مشاهده هستند (اغلب زیر «Background fetches» قرار دارند).
- ثبت وقایع: استفاده گسترده از دستورات `console.log` هم در رشته اصلی و هم در سرویس ورکر برای درک جریان رویدادها ضروری است.
- شبیهسازی رویدادها: برخی از ابزارهای توسعهدهنده مرورگر به شما امکان میدهند رویدادهای سرویس ورکر (مانند 'sync' یا 'push') را به صورت دستی فعال کنید که میتواند برای آزمایش منطق پسزمینه مفید باشد، اگرچه شبیهسازی مستقیم رویدادهای واکشی پسزمینه ممکن است محدود باشد و معمولاً به فعالیت واقعی شبکه متکی است.
چشمانداز آینده و فناوریهای مرتبط
API واکشی پسزمینه بخشی از یک تلاش گستردهتر برای آوردن قابلیتهای قدرتمندتر به پلتفرم وب است که اغلب تحت ابتکاراتی مانند پروژه فوگو (Project Fugu) (یا «پروژه قابلیتها») گروهبندی میشود. این پروژه با هدف پر کردن شکاف بین برنامههای وب و برنامههای بومی از طریق در معرض قرار دادن بیشتر سختافزار دستگاه و ویژگیهای سیستم عامل به وب به روشی امن و حافظ حریم خصوصی است. با تکامل وب، میتوانیم انتظار API های بیشتری از این قبیل را داشته باشیم که قابلیتهای آفلاین، یکپارچگی سیستم و عملکرد را افزایش میدهند.
قابلیتهای وب و پروژه فوگو
API واکشی پسزمینه یک نمونه بارز از یک قابلیت وب است که مرزهای کاری که برنامههای وب میتوانند انجام دهند را جابجا میکند. سایر API های مرتبط تحت پروژه فوگو که تجربه کاربری و قابلیتهای آفلاین را افزایش میدهند عبارتند از:
- همگامسازی پسزمینه دورهای: برای همگامسازی منظم مقادیر کمی از دادهها.
- API اشتراکگذاری وب: برای به اشتراک گذاشتن محتوا با سایر برنامهها در دستگاه.
- API دسترسی به سیستم فایل: برای تعامل مستقیمتر با سیستم فایل محلی کاربر (با اجازه صریح کاربر).
- API نشانگذاری (Badging API): برای نشان دادن تعداد خوانده نشدهها یا وضعیت روی آیکونهای برنامه.
این API ها در مجموع با هدف توانمندسازی توسعهدهندگان برای ساخت برنامههای وب هستند که از نظر عملکرد و تجربه کاربری از برنامههای بومی قابل تشخیص نیستند، که یک پیروزی قابل توجه برای مخاطبان جهانی با ترجیحات و قابلیتهای دستگاهی متنوع است.
ادغام با Workbox
برای بسیاری از توسعهدهندگان، کار مستقیم با API های سرویس ورکر میتواند پیچیده باشد. کتابخانههایی مانند Workbox الگوهای رایج سرویس ورکر را، از جمله استراتژیهای کش و همگامسازی پسزمینه، ساده میکنند. در حالی که Workbox هنوز ماژول مستقیمی به طور خاص برای واکشی پسزمینه ندارد، یک پایه قوی برای مدیریت سرویس ورکر شما فراهم میکند و میتواند در کنار پیادهسازی سفارشی واکشی پسزمینه شما استفاده شود. با بلوغ API، ممکن است شاهد ادغام نزدیکتر با چنین کتابخانههایی باشیم.
مقایسه با سایر API ها (Fetch، XHR، Streams)
مهم است که بفهمیم واکشی پسزمینه در مقایسه با سایر API های شبکه در کجا قرار میگیرد:
- `fetch()` و XHR استاندارد: اینها برای درخواستهای کوتاهمدت، همزمان (یا مبتنی بر پرامیس ناهمزمان) هستند که به تب فعال مرورگر وابسته هستند. آنها برای اکثر واکشیهای داده مناسب هستند اما اگر تب بسته شود یا شبکه قطع شود، با شکست مواجه میشوند. واکشی پسزمینه برای وظایف پایدار و طولانیمدت است.
- Streams API: برای پردازش پاسخهای بزرگ به صورت تکه تکه مفید است، که میتواند با `fetch()` یا واکشی پسزمینه ترکیب شود. به عنوان مثال، یک رویداد `backgroundfetchsuccess` میتواند یک پاسخ را بازیابی کند و سپس از استریمهای قابل خواندن برای پردازش تدریجی محتوای دانلود شده استفاده کند، به جای اینکه منتظر بماند تا کل blob در حافظه باشد. این به ویژه برای فایلهای بسیار بزرگ یا پردازش در زمان واقعی مفید است.
واکشی پسزمینه این API ها را با فراهم کردن مکانیزم زیربنایی برای انتقال قابل اعتماد در پسزمینه تکمیل میکند، در حالی که `fetch()` (یا XHR) ممکن است برای تعاملات کوچکتر و پیشزمینه استفاده شود، و استریمها میتوانند برای پردازش کارآمد دادههای به دست آمده از هر دو استفاده شوند. تمایز کلیدی ماهیت «پسزمینه» و «پایدار» واکشی پسزمینه است.
نتیجهگیری: توانمندسازی دانلودهای قوی فرانتاند
API واکشی پسزمینه فرانتاند یک جهش قابل توجه به جلو در توسعه وب را نشان میدهد و اساساً نحوه مدیریت فایلهای بزرگ در سمت کلاینت را تغییر میدهد. با فعال کردن دانلودهای واقعاً پایدار و قابل اعتماد که میتوانند از بسته شدن تبها و قطعیهای شبکه جان سالم به در ببرند، به توسعهدهندگان این امکان را میدهد تا برنامههای وب پیشروندهای بسازند که تجربهای شبیه به برنامههای بومی ارائه میدهند. این فقط یک بهبود فنی نیست؛ بلکه یک توانمندساز حیاتی برای مخاطبان جهانی است که بسیاری از آنها به اتصالات اینترنتی متناوب یا کمتر قابل اعتماد متکی هستند.
از مصرف رسانه آفلاین یکپارچه در بازارهای نوظهور گرفته تا همگامسازی دادههای سازمانی قوی در سراسر قارهها، واکشی پسزمینه راه را برای یک وب مقاومتر و کاربرپسندتر هموار میکند. در حالی که نیاز به پیادهسازی دقیق دارد، به ویژه در مورد مدیریت خطا، بازخورد کاربر و مدیریت ذخیرهسازی، مزایای آن از نظر بهبود تجربه کاربری و قابلیت اطمینان برنامه بسیار زیاد است. با ادامه گسترش پشتیبانی مرورگر، ادغام API واکشی پسزمینه در برنامههای وب شما به یک استراتژی ضروری برای ارائه تجربیات دیجیتال در سطح جهانی به کاربران در همه جا تبدیل خواهد شد.