توسعه فرانت‌اند

Service Workers: آفلاین و کش پیشرفته

دامنه درخواست، نسخه‌بندی و تازگی، تفاوت خطای شبکه و HTTP، دو فایل کامل Network First با timeout و fallback و مرز داده خصوصی.

1404/12/29 4 دقیقه مطالعه 1,029 بازدید

صفحه از cache سریع نمایش داده می‌شود، اما قیمت آن مربوط به مراجعه قبلی است. در صفحه دیگری، برنامه آن‌قدر منتظر شبکه می‌ماند که نسخه محلی عملاً به کار نمی‌آید. طراحی cache از انتخاب نام یک strategy کامل نمی‌شود. باید نوع داده، حد انتظار، زمان اعتبار و نتیجه شکست معلوم باشند. Service Worker امکان کنترل بخشی از دریافت را می‌دهد؛ حقیقت داده و قرارداد محصول را خودبه‌خود تعیین نمی‌کند.

برای هر درخواست بنویسید چه چیزی می‌تواند قدیمی باشد و چه چیزی باید تازه تأیید شود. فایل عمومی دارای نام نسخه‌دار با اطلاعات خصوصی حساب شرایط یکسان ندارد. عملیات POST و پرداخت نیز نباید مانند تصویر عمومی وارد cache شوند. تقسیم درخواست‌ها به گروه‌های محدود و قابل توضیح، از یک handler سراسری که هر چیزی را ذخیره می‌کند قابل نگهداری‌تر است.

چرخه عمر بخشی از سازگاری است

راهنمای استفاده از Service Workers ثبت، حوزه و مسیر install و activate را توضیح می‌دهد. Worker در زمینه جدا اجرا می‌شود و دسترسی مستقیم به DOM ندارد. secure context لازم است و localhost برای توسعه مناسب است. اولین دریافت صفحه لزوماً زیر کنترل worker ثبت‌شده در همان مراجعه نیست؛ ثبت و کنترل را در پیام رابط از هم جدا کنید.

راهنمای چرخه عمر در web.dev هدف انتظار نسخه تازه و هماهنگی clientها را شرح می‌دهد. نسخه جدید می‌تواند نصب شده اما هنوز فعال نباشد. افزودن skipWaiting به همه پروژه‌ها تصمیم بی‌اثر نیست؛ صفحه باز ممکن است با فایل‌ها یا schema قبلی کار کند. update باید با سازگاری نسخه و کار جاری مخاطب هماهنگ شود تا تغییر بی‌خبر، اطلاعات یا مسیر عمل را خراب نکند.

نام strategy قرارداد داده را جایگزین نمی‌کند

مستندات Workbox Strategies راه‌های Cache First، Network First و Stale-While-Revalidate را توضیح می‌دهد. هرکدام trade-off دارند. Cache First برای منبع ثابت می‌تواند مناسب باشد ولی به نسخه و حذف نیاز دارد. Network First تازگی را ترجیح می‌دهد اما بدون حد انتظار، fallback دیر می‌رسد. Stale-While-Revalidate نسخه قبلی را سریع می‌دهد و تازه‌سازی جدا دارد؛ مخاطب باید محدودیت نسخه قبلی را بفهمد.

Cache Only و Network Only نیز برای دامنه‌های مشخص می‌توانند معنی داشته باشند. آن‌ها را با آنلاین یا آفلاین بودن کلی دستگاه یکی ندانید. navigator.onLine فقط نشانه‌ای محدود است و موفقیت درخواست خاص را تضمین نمی‌کند. برنامه باید نتیجه واقعی fetch را مدیریت کند. اگر سرویس پاسخ خطا داده، همان نتیجه با شکست دریافت شبکه متفاوت است و نباید به‌طور خودکار پشت نسخه موفق قدیمی پنهان شود.

نمونه Network First با حد انتظار

دو فایل بعدی را به نام index.html و sw.js در پوشه آموزشی تازه قرار دهید. هیچ تصویر، فونت، manifest یا API بیرونی لازم نیست. این نمونه درباره مدیریت صفحه عمومی است و نصب PWA یا Push را پیاده نمی‌کند. برای سرو محلی، npm init -y و npm install -D http-server را اجرا کنید، سپس npx http-server . -p 8080 -c-1. Node.js سازگار مانند نسخه 24 پیش‌نیاز ابزار است.

صفحه را از http://localhost:8080/index.html مشاهده کنید. نخستین مراجعه فایل‌ها را دریافت و worker را ثبت می‌کند. پس از فعال شدن و کنترل مراجعه بعدی، فقط navigation دقیق همان index بدون query مدیریت می‌شود. دیگر URLها و methodها دست‌کاری نمی‌شوند. حد دو ثانیه یک مقدار آموزشی برای انتظار پاسخ اولیه شبکه است، نه توصیه ثابت همه سرویس‌ها و نه تضمین پایان دانلود کل بدنه.

<!doctype html><html lang="fa" dir="rtl"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>صفحه عمومی نمونه cache</title><style>body{font-family:Tahoma,Arial,sans-serif;line-height:1.9;background:#f2f4f7;color:#17243b;margin:0}main{width:min(38rem,calc(100% - 2rem));margin:auto;padding-block:2rem}</style></head><body><main><h1>محتوای عمومی آموزشی</h1><p>این صفحه ممکن است از نسخه ذخیره‌شده دیده شود و اطلاعات متغیر یا خصوصی ندارد.</p><p id="control" role="status">در حال بررسی کنترل صفحه.</p></main><script>const control=document.getElementById('control');function report(){control.textContent=navigator.serviceWorker.controller?'این صفحه زیر کنترل Service Worker است.':'این مراجعه هنوز زیر کنترل Service Worker نیست.';}if('serviceWorker' in navigator){navigator.serviceWorker.addEventListener('controllerchange',report);navigator.serviceWorker.register('./sw.js').then(report).catch(()=>{control.textContent='ثبت Service Worker انجام نشد.';});report();}else{control.textContent='این محیط Service Worker ارائه نمی‌کند.';}</script></body></html>

پیام صفحه می‌گوید آیا client زیر کنترل worker است. این خبر با در دسترس بودن همه فایل‌ها یا موفقیت شبکه یکسان نیست. متن اصلی ثابت و عمومی است و هشدار می‌دهد ممکن است از نسخه ذخیره‌شده دیده شود. در محصول دارای داده متغیر، زمان و اعتبار اطلاعات نیاز به قرارداد دقیق‌تر دارد. این نمونه برای فهم مسیر پاسخ است و درباره نتیجه عملیات حساس ادعایی نمی‌کند.

const PREFIX = 'tara-network-deadline-demo-';
const CACHE = PREFIX + 'v1';
const INDEX = new URL('./index.html', self.location.href).href;
const fallbackHtml = '<!doctype html><html lang="fa" dir="rtl"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>محتوا در دسترس نیست</title></head><body><main><h1>نسخه محلی در دسترس نیست</h1><p>دریافت شبکه انجام نشد. اتصال را بررسی و دوباره مراجعه کنید.</p></main></body></html>';
self.addEventListener('install', event => event.waitUntil(caches.open(CACHE).then(cache => cache.add(INDEX))));
self.addEventListener('activate', event => event.waitUntil(caches.keys().then(keys => Promise.all(keys.filter(key => key.startsWith(PREFIX) && key !== CACHE).map(key => caches.delete(key))))));
async function fetchWithDeadline(request) {
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), 2000);
  try { return await fetch(request, { signal: controller.signal }); }
  finally { clearTimeout(timer); }
}
self.addEventListener('fetch', event => {
  if (event.request.method !== 'GET' || event.request.mode !== 'navigate' || event.request.url !== INDEX) return;
  event.respondWith((async () => {
    try {
      const response = await fetchWithDeadline(event.request);
      if (response.ok) {
        const copy = response.clone();
        event.waitUntil(caches.open(CACHE).then(cache => cache.put(INDEX, copy)).catch(() => {}));
      }
      return response;
    } catch {
      const cached = await caches.open(CACHE).then(cache => cache.match(INDEX)).catch(() => undefined);
      return cached || new Response(fallbackHtml, { status: 503, headers: { 'Content-Type': 'text/html; charset=utf-8' } });
    }
  })());
});

هنگام install فقط index ذخیره می‌شود. handler با AbortController انتظار پاسخ اولیه را محدود می‌کند. اگر fetch پاسخ HTTP بدهد، همان پاسخ برمی‌گردد؛ تنها پاسخ موفق برای ذخیره تازه استفاده می‌شود. شکست نوشتن cache مسیر پاسخ موفق شبکه را خراب نمی‌کند. اگر دریافت شکست بخورد یا به حد انتظار برسد، نسخه cache خوانده می‌شود و در نبود آن، صفحه خطای کامل 503 داده می‌شود.

پاسخ و ذخیره دو موفقیت متفاوت‌اند

مرجع Cache.put قرارداد ذخیره Request و Response را توضیح می‌دهد. برای حفظ پاسخ مصرف‌کننده، نسخه clone به cache داده می‌شود. خطای ذخیره مانند پر شدن ظرفیت نباید بی‌دلیل پاسخ سالم شبکه را به شکست تبدیل کند. در نمونه، Promise مربوط به ذخیره با waitUntil دنبال و شکست آن مدیریت می‌شود. این کار تضمین عمر نامحدود worker ایجاد نمی‌کند.

درخواست شبکه هم جریان و منابع خودش را دارد. مرجع AbortController ابزار لغو عملیات پشتیبانی‌شده را شرح می‌دهد. حد انتظار باید با نیاز داده و رفتار سرویس انتخاب شود. لغو دریافت در client به معنی برگشت دادن اثر یک عملیات سمت سرور نیست. نمونه فقط GET عمومی دارد؛ تعمیم همین رفتار به ثبت سفارش یا تغییر داده مناسب نیست.

تازگی و cache key

یک URL با پارامترهای متفاوت می‌تواند نتیجه متفاوتی داشته باشد. حذف بی‌دلیل query هنگام ساخت key ممکن است فیلتر یا حساب را مخلوط کند. در نمونه، query اصلاً به مسیر cache وارد نمی‌شود تا دامنه روشن بماند. در برنامه واقعی، نام و مقدار پارامترها، headerهای مرتبط و سیاست پاسخ باید با هویت منبع هماهنگ باشند. cache key بخشی از مدل داده است و فقط جزئیات ابزار محسوب نمی‌شود.

برای داده دارای انقضا، ذخیره یک timestamp باید با بررسی هنگام مصرف همراه باشد. مقدار TTL بدون مسیر بررسی، خودکار داده را حذف نمی‌کند. اگر نسخه قدیمی هنوز نمایش داده می‌شود، مخاطب باید محدودیت را بداند. راهنمای cache در Apollo نمونه دیگری از رابطه هویت و تازگی دارد، ولی cache شبکه Service Worker و cache موجودیت client مسئولیت و عمر یکسانی ندارند.

پاک‌سازی محدود و نسخه‌بندی

activate نمونه فقط cacheهایی با پیشوند همین برنامه و نسخه متفاوت را حذف می‌کند. پاک کردن تمام cacheهای origin می‌تواند بخش دیگری را خراب کند. برای پروژه مشترک، پیشوند، scope و مالکیت باید ثبت شوند. تغییر فایل‌ها و worker نیاز به قرارداد انتشار دارد. اگر client قدیمی هنوز اجرا می‌شود، حذف منبعی که او به آن نیاز دارد می‌تواند مسیر جاری را قطع کند.

مرجع skipWaiting نحوه گذر از انتظار را توضیح می‌دهد. استفاده از آن باید با سناریوی update هماهنگ باشد؛ افزودن خودکار آن به boilerplate تضمین تجربه بهتر نیست. می‌توان خبر نسخه تازه و زمان مناسب تغییر را طراحی کرد. راهنمای اعلان‌ها درباره پیام پایدار و اختیار ادامه کار کمک می‌کند. update نباید خبر مبهمی باشد که داده واردشده را بی‌خبر از بین ببرد.

داده خصوصی و ظرفیت

صفحه خصوصی حساب نباید با سیاست عمومی index نمونه ذخیره شود. logout، تغییر کاربر و دستگاه مشترک نیاز به پاک‌سازی و مرز داده دارند. هدرها و تنظیم سرور هم باید در تصمیم cache دیده شوند. یک handler عمومی که هر response موفق را نگه می‌دارد ممکن است اطلاعات نامناسب را ذخیره کند. دامنه را از ابتدا محدود کنید و داده‌ای را که برای ادامه آفلاین لازم نیست بی‌دلیل نگه ندارید.

مرورگر می‌تواند داده محلی را محدود یا حذف کند. cache ذخیره دائمی تضمین‌شده نیست و نباید تنها نسخه اطلاعات ضروری باشد. راهنمای storage مرورگر درباره ظرفیت و eviction توضیح دارد. برای مجموعه بزرگ، تعداد و عمر entryها باید سیاست داشته باشد. در نمونه فقط یک منبع ذخیره می‌شود؛ این محدودیت آموزشی جای الگوریتم مدیریت مجموعه واقعی را نمی‌گیرد.

پس‌زمینه و عملیات تکراری

مرجع Background Sync امکان و محدودیت همگام‌سازی پس‌زمینه را شرح می‌دهد. پشتیبانی و زمان اجرای آن به محیط وابسته است. Worker daemon همیشه روشن نیست و نباید دریافت قطعی یک عملیات را از ثبت در صف محلی نتیجه گرفت. برای ارسال دوباره، شناسه، ترتیب و جلوگیری از اثر تکراری لازم است. نتیجه نهایی باید از سرویس معتبر تأیید شود.

Push نیز به مجوز و مسیر ارسال نیاز دارد و cache کردن HTML آن را فعال نمی‌کند. برای شرایط محیط، راهنمای PWA در Safari تفاوت قابلیت‌ها را توضیح می‌دهد. نمونه آموزشی حاضر فقط دریافت یک صفحه عمومی را مدیریت می‌کند. قابلیت‌های دیگر باید با fallback و پیام واقع‌بینانه اضافه شوند تا عدم پشتیبانی، مسیر اصلی وب را بی‌دلیل متوقف نکند.

قرارداد قابل پیگیری

برای هر گروه درخواست، method، دامنه، key، حد انتظار و سیاست تازگی را ثبت کنید. مشخص کنید در پاسخ HTTP ناموفق، شکست شبکه و خطای cache چه نتیجه‌ای ارائه می‌شود. نمونه پایه PWA برای شروع چرخه ثبت مکمل است. Workbox می‌تواند بخشی از این قواعد را آماده کند، اما بهترین بودن آن برای هر پروژه ادعای لازم یا دقیقی نیست.

Service Worker کنترل بیشتری بر دریافت می‌دهد و همین کنترل به قرارداد دقیق‌تر نیاز دارد. نسخه‌بندی، ظرفیت و امنیت بخشی از طراحی‌اند، نه کارهای اختیاری پس از ساخت. از دامنه کوچک و روشن شروع کنید و هر strategy را به نیاز داده وصل کنید. پاسخ سریع وقتی مفید است که مخاطب بداند اطلاعات چه اعتباری دارد و در شکست یا update بتواند مسیر کار را قابل اعتماد ادامه دهد.

واکنش به این مقاله

اشتراک‌گذاری این مقاله:
دیدگاه‌ها 0
۰ / ۲۰۰۰

هنوز دیدگاهی ثبت نشده

اولین نفری باشید که نظر می‌دهد!