کتاب آشپزی آفلاین

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

برای نمایش عملکرد برخی از این الگوها، آموزش دیده به هیجان و این ویدیو را ببینید که تأثیر عملکرد را نشان می دهد.

Service Worker به شما امکان می دهد درخواست ها را به طور مستقل از حافظه پنهان رسیدگی کنید، بنابراین آنها را جداگانه نشان خواهم داد. اول، کش کردن، چه زمانی باید انجام شود؟

در هنگام نصب - به عنوان یک وابستگی.
در هنگام نصب - به عنوان یک وابستگی.

Service Worker یک رویداد install به شما می دهد. می‌توانید از این برای آماده کردن موارد استفاده کنید، چیزهایی که باید قبل از رسیدگی به رویدادهای دیگر آماده باشند. در حالی که این اتفاق می افتد، هر نسخه قبلی از Service Worker شما هنوز در حال اجرا و ارائه صفحات است، بنابراین کارهایی که در اینجا انجام می دهید نباید این کار را مختل کنند.

ایده‌آل برای: CSS، تصاویر، فونت‌ها، JS، قالب‌ها... اساساً هر چیزی که در آن «نسخه» سایتتان ثابت است.

اینها چیزهایی هستند که اگر واکشی نشدند، سایت شما را کاملاً غیر کاربردی می‌کنند، چیزهایی که یک برنامه مشابه پلتفرم خاص بخشی از دانلود اولیه می‌شود.

self.addEventListener('install', function (event) {
  event
.waitUntil(
    caches
.open('mysite-static-v3').then(function (cache) {
     
return cache.addAll([
       
'/css/whatever-v3.css',
       
'/css/imgs/sprites-v6.png',
       
'/css/fonts/whatever-v8.woff',
       
'/js/all-min-v4.js',
       
// etc.
     
]);
   
}),
 
);
});

event.waitUntil برای تعیین طول و موفقیت نصب وعده می دهد. اگر وعده رد شود، نصب به عنوان یک شکست در نظر گرفته می شود و این Service Worker رها می شود (اگر نسخه قدیمی در حال اجرا باشد، دست نخورده باقی می ماند). caches.open() و cache.addAll() وعده ها را برمی گرداند. اگر هر یک از منابع واکشی نشد، فراخوانی cache.addAll() رد می شود.

در آموزش‌های هیجان‌انگیز، از این برای ذخیره دارایی‌های استاتیک استفاده می‌کنم.

در هنگام نصب - نه به عنوان یک وابستگی

در هنگام نصب - نه به عنوان یک وابستگی.
در هنگام نصب - نه به عنوان یک وابستگی.

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

ایده‌آل برای: منابع بزرگ‌تری که فوراً مورد نیاز نیستند، مانند دارایی‌ها برای سطوح بعدی یک بازی.

self.addEventListener('install', function (event) {
  event
.waitUntil(
    caches
.open('mygame-core-v1').then(function (cache) {
      cache
       
.addAll
       
// levels 11–20
       
();
     
return cache
       
.addAll
       
// core assets and levels 1–10
       
();
   
}),
 
);
});

مثال بالا وعده cache.addAll برای سطوح 11-20 را به event.waitUntil نمی‌دهد، بنابراین حتی اگر شکست بخورد، بازی همچنان به‌صورت آفلاین در دسترس خواهد بود. البته، شما باید به عدم وجود احتمالی این سطوح رسیدگی کنید و در صورت از دست دادن آنها، دوباره سعی کنید آنها را ذخیره کنید.

ممکن است Service Worker هنگام دانلود سطوح 11 تا 20 کشته شود، زیرا مدیریت رویدادها به پایان رسیده است، به این معنی که آنها در حافظه پنهان ذخیره نمی شوند. در آینده Web Periodic Background Synchronization API مواردی از این قبیل و دانلودهای بزرگتر مانند فیلم را مدیریت خواهد کرد. آن API در حال حاضر فقط در فورک‌های Chromium پشتیبانی می‌شود.

در فعال کردن

در فعال کردن
در فعال کردن

ایده آل برای: پاکسازی و مهاجرت.

هنگامی که یک Service Worker جدید نصب شد و از نسخه قبلی استفاده نمی شود، نسخه جدید فعال می شود و شما یک رویداد activate دریافت می کنید. از آنجایی که نسخه قدیمی در دسترس نیست، زمان خوبی برای مدیریت مهاجرت طرحواره در IndexedDB و همچنین حذف کش های استفاده نشده است.

self.addEventListener('activate', function (event) {
  event
.waitUntil(
    caches
.keys().then(function (cacheNames) {
     
return Promise.all(
        cacheNames
         
.filter(function (cacheName) {
           
// Return true if you want to remove this cache,
           
// but remember that caches are shared across
           
// the whole origin
         
})
         
.map(function (cacheName) {
           
return caches.delete(cacheName);
         
}),
     
);
   
}),
 
);
});

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

در Trained-to-Trill از این برای حذف حافظه پنهان قدیمی استفاده می کنم.

در مورد تعامل با کاربر

در مورد تعامل با کاربر
در مورد تعامل با کاربر

ایده آل برای: زمانی که نمی توان کل سایت را آفلاین کرد، و شما انتخاب کردید که به کاربر اجازه دهید محتوایی را که می خواهد به صورت آفلاین در دسترس قرار دهد انتخاب کند. به عنوان مثال یک ویدیو در چیزی مانند YouTube، یک مقاله در ویکی پدیا، یک گالری خاص در فلیکر.

به کاربر یک دکمه "Read later" یا "Save for offline" بدهید. وقتی روی آن کلیک شد، آنچه را که نیاز دارید از شبکه واکشی کنید و آن را در حافظه پنهان قرار دهید.

document.querySelector('.cache-article').addEventListener('click', function (event) {
  event
.preventDefault();

 
var id = this.dataset.articleId;
  caches
.open('mysite-article-' + id).then(function (cache) {
    fetch
('/get-article-urls?id=' + id)
     
.then(function (response) {
       
// /get-article-urls returns a JSON-encoded array of
       
// resource URLs that a given article depends on
       
return response.json();
     
})
     
.then(function (urls) {
        cache
.addAll(urls);
     
});
 
});
});

حافظه پنهان API از صفحات و همچنین سرویس‌کاران در دسترس است، به این معنی که می‌توانید مستقیماً از صفحه به حافظه پنهان اضافه کنید.

در پاسخ شبکه

در پاسخ شبکه
در پاسخ شبکه

ایده آل برای: به روز رسانی مکرر منابعی مانند صندوق ورودی کاربر یا محتوای مقاله. همچنین برای محتوای غیر ضروری مانند آواتارها مفید است، اما مراقبت لازم است.

اگر درخواستی با چیزی در کش مطابقت نداشت، آن را از شبکه دریافت کنید، آن را به صفحه ارسال کنید و همزمان آن را به کش اضافه کنید.

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

self.addEventListener('fetch', function (event) {
  event
.respondWith(
    caches
.open('mysite-dynamic').then(function (cache) {
     
return cache.match(event.request).then(function (response) {
       
return (
          response
||
          fetch
(event.request).then(function (response) {
            cache
.put(event.request, response.clone());
           
return response;
         
})
       
);
     
});
   
}),
 
);
});

برای استفاده کارآمد از حافظه، فقط یک بار می‌توانید متن پاسخ/درخواست را بخوانید. کد بالا از .clone() برای ایجاد کپی های اضافی استفاده می کند که می توانند جداگانه خوانده شوند.

در آموزش‌های هیجان‌انگیز، من از این برای ذخیره کردن تصاویر فلیکر استفاده می‌کنم.

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

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

ایده آل برای: به روز رسانی مکرر منابع در جایی که داشتن آخرین نسخه غیر ضروری است. آواتارها می توانند در این دسته قرار گیرند.

اگر نسخه ذخیره شده در حافظه پنهان موجود است، از آن استفاده کنید، اما برای دفعه بعد یک به‌روزرسانی دریافت کنید.

self.addEventListener('fetch', function (event) {
  event
.respondWith(
    caches
.open('mysite-dynamic').then(function (cache) {
     
return cache.match(event.request).then(function (response) {
       
var fetchPromise = fetch(event.request).then(function (networkResponse) {
          cache
.put(event.request, networkResponse.clone());
         
return networkResponse;
       
});
       
return response || fetchPromise;
     
});
   
}),
 
);
});

این بسیار شبیه به اعتبار مجدد HTTP است.

پیام فشار

پیام فشار.
پیام فشار.

Push API یکی دیگر از ویژگی های ساخته شده در بالای Service Worker است. این اجازه می دهد تا Service Worker در پاسخ به پیامی از سرویس پیام رسانی سیستم عامل بیدار شود. این اتفاق می افتد حتی زمانی که کاربر برگه ای برای سایت شما باز ندارد. فقط کارگر خدماتی بیدار می شود. شما برای انجام این کار از یک صفحه اجازه درخواست می کنید و از کاربر خواسته می شود.

ایده آل برای: محتوای مربوط به یک اعلان، مانند یک پیام چت، یک خبر فوری یا یک ایمیل. همچنین به ندرت محتوایی که از همگام‌سازی فوری بهره می‌برد، تغییر می‌دهد، مانند به‌روزرسانی فهرست کار یا تغییر تقویم.

نتیجه نهایی رایج، اعلانی است که با ضربه زدن روی آن، صفحه مربوطه را باز می‌کند/متمرکز می‌کند، اما برای آن به‌روزرسانی حافظه پنهان قبل از این اتفاق بسیار مهم است. واضح است که کاربر در زمان دریافت پیام فشار آنلاین است، اما ممکن است زمانی که در نهایت با اعلان ارتباط برقرار می کند، آنلاین نباشد، بنابراین در دسترس قرار دادن این محتوا به صورت آفلاین مهم است.

این کد قبل از نمایش اعلان، حافظه پنهان را به روز می کند:

self.addEventListener('push', function (event) {
 
if (event.data.text() == 'new-email') {
    event
.waitUntil(
      caches
       
.open('mysite-dynamic')
       
.then(function (cache) {
         
return fetch('/inbox.json').then(function (response) {
            cache
.put('/inbox.json', response.clone());
           
return response.json();
         
});
       
})
       
.then(function (emails) {
          registration
.showNotification('New email', {
            body
: 'From ' + emails[0].from.name,
            tag
: 'new-email',
         
});
       
}),
   
);
 
}
});

self
.addEventListener('notificationclick', function (event) {
 
if (event.notification.tag == 'new-email') {
   
// Assume that all of the resources needed to render
   
// /inbox/ have previously been cached, e.g. as part
   
// of the install handler.
   
new WindowClient('/inbox/');
 
}
});

در زمینه همگام سازی

در زمینه همگام سازی
در زمینه همگام سازی

همگام سازی پس زمینه یکی دیگر از ویژگی های ساخته شده در بالای Service Worker است. این به شما امکان می دهد تا همگام سازی داده های پس زمینه را به صورت یکباره یا در یک بازه (بسیار اکتشافی) درخواست کنید. این اتفاق می افتد حتی زمانی که کاربر برگه ای برای سایت شما باز ندارد. فقط کارگر خدماتی بیدار می شود. شما برای انجام این کار از یک صفحه اجازه درخواست می کنید و از کاربر خواسته می شود.

ایده‌آل برای: به‌روزرسانی‌های غیر فوری، به‌ویژه آن‌هایی که به‌طور منظم اتفاق می‌افتند که یک پیام فشار در هر به‌روزرسانی برای کاربران بسیار مکرر است، مانند جدول زمانی اجتماعی یا مقالات خبری.

self.addEventListener('sync', function (event) {
 
if (event.id == 'update-leaderboard') {
    event
.waitUntil(
      caches
.open('mygame-dynamic').then(function (cache) {
       
return cache.add('/leaderboard.json');
     
}),
   
);
 
}
});

ماندگاری کش

به منبع شما مقدار مشخصی فضای آزاد داده می شود تا کاری را که می خواهد انجام دهد. این فضای آزاد بین تمام فضای ذخیره‌سازی مبدا به اشتراک گذاشته می‌شود: ذخیره‌سازی (محلی) ، IndexedDB ، دسترسی به سیستم فایل ، و البته حافظه پنهان .

مبلغی که دریافت می کنید مشخص نیست. بسته به شرایط دستگاه و ذخیره سازی متفاوت خواهد بود. می توانید از طریق:

if (navigator.storage && navigator.storage.estimate) {
 
const quota = await navigator.storage.estimate();
 
// quota.usage -> Number of bytes used.
 
// quota.quota -> Maximum number of bytes available.
 
const percentageUsed = (quota.usage / quota.quota) * 100;
  console
.log(`You've used ${percentageUsed}% of the available storage.`);
 
const remaining = quota.quota - quota.usage;
  console
.log(`You can write up to ${remaining} more bytes.`);
}

با این حال، مانند تمام فضای ذخیره سازی مرورگر، مرورگر آزاد است که داده های شما را در صورتی که دستگاه تحت فشار ذخیره سازی قرار گیرد دور بریزد. متأسفانه مرورگر نمی تواند تفاوت بین فیلم هایی را که می خواهید به هر قیمتی نگه دارید و بازی هایی که واقعاً به آنها اهمیت نمی دهید، تشخیص دهد.

برای حل این مشکل، از رابط StorageManager استفاده کنید:

// From a page:
navigator
.storage.persist()
.then(function(persisted) {
 
if (persisted) {
   
// Hurrah, your data is here to stay!
 
} else {
   
// So sad, your data may get chucked. Sorry.
});

البته کاربر باید مجوز بدهد. برای این کار از Permissions API استفاده کنید.

قرار دادن کاربر به بخشی از این جریان مهم است، زیرا اکنون می‌توانیم انتظار داشته باشیم که آنها کنترل حذف را در دست داشته باشند. اگر دستگاه آنها تحت فشار ذخیره سازی قرار گیرد و پاک کردن داده های غیر ضروری مشکل را حل نکند، کاربر باید قضاوت کند که کدام موارد را نگه دارد و حذف کند.

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

ارائه پیشنهادات - پاسخ به درخواست ها

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

فقط کش

فقط کش
فقط کش

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

self.addEventListener('fetch', function (event) {
 
// If a match isn't found in the cache, the response
 
// will look like a connection error
  event
.respondWith(caches.match(event.request));
});

... اگر چه شما اغلب نیازی به رسیدگی به این مورد به طور خاص ندارید، Cache، بازگشت به شبکه آن را پوشش می دهد.

فقط شبکه

فقط شبکه
فقط شبکه

ایده آل برای: چیزهایی که معادل آفلاین ندارند، مانند پینگ های تجزیه و تحلیل، درخواست های غیر GET.

self.addEventListener('fetch', function (event) {
  event
.respondWith(fetch(event.request));
 
// or simply don't call event.respondWith, which
 
// will result in default browser behavior
});

... اگر چه شما اغلب نیازی به رسیدگی به این مورد به طور خاص ندارید، Cache، بازگشت به شبکه آن را پوشش می دهد.

کش، بازگشت به شبکه

کش، بازگشت به شبکه.
کش، بازگشت به شبکه.

ایده آل برای: ساختن آفلاین. در چنین مواردی، به این ترتیب اکثر درخواست‌ها رسیدگی می‌کنید. الگوهای دیگر بر اساس درخواست دریافتی استثنا خواهند بود.

self.addEventListener('fetch', function (event) {
  event
.respondWith(
    caches
.match(event.request).then(function (response) {
     
return response || fetch(event.request);
   
}),
 
);
});

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

کش و مسابقه شبکه

کش و مسابقه شبکه.
کش و مسابقه شبکه.

ایده‌آل برای: دارایی‌های کوچک که در آن‌ها به دنبال عملکرد دستگاه‌هایی با دسترسی آهسته دیسک هستید.

با ترکیبی از هارد دیسک های قدیمی، اسکنرهای ویروس و اتصالات اینترنتی سریعتر، دریافت منابع از شبکه می تواند سریعتر از رفتن به دیسک باشد. با این حال، رفتن به شبکه زمانی که کاربر محتوا را در دستگاه خود دارد می تواند باعث اتلاف داده شود، بنابراین این را در نظر داشته باشید.

// Promise.race is no good to us because it rejects if
// a promise rejects before fulfilling. Let's make a proper
// race function:
function promiseAny(promises) {
 
return new Promise((resolve, reject) => {
   
// make sure promises are all promises
    promises
= promises.map((p) => Promise.resolve(p));
   
// resolve this promise as soon as one resolves
    promises
.forEach((p) => p.then(resolve));
   
// reject if all promises reject
    promises
.reduce((a, b) => a.catch(() => b)).catch(() => reject(Error('All failed')));
 
});
}

self
.addEventListener('fetch', function (event) {
  event
.respondWith(promiseAny([caches.match(event.request), fetch(event.request)]));
});

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

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

ایده آل برای: یک راه حل سریع برای منابعی که به طور مکرر به روز می شوند، خارج از "نسخه" سایت. به عنوان مثال مقالات، آواتارها، جدول زمانی رسانه های اجتماعی، و تابلوهای رهبران بازی.

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

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

self.addEventListener('fetch', function (event) {
  event
.respondWith(
    fetch
(event.request).catch(function () {
     
return caches.match(event.request);
   
}),
 
);
});

کش و سپس شبکه

کش و سپس شبکه
کش و سپس شبکه.

ایده آل برای: محتوایی که اغلب به روز می شود. به عنوان مثال مقالات، جدول زمانی رسانه های اجتماعی، و بازی ها. تابلوهای امتیازات

برای این کار، صفحه باید دو درخواست، یکی به حافظه پنهان و دیگری به شبکه ارسال کند. ایده این است که ابتدا داده های ذخیره شده در حافظه پنهان نشان داده شود، سپس در صورت رسیدن داده های شبکه، صفحه را به روز کنید.

گاهی اوقات می‌توانید زمانی که داده‌های جدید می‌رسد، داده‌های فعلی را جایگزین کنید (مثلاً جدول امتیازات بازی)، اما این می‌تواند با محتوای بزرگ‌تر مختل شود. اساساً چیزی را که کاربر ممکن است در حال خواندن یا تعامل با آن باشد، «ناپدید» نکنید.

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

کد در صفحه:

var networkDataReceived = false;

startSpinner
();

// fetch fresh data
var networkUpdate = fetch('/data.json')
 
.then(function (response) {
   
return response.json();
 
})
 
.then(function (data) {
    networkDataReceived
= true;
    updatePage
(data);
 
});

// fetch cached data
caches
 
.match('/data.json')
 
.then(function (response) {
   
if (!response) throw Error('No data');
   
return response.json();
 
})
 
.then(function (data) {
   
// don't overwrite newer network data
   
if (!networkDataReceived) {
      updatePage
(data);
   
}
 
})
 
.catch(function () {
   
// we didn't get cached data, the network is our last hope:
   
return networkUpdate;
 
})
 
.catch(showErrorMessage)
 
.then(stopSpinner);

کد در سرویس کارگر:

شما همیشه باید به شبکه بروید و در حین رفتن یک کش را به روز کنید.

self.addEventListener('fetch', function (event) {
  event
.respondWith(
    caches
.open('mysite-dynamic').then(function (cache) {
     
return fetch(event.request).then(function (response) {
        cache
.put(event.request, response.clone());
       
return response;
     
});
   
}),
 
);
});

در آموزش‌های هیجان‌انگیز، با استفاده از XHR به‌جای واکشی ، و سوءاستفاده از سرصفحه Accept برای اینکه به Service Worker بگویم از کجا نتیجه را دریافت کند، کار کردم ( کد صفحه ، کد Service Worker ).

بازگشتی عمومی

بازگشتی عمومی
بازگشتی عمومی

اگر نتوانستید چیزی را از حافظه پنهان و/یا شبکه ارائه دهید، ممکن است بخواهید یک بازگشت عمومی ارائه دهید.

ایده‌آل برای: تصاویر ثانویه مانند آواتارها، درخواست‌های POST ناموفق، و "غیرقابل دسترسی در حالت آفلاین". صفحه

self.addEventListener('fetch', function (event) {
  event
.respondWith(
   
// Try the cache
    caches
     
.match(event.request)
     
.then(function (response) {
       
// Fall back to network
       
return response || fetch(event.request);
     
})
     
.catch(function () {
       
// If both fail, show a generic fallback:
       
return caches.match('/offline.html');
       
// However, in reality you'd have many different
       
// fallbacks, depending on URL and headers.
       
// Eg, a fallback silhouette image for avatars.
     
}),
 
);
});

موردی که به آن بازگشتی دارید احتمالاً یک وابستگی به نصب است.

اگر صفحه شما در حال ارسال ایمیل است، کارمند خدمات شما ممکن است دوباره ایمیل را در یک "صندوق خروجی" IndexedDB ذخیره کند و به صفحه پاسخ دهد که ارسال ناموفق بوده اما داده ها با موفقیت حفظ شده اند.

الگوی سمت کارگر خدمات

قالب سمت ServiceWorker.
قالب سمت ServiceWorker.

ایده آل برای: صفحاتی که نمی توانند پاسخ سرور خود را در حافظه پنهان داشته باشند.

رندر کردن صفحات در سرور کارها را سریع می کند ، اما این می تواند به معنای گنجاندن داده های حالتی باشد که ممکن است در حافظه پنهان معنی نداشته باشند، به عنوان مثال "ورود به عنوان...". اگر صفحه شما توسط یک سرویس‌کار کنترل می‌شود، می‌توانید به جای آن داده‌های JSON را به همراه یک الگو درخواست کنید و در عوض آن را ارائه دهید.

importScripts('templating-engine.js');

self
.addEventListener('fetch', function (event) {
 
var requestURL = new URL(event.request.url);

  event
.respondWith(
   
Promise.all([
      caches
.match('/article-template.html').then(function (response) {
       
return response.text();
     
}),
      caches
.match(requestURL.path + '.json').then(function (response) {
       
return response.json();
     
}),
   
]).then(function (responses) {
     
var template = responses[0];
     
var data = responses[1];

     
return new Response(renderTemplate(template, data), {
        headers
: {
         
'Content-Type': 'text/html',
       
},
     
});
   
}),
 
);
});

کنار هم گذاشتن

شما محدود به یکی از این روش ها نیستید. در واقع، بسته به URL درخواست، احتمالاً از بسیاری از آنها استفاده خواهید کرد. به عنوان مثال، استفاده های آموزش دیده برای هیجان :

فقط به درخواست نگاه کنید و تصمیم بگیرید که چه کاری انجام دهید:

self.addEventListener('fetch', function (event) {
 
// Parse the URL:
 
var requestURL = new URL(event.request.url);

 
// Handle requests to a particular host specifically
 
if (requestURL.hostname == 'api.example.com') {
    event
.respondWith(/* some combination of patterns */);
   
return;
 
}
 
// Routing for local URLs
 
if (requestURL.origin == location.origin) {
   
// Handle article URLs
   
if (/^\/article\//.test(requestURL.pathname)) {
      event
.respondWith(/* some other combination of patterns */);
     
return;
   
}
   
if (/\.webp$/.test(requestURL.pathname)) {
      event
.respondWith(/* some other combination of patterns */);
     
return;
   
}
   
if (request.method == 'POST') {
      event
.respondWith(/* some other combination of patterns */);
     
return;
   
}
   
if (/cheese/.test(requestURL.pathname)) {
      event
.respondWith(
       
new Response('Flagrant cheese error', {
          status
: 512,
       
}),
     
);
     
return;
   
}
 
}

 
// A sensible default pattern
  event
.respondWith(
    caches
.match(event.request).then(function (response) {
     
return response || fetch(event.request);
   
}),
 
);
});

... شما عکس را دریافت می کنید.

اعتبارات

برای نمادهای دوست داشتنی:

و با تشکر از جف پوسنیک برای دریافت بسیاری از خطاهای زوزه کش قبل از زدن "انتشار".

در ادامه مطلب