پیش بارگذاری ناوبری به شما امکان می دهد با درخواست های موازی بر زمان راه اندازی سرویس دهنده غلبه کنید.
- در برخی شرایط، زمان راهاندازی کارگر سرویس میتواند پاسخ شبکه را به تاخیر بیندازد .
- پیش بارگذاری ناوبری که در سه موتور مرورگر اصلی موجود است، این مشکل را برطرف میکند و به شما امکان میدهد درخواست را به موازات راهاندازی سرویسکار انجام دهید.
- میتوانید درخواستهای پیشبارگذاری را از پیمایشهای معمولی با استفاده از سربرگ تشخیص دهید و محتوای متفاوتی را ارائه دهید .
وقتی به سایتی هدایت میشوید که از یک سرویسدهنده برای رسیدگی به رویدادها استفاده میکند، مرورگر از کارمند سرویس پاسخ میخواهد. این شامل راهاندازی سرویسکار (اگر قبلاً اجرا نشده است) و ارسال رویداد واکشی است.
زمان راه اندازی به دستگاه و شرایط بستگی دارد. معمولاً حدود 50 میلیثانیه است. در تلفن همراه بیشتر از 250 میلیثانیه است. در موارد شدید (دستگاه های کند، CPU در مضیقه) می تواند بیش از 500 میلی ثانیه باشد. با این حال، از آنجایی که کارمند سرویس بین رویدادها برای مدت زمان تعیین شده توسط مرورگر بیدار می ماند، این تأخیر را فقط گاهی اوقات دریافت می کنید، مانند زمانی که کاربر از یک برگه جدید یا سایت دیگری به سایت شما می رود.
اگر از حافظه پنهان پاسخ دهید، زمان راهاندازی مشکلی ایجاد نمیکند، زیرا فایده پرش از شبکه بیشتر از تأخیر راهاندازی است. اما اگر با استفاده از شبکه پاسخ می دهید…
درخواست شبکه با راهاندازی سرویسدهنده به تأخیر میافتد.
ما همچنان به کاهش زمان راهاندازی با استفاده از ذخیرهسازی کد در V8 ادامه میدهیم . با این حال، زمان بوت آپ همیشه بزرگتر از صفر خواهد بود.
فیس بوک تأثیر این مشکل را متوجه ما کرد و راهی برای انجام درخواست های ناوبری به صورت موازی درخواست کرد:
پیشبارگذاری ناوبری ویژگیای است که به شما امکان میدهد بگویید: "وقتی کاربر درخواست ناوبری GET میکند، درخواست شبکه را در حالی که کارمند سرویس در حال راهاندازی است شروع کنید".
تاخیر راه اندازی هنوز وجود دارد، اما درخواست شبکه را مسدود نمی کند، بنابراین کاربر زودتر محتوا را دریافت می کند.
در اینجا یک ویدیو از آن در حال انجام است، که در آن به کارگر سرویس با استفاده از یک حلقه while، تاخیر راه اندازی 500 میلی ثانیه ای عمدی داده می شود:
در اینجا خود نسخه ی نمایشی است . برای بهره مندی از مزایای پیش بارگذاری پیمایش، به مرورگری نیاز دارید که از آن پشتیبانی کند .
addEventListener('activate', event => {
event.waitUntil(async function() {
// Feature-detect
if (self.registration.navigationPreload) {
// Enable navigation preloads!
await self.registration.navigationPreload.enable();
}
}());
});
می توانید هر زمان که بخواهید navigationPreload.enable()
فراخوانی کنید یا آن را با navigationPreload.disable()
غیرفعال کنید. با این حال، از آنجایی که رویداد fetch
شما باید از آن استفاده کند، بهتر است آن را در رویداد activate
سرویس کارمند خود فعال و غیرفعال کنید.
اکنون مرورگر بارگذاریهای پیشفرض را برای پیمایش انجام میدهد، اما همچنان باید از پاسخ استفاده کنید :
addEventListener('fetch', event => {
event.respondWith(async function() {
// Respond from the cache if we can
const cachedResponse = await caches.match(event.request);
if (cachedResponse) return cachedResponse;
// Else, use the preloaded response, if it's there
const response = await event.preloadResponse;
if (response) return response;
// Else try the network.
return fetch(event.request);
}());
});
event.preloadResponse
قولی است که با یک پاسخ حل می شود، اگر:
- پیش بارگیری ناوبری فعال است.
- درخواست یک درخواست
GET
است. - درخواست یک درخواست ناوبری است (که مرورگرها هنگام بارگیری صفحات، از جمله iframe) ایجاد می کنند.
در غیر این صورت event.preloadResponse
هنوز وجود دارد، اما با undefined
حل می شود.
اگر صفحه شما به دادههایی از شبکه نیاز دارد، سریعترین راه این است که آن را در سرویسکار درخواست کنید و یک پاسخ جریانی واحد شامل بخشهایی از حافظه پنهان و قطعاتی از شبکه ایجاد کنید.
فرض کنید میخواهیم مقالهای را نمایش دهیم:
addEventListener('fetch', event => {
const url = new URL(event.request.url);
const includeURL = new URL(url);
includeURL.pathname += 'include';
if (isArticleURL(url)) {
event.respondWith(async function() {
// We're going to build a single request from multiple parts.
const parts = [
// The top of the page.
caches.match('/article-top.include'),
// The primary content
fetch(includeURL)
// A fallback if the network fails.
.catch(() => caches.match('/article-offline.include')),
// The bottom of the page
caches.match('/article-bottom.include')
];
// Merge them all together.
const {done, response} = await mergeResponses(parts);
// Wait until the stream is complete.
event.waitUntil(done);
// Return the merged response.
return response;
}());
}
});
در بالا، mergeResponses
یک تابع کوچک است که جریان های هر درخواست را ادغام می کند. این بدان معنی است که ما می توانیم هدر حافظه پنهان را در حالی که محتوای شبکه در جریان است نمایش دهیم.
این سریعتر از مدل "پوسته برنامه" است زیرا درخواست شبکه همراه با درخواست صفحه انجام می شود و محتوا می تواند بدون هک های عمده پخش شود.
با این حال، درخواست includeURL
با زمان راهاندازی سرویسدهنده به تأخیر میافتد. ما میتوانیم از پیشبارگذاری ناوبری برای رفع این مشکل نیز استفاده کنیم، اما در این مورد نمیخواهیم صفحه کامل را از قبل بارگذاری کنیم، میخواهیم یک شامل را از قبل بارگذاری کنیم.
برای پشتیبانی از این، یک هدر با هر درخواست پیش بارگذاری ارسال می شود:
Service-Worker-Navigation-Preload: true
سرور می تواند از این برای ارسال محتوای متفاوت برای درخواست های پیش بارگذاری ناوبری نسبت به درخواست ناوبری معمولی استفاده کند. فقط به یاد داشته باشید که یک هدر Vary: Service-Worker-Navigation-Preload
اضافه کنید، تا کش ها بدانند که پاسخ های شما متفاوت است.
اکنون می توانیم از درخواست پیش بارگذاری استفاده کنیم:
// Try to use the preload
const networkContent = Promise.resolve(event.preloadResponse)
// Else do a normal fetch
.then(r => r || fetch(includeURL))
// A fallback if the network fails.
.catch(() => caches.match('/article-offline.include'));
const parts = [
caches.match('/article-top.include'),
networkContent,
caches.match('/article-bottom')
];
بهطور پیشفرض، مقدار هدر Service-Worker-Navigation-Preload
true
است، اما میتوانید آن را روی هر چیزی که میخواهید تنظیم کنید:
navigator.serviceWorker.ready.then(registration => {
return registration.navigationPreload.setHeaderValue(newValue);
}).then(() => {
console.log('Done!');
});
برای مثال میتوانید آن را روی شناسه آخرین پستی که به صورت محلی ذخیره کردهاید تنظیم کنید، بنابراین سرور فقط دادههای جدیدتر را برمیگرداند.
با استفاده از getState
می توانید وضعیت پیش بارگذاری ناوبری را جستجو کنید:
navigator.serviceWorker.ready.then(registration => {
return registration.navigationPreload.getState();
}).then(state => {
console.log(state.enabled); // boolean
console.log(state.headerValue); // string
});
با تشکر فراوان از مت فالکنهاگن و تسویوشی هورو برای کارشان بر روی این ویژگی و کمک به این مقاله. و تشکر فراوان از همه کسانی که در تلاش استانداردسازی دخیل بودند