استراتژی‌هایی برای ذخیره‌سازی کش توسط کارگران خدماتی، استراتژی‌هایی برای ذخیره‌سازی در حافظه پنهان کارکنان خدمات

تا به حال، تنها اشاره‌ها و کدهای کوچکی از رابط Cache وجود داشته است. برای استفاده مؤثر از سرویس‌کارها، لازم است یک یا چند استراتژی ذخیره‌سازی را اتخاذ کنید که نیاز به کمی آشنایی با رابط Cache دارد.

استراتژی ذخیره سازی یک تعامل بین رویداد fetch یک سرویس دهنده و رابط Cache است. نحوه نگارش یک استراتژی کش بستگی دارد. برای مثال، ممکن است ترجیح داده شود که درخواست‌ها برای دارایی‌های استاتیک متفاوت از اسناد رسیدگی شود، و این بر نحوه ترکیب استراتژی ذخیره‌سازی تأثیر می‌گذارد.

قبل از اینکه به خود استراتژی‌ها بپردازیم، اجازه دهید لحظه‌ای در مورد اینکه رابط Cache نیست، چیست و خلاصه‌ای از روش‌هایی که برای مدیریت کش‌های سرویس‌کار ارائه می‌دهد صحبت کنیم.

رابط Cache در مقابل کش HTTP

اگر قبلاً با رابط Cache کار نکرده اید، ممکن است وسوسه انگیز باشد که آن را مانند حافظه پنهان HTTP یا حداقل مرتبط با آن در نظر بگیرید. این مورد نیست.

  • رابط Cache یک مکانیسم کش است که کاملاً جدا از کش HTTP است.
  • از هر پیکربندی Cache-Control که برای تأثیرگذاری بر حافظه نهان HTTP استفاده می‌کنید، تأثیری روی دارایی‌هایی که در واسط Cache ذخیره می‌شوند، ندارد.

این کمک می کند که حافظه پنهان مرورگر را لایه لایه در نظر بگیرید. حافظه پنهان HTTP یک حافظه پنهان سطح پایین است که توسط جفت‌های کلید-مقدار با دستورالعمل‌های بیان شده در هدرهای HTTP هدایت می‌شود.

در مقابل، رابط Cache یک کش سطح بالا است که توسط یک JavaScript API هدایت می شود. این انعطاف پذیری بیشتری نسبت به استفاده از جفت های کلید-مقدار HTTP نسبتاً ساده ارائه می دهد و نیمی از چیزی است که استراتژی های کش را ممکن می کند. برخی از روش‌های مهم API در مورد کش‌های سرویس‌کار عبارتند از:

  • CacheStorage.open برای ایجاد یک نمونه Cache جدید.
  • Cache.add و Cache.put برای ذخیره پاسخ های شبکه در کش سرویس کارمند.
  • Cache.match برای یافتن پاسخ ذخیره شده در حافظه پنهان در یک نمونه Cache .
  • Cache.delete برای حذف یک پاسخ ذخیره شده از یک نمونه Cache .

اینها فقط تعدادی هستند. روش‌های مفید دیگری نیز وجود دارد، اما این‌ها روش‌های اساسی هستند که در ادامه در این راهنما خواهید دید.

رویداد fetch فروتن

نیمه دیگر استراتژی ذخیره سازی، رویداد fetch کارگر سرویس است. تاکنون در این مستندات، مقداری در مورد «شنود درخواست‌های شبکه» شنیده‌اید، و رویداد fetch در داخل یک سرویس‌کار جایی است که این اتفاق می‌افتد:

// Establish a cache name
const cacheName = 'MyFancyCacheName_v1';

self.addEventListener('install', (event) => {
  event.waitUntil(caches.open(cacheName));
});

self.addEventListener('fetch', async (event) => {
  // Is this a request for an image?
  if (event.request.destination === 'image') {
    // Open the cache
    event.respondWith(caches.open(cacheName).then((cache) => {
      // Respond with the image from the cache or from the network
      return cache.match(event.request).then((cachedResponse) => {
        return cachedResponse || fetch(event.request.url).then((fetchedResponse) => {
          // Add the network response to the cache for future visits.
          // Note: we need to make a copy of the response to save it in
          // the cache and use the original as the request response.
          cache.put(event.request, fetchedResponse.clone());

          // Return the network response
          return fetchedResponse;
        });
      });
    }));
  } else {
    return;
  }
});

این یک نمونه اسباب‌بازی است - و شما می‌توانید آن را در عمل ببینید - اما نمونه‌ای است که نگاهی اجمالی به کارهایی که کارگران خدمات می‌توانند انجام دهند را ارائه می‌دهد. کد بالا کارهای زیر را انجام می دهد:

  1. ویژگی destination درخواست را بررسی کنید تا ببینید آیا این یک درخواست تصویری است یا خیر.
  2. اگر تصویر در کش سرویس کارگر است، آن را از آنجا سرو کنید. اگر نه، تصویر را از شبکه واکشی کنید، پاسخ را در حافظه پنهان ذخیره کنید و پاسخ شبکه را برگردانید.
  3. تمام درخواست‌های دیگر بدون هیچ تعاملی با حافظه پنهان از طریق سرویس‌کار ارسال می‌شوند.

شی event واکشی حاوی یک ویژگی request است که چند بیت اطلاعات مفید برای کمک به شناسایی نوع هر درخواست دارد:

  • url ، که URL درخواست شبکه است که در حال حاضر توسط رویداد fetch مدیریت می شود.
  • method ، که روش درخواست است (به عنوان مثال، GET یا POST ).
  • mode ، که حالت درخواست را توصیف می کند. مقدار 'navigate' اغلب برای تشخیص درخواست اسناد HTML از سایر درخواست ها استفاده می شود.
  • destination ، که نوع محتوای درخواست شده را به گونه ای توصیف می کند که از استفاده از پسوند فایل دارایی درخواستی جلوگیری می کند.

یک بار دیگر، نام بازی Asynchrony است. به خاطر می آورید که رویداد install یک روش event.waitUntil ارائه می دهد که یک قول را می گیرد و قبل از ادامه فعال سازی منتظر می ماند تا حل شود. رویداد fetch روش event.respondWith مشابهی را ارائه می‌کند که می‌توانید از آن برای بازگرداندن نتیجه درخواست fetch ناهمزمان یا پاسخی که توسط روش match رابط Cache بازگردانده شده است استفاده کنید.

استراتژی های ذخیره سازی

اکنون که کمی با نمونه های Cache و کنترل کننده رویداد fetch آشنایی دارید، آماده هستید تا به برخی از استراتژی های ذخیره سازی کش توسط سرویس دهنده بپردازید. در حالی که احتمالات عملا بی پایان هستند، این راهنما با استراتژی هایی که با Workbox ارائه می شود، همراه است، بنابراین می توانید درک کنید که در قسمت داخلی Workbox چه می گذرد.

فقط کش

جریان را از صفحه، به سرویس دهنده، به حافظه پنهان نشان می دهد.

بیایید با یک استراتژی ذخیره سازی ساده شروع کنیم که آن را «فقط کش» می نامیم. فقط این است که: وقتی سرویس‌کار صفحه را کنترل می‌کند، درخواست‌های تطبیق فقط به حافظه پنهان می‌روند. این بدان معناست که هر دارایی ذخیره شده در حافظه پنهان باید از پیش ذخیره شود تا بتواند الگو را کار کند و این دارایی ها هرگز در حافظه پنهان به روز نمی شوند تا زمانی که سرویس دهنده به روز شود.

// Establish a cache name
const cacheName = 'MyFancyCacheName_v1';

// Assets to precache
const precachedAssets = [
  '/possum1.jpg',
  '/possum2.jpg',
  '/possum3.jpg',
  '/possum4.jpg'
];

self.addEventListener('install', (event) => {
  // Precache assets on install
  event.waitUntil(caches.open(cacheName).then((cache) => {
    return cache.addAll(precachedAssets);
  }));
});

self.addEventListener('fetch', (event) => {
  // Is this one of our precached assets?
  const url = new URL(event.request.url);
  const isPrecachedRequest = precachedAssets.includes(url.pathname);

  if (isPrecachedRequest) {
    // Grab the precached asset from the cache
    event.respondWith(caches.open(cacheName).then((cache) => {
      return cache.match(event.request.url);
    }));
  } else {
    // Go to the network
    return;
  }
});

در بالا، آرایه ای از دارایی ها در زمان نصب از قبل ذخیره می شوند. هنگامی که سرویس‌کار واکشی‌ها را مدیریت می‌کند، بررسی می‌کنیم که آیا URL درخواستی که توسط رویداد fetch انجام می‌شود در آرایه دارایی‌های از پیش ذخیره‌شده قرار دارد یا خیر. اگر چنین است، منبع را از حافظه پنهان می گیریم و از شبکه می گذریم. سایر درخواست‌ها به شبکه و فقط شبکه منتقل می‌شوند. برای مشاهده عملی این استراتژی، این نسخه نمایشی را با کنسول خود باز کنید .

فقط شبکه

جریان را از صفحه، به کارگر سرویس، به شبکه نشان می دهد.

نقطه مقابل "تنها حافظه پنهان" "فقط شبکه" است، که در آن یک درخواست از طریق یک سرویس دهنده بدون هیچ گونه تعاملی با کش سرویس دهنده به شبکه ارسال می شود. این یک استراتژی خوب برای حصول اطمینان از تازگی محتوا است (مارک آپ فکری)، اما نتیجه آن این است که وقتی کاربر آفلاین است هرگز کار نخواهد کرد.

اطمینان از ارسال درخواست به شبکه فقط به این معنی است که event.respondWith برای یک درخواست منطبق تماس نمی‌گیرید. اگر می‌خواهید صریح باشید، می‌توانید یک return; در fetch رویداد پاسخ به تماس شما برای درخواست هایی که می خواهید به شبکه ارسال کنید. این چیزی است که در نسخه ی نمایشی استراتژی "Cache Only" برای درخواست هایی که از قبل ذخیره نشده اند اتفاق می افتد.

ابتدا حافظه پنهان، بازگشت به شبکه

جریان را از صفحه، به سرویس دهنده، به حافظه پنهان و سپس به شبکه را نشان می دهد، اگر در حافظه پنهان نباشد.

این استراتژی جایی است که همه چیز کمی بیشتر درگیر می شود. برای تطبیق درخواست ها، روند به این صورت است:

  1. درخواست به حافظه پنهان برخورد می کند. اگر دارایی در حافظه پنهان است، آن را از آنجا سرو کنید.
  2. اگر درخواست در کش نیست ، به شبکه بروید.
  3. پس از اتمام درخواست شبکه، آن را به حافظه پنهان اضافه کنید، سپس پاسخ را از شبکه برگردانید.

در اینجا یک نمونه از این استراتژی است که می توانید آن را در یک نسخه آزمایشی زنده آزمایش کنید:

// Establish a cache name
const cacheName = 'MyFancyCacheName_v1';

self.addEventListener('fetch', (event) => {
  // Check if this is a request for an image
  if (event.request.destination === 'image') {
    event.respondWith(caches.open(cacheName).then((cache) => {
      // Go to the cache first
      return cache.match(event.request.url).then((cachedResponse) => {
        // Return a cached response if we have one
        if (cachedResponse) {
          return cachedResponse;
        }

        // Otherwise, hit the network
        return fetch(event.request).then((fetchedResponse) => {
          // Add the network response to the cache for later visits
          cache.put(event.request, fetchedResponse.clone());

          // Return the network response
          return fetchedResponse;
        });
      });
    }));
  } else {
    return;
  }
});

اگرچه این مثال فقط تصاویر را پوشش می‌دهد، این یک استراتژی عالی برای اعمال بر روی تمام دارایی‌های ثابت (مانند CSS، جاوا اسکریپت، تصاویر و فونت‌ها) به‌ویژه موارد دارای نسخه هش است. با کنار گذاشتن هر گونه بررسی تازگی محتوا با سروری که حافظه پنهان HTTP ممکن است راه اندازی شود، سرعت دارایی های تغییرناپذیر را افزایش می دهد. مهمتر از آن، هر دارایی ذخیره شده به صورت آفلاین در دسترس خواهد بود.

ابتدا شبکه، بازگشت به حافظه پنهان

جریان را از صفحه، به سرویس‌دهنده، به شبکه و سپس در صورت در دسترس نبودن شبکه به حافظه پنهان نشان می‌دهد.

اگر بخواهید «اول حافظه پنهان، دوم شبکه» را روی سر آن بچرخانید، در نهایت با استراتژی «اول شبکه، دوم حافظه پنهان» مواجه می شوید که به نظر می رسد:

  1. ابتدا برای درخواست به شبکه می روید و پاسخ را در حافظه پنهان قرار می دهید.
  2. اگر بعداً آفلاین باشید، به آخرین نسخه آن پاسخ در حافظه پنهان باز می‌گردید.

این استراتژی برای درخواست‌های HTML یا API زمانی عالی است که وقتی آنلاین هستید، آخرین نسخه یک منبع را می‌خواهید، اما می‌خواهید به آخرین نسخه موجود دسترسی آفلاین بدهید. زمانی که در درخواست‌های HTML اعمال می‌شود، به این صورت است:

// Establish a cache name
const cacheName = 'MyFancyCacheName_v1';

self.addEventListener('fetch', (event) => {
  // Check if this is a navigation request
  if (event.request.mode === 'navigate') {
    // Open the cache
    event.respondWith(caches.open(cacheName).then((cache) => {
      // Go to the network first
      return fetch(event.request.url).then((fetchedResponse) => {
        cache.put(event.request, fetchedResponse.clone());

        return fetchedResponse;
      }).catch(() => {
        // If the network is unavailable, get
        return cache.match(event.request.url);
      });
    }));
  } else {
    return;
  }
});

می توانید این را در یک نسخه نمایشی امتحان کنید. ابتدا به صفحه بروید. ممکن است لازم باشد قبل از قرار دادن پاسخ HTML در حافظه پنهان، مجدداً بارگیری کنید. سپس در ابزارهای توسعه دهنده خود، یک اتصال آفلاین را شبیه سازی کنید و دوباره بارگیری کنید. آخرین نسخه موجود فوراً از حافظه پنهان ارائه می شود.

در شرایطی که قابلیت آفلاین مهم است، اما باید بین این قابلیت و دسترسی به جدیدترین نسخه کمی از نشانه‌گذاری یا داده‌های API تعادل برقرار کنید، «اول شبکه، دوم حافظه پنهان» یک استراتژی محکم است که به آن هدف می‌رسد.

قدیمی-در حالی که-تأیید مجدد

جریان را از صفحه، به سرویس دهنده، به حافظه پنهان، سپس از شبکه به حافظه پنهان نشان می دهد.

از میان استراتژی‌هایی که تا به حال به آن پرداخته‌ایم، «Stale-while-Revalidate» پیچیده‌ترین است. از برخی جهات شبیه به دو استراتژی آخر است، اما این رویه سرعت دسترسی به یک منبع را در اولویت قرار می دهد، در حالی که آن را در پس زمینه به روز نگه می دارد. این استراتژی چیزی شبیه به این است:

  1. در اولین درخواست برای یک دارایی، آن را از شبکه واکشی کنید، آن را در حافظه پنهان قرار دهید و پاسخ شبکه را برگردانید.
  2. در درخواست‌های بعدی، ابتدا دارایی را از حافظه پنهان ارائه دهید، سپس در پس‌زمینه، آن را مجدداً از شبکه درخواست کنید و ورودی حافظه پنهان دارایی را به‌روزرسانی کنید.
  3. برای درخواست های پس از آن، آخرین نسخه واکشی شده از شبکه را که در مرحله قبل در حافظه پنهان قرار داده شده بود، دریافت خواهید کرد.

این یک استراتژی عالی برای چیزهایی است که برای به روز نگه داشتن آنها بسیار مهم است، اما حیاتی نیستند. به چیزهایی مانند آواتار برای یک سایت رسانه اجتماعی فکر کنید. هنگامی که کاربران به انجام این کار نزدیک می شوند، آنها به روز می شوند، اما آخرین نسخه برای هر درخواستی کاملاً ضروری نیست.

// Establish a cache name
const cacheName = 'MyFancyCacheName_v1';

self.addEventListener('fetch', (event) => {
  if (event.request.destination === 'image') {
    event.respondWith(caches.open(cacheName).then((cache) => {
      return cache.match(event.request).then((cachedResponse) => {
        const fetchedResponse = fetch(event.request).then((networkResponse) => {
          cache.put(event.request, networkResponse.clone());

          return networkResponse;
        });

        return cachedResponse || fetchedResponse;
      });
    }));
  } else {
    return;
  }
});

شما می توانید این را در یک نسخه آزمایشی زنده دیگر مشاهده کنید، به خصوص اگر به برگه شبکه در ابزارهای توسعه دهنده مرورگر خود و نمایشگر CacheStorage آن توجه کنید (اگر ابزارهای توسعه دهنده مرورگر شما چنین ابزاری دارند).

پیش به سمت Workbox!

این سند بررسی ما را در مورد API کارگران سرویس و همچنین APIهای مرتبط به پایان می‌رساند، به این معنی که شما به اندازه کافی در مورد نحوه استفاده مستقیم از کارکنان خدمات برای شروع کار با Workbox یاد گرفته‌اید!