Pemuatan awal navigasi memungkinkan Anda mengatasi waktu mulai pekerja layanan dengan membuat permintaan secara paralel.
Ringkasan
- Dalam beberapa situasi, waktu booting pekerja layanan dapat menunda respons jaringan.
- Pemuatan awal navigasi, yang tersedia di tiga mesin browser utama, memperbaiki masalah ini dengan memungkinkan Anda membuat permintaan secara paralel dengan booting service worker.
- Anda dapat membedakan permintaan pramuat dari navigasi reguler menggunakan header, dan menampilkan konten yang berbeda.
Permasalahan
Saat Anda membuka situs yang menggunakan service worker untuk menangani peristiwa pengambilan, browser akan meminta respons dari service worker. Hal ini melibatkan booting pekerja layanan (jika belum berjalan), dan mengirimkan peristiwa pengambilan.
Waktu booting bergantung pada perangkat dan kondisi. Biasanya sekitar 50 md. Di perangkat seluler, nilainya lebih seperti 250 md. Dalam kasus ekstrem (perangkat lambat, CPU bermasalah), nilainya bisa lebih dari 500 md. Namun, karena pekerja layanan tetap aktif selama waktu yang ditentukan browser di antara peristiwa, Anda hanya akan mengalami penundaan ini sesekali, seperti saat pengguna membuka situs Anda dari tab baru, atau situs lain.
Waktu booting tidak menjadi masalah jika Anda merespons dari cache, karena manfaat melewati jaringan lebih besar daripada penundaan booting. Namun, jika Anda merespons menggunakan jaringan…
Permintaan jaringan tertunda karena booting service worker.
Kami terus mengurangi waktu booting dengan menggunakan cache kode di V8, dengan melewati pekerja layanan yang tidak memiliki peristiwa pengambilan, dengan meluncurkan pekerja layanan secara spekulatif, dan pengoptimalan lainnya. Namun, waktu booting akan selalu lebih besar dari nol.
Facebook menyampaikan dampak masalah ini kepada kami, dan meminta cara untuk melakukan permintaan navigasi secara paralel:
Pramuat navigasi untuk menyelamatkan
Pemuatan awal navigasi adalah fitur yang memungkinkan Anda mengatakan, "Saat pengguna membuat permintaan navigasi GET, mulai permintaan jaringan saat pekerja layanan sedang melakukan booting".
Penundaan saat memulai masih ada, tetapi tidak memblokir permintaan jaringan, sehingga pengguna mendapatkan konten lebih cepat.
Berikut adalah video yang menunjukkannya saat beroperasi, dengan pekerja layanan diberi penundaan startup 500 md yang disengaja menggunakan loop while:
Berikut demonya. Untuk mendapatkan manfaat pramuat navigasi, Anda memerlukan browser yang mendukungnya.
Mengaktifkan pramuat navigasi
addEventListener('activate', event => {
event.waitUntil(async function() {
// Feature-detect
if (self.registration.navigationPreload) {
// Enable navigation preloads!
await self.registration.navigationPreload.enable();
}
}());
});
Anda dapat memanggil navigationPreload.enable()
kapan saja, atau menonaktifkannya dengan navigationPreload.disable()
. Namun, karena peristiwa fetch
Anda perlu menggunakannya, sebaiknya aktifkan dan nonaktifkan di peristiwa activate
pekerja layanan Anda.
Menggunakan respons yang sudah dimuat
Sekarang browser akan melakukan pemuatan awal untuk navigasi, tetapi Anda tetap perlu menggunakan respons:
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
adalah promise yang diselesaikan dengan respons, jika:
- Pramuat navigasi diaktifkan.
- Permintaan adalah permintaan
GET
. - Permintaan adalah permintaan navigasi (yang dibuat browser saat memuat halaman, termasuk iframe).
Jika tidak, event.preloadResponse
masih ada, tetapi akan diselesaikan dengan undefined
.
Respons kustom untuk pramuat
Jika halaman Anda memerlukan data dari jaringan, cara tercepat adalah dengan memintanya di pekerja layanan dan membuat satu respons streaming yang berisi bagian dari cache dan bagian dari jaringan.
Katakanlah kita ingin menampilkan artikel:
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;
}());
}
});
Di atas, mergeResponses
adalah fungsi kecil yang menggabungkan aliran setiap permintaan. Artinya, kita dapat menampilkan header yang di-cache saat konten jaringan di-streaming.
Hal ini lebih cepat daripada model "shell aplikasi" karena permintaan jaringan dibuat bersama dengan permintaan halaman, dan konten dapat di-streaming tanpa peretasan besar.
Namun, permintaan untuk includeURL
akan tertunda oleh waktu mulai pekerja layanan. Kita juga dapat menggunakan pra-pemuatan navigasi untuk memperbaikinya, tetapi dalam kasus ini kita tidak ingin memuat halaman lengkap, kita ingin memuat include.
Untuk mendukung hal ini, header dikirim dengan setiap permintaan pramuat:
Service-Worker-Navigation-Preload: true
Server dapat menggunakan ini untuk mengirim konten yang berbeda untuk permintaan pra-muat navigasi daripada yang akan dikirim untuk permintaan navigasi reguler. Jangan lupa untuk menambahkan header Vary: Service-Worker-Navigation-Preload
, sehingga cache mengetahui bahwa respons Anda berbeda.
Sekarang kita dapat menggunakan permintaan pramuat:
// 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')
];
Mengubah header
Secara default, nilai header Service-Worker-Navigation-Preload
adalah true
, tetapi Anda dapat menyetelnya ke nilai apa pun yang Anda inginkan:
navigator.serviceWorker.ready.then(registration => {
return registration.navigationPreload.setHeaderValue(newValue);
}).then(() => {
console.log('Done!');
});
Misalnya, Anda dapat menyetelnya ke ID postingan terakhir yang telah Anda simpan ke cache secara lokal, sehingga server hanya menampilkan data yang lebih baru.
Mendapatkan status
Anda dapat mencari status pra-pemuatan navigasi menggunakan getState
:
navigator.serviceWorker.ready.then(registration => {
return registration.navigationPreload.getState();
}).then(state => {
console.log(state.enabled); // boolean
console.log(state.headerValue); // string
});
Terima kasih banyak kepada Matt Falkenhagen dan Tsuyoshi Horo atas upaya mereka dalam mengembangkan fitur ini, dan bantuan mereka dalam menulis artikel ini. Kami juga mengucapkan terima kasih banyak kepada semua orang yang terlibat dalam upaya standardisasi ini