En este artículo, me gustaría hablar sobre Service Workers (SW). Los SW nos permiten hacer que nuestra aplicación esté lista para funcionar sin conexión para que funcione incluso si no tenemos una conexión a Internet. También nos permiten utilizar muchas otras funciones avanzadas como notificaciones push o sincronización en segundo plano. SW continúa ejecutándose incluso después de que se cierra el navegador, lo que significa que los trabajadores del servicio continúan ejecutándose. Este es un proceso en segundo plano. Así que registremos a nuestro primer trabajador de servicio.
(En este artículo implementaré la funcionalidad relacionada con SW en JS simple, ya que el código está escrito en JS simple, podemos integrarlo en cualquier marco JS como Angular, React o Vue)
Como primer paso, agregue el archivo sw.js a la raíz del proyecto. En app.js, tenemos que comprobar si SW está disponible en el navegador, es decir, si SW es compatible con este navegador. Ahora que sabemos que los SW están disponibles, podemos ejecutar el método navigator.serviceWorker.register (), especificando la ruta al archivo donde reside nuestro SW para poder registrarlo. Este método en realidad devuelve una Promesa. Entonces, para obtener información, una vez que esté lista, podemos unirnos a él.

if ('serviceWorker' in navigator) {
navigator.serviceWorker
.register('/sw.js')
.then(event => {
console.log('Service worker registered', event);
});
}
SW, . , SW . , . SW, , , self, « SW», addEventListener (). SW , , , , Service Worker’a. , , . , Service Worker .

self.addEventListener('install', event => {
console.log('Installing [Service Worker]', event);
});
. Service Worker’a - , , . caches, API , open (), . , . event.waitUntil (). , . . then . cache.addAll () , .

self.addEventListener('install', event => {
console.log('Installing [Service Worker]', event);
event.waitUntil(
caches.open('static')
.then(cache => {
console.log('[Service Worker] Precaching App Shell');
cache.addAll([
'/',
'/index.html',
'/favicon.ico',
'/src/js/app.js',
'/src/js/chart.js',
'/src/js/materialize.js',
'/src/js/materialize.min.js',
'/src/css/materialize.css',
'/src/css/materialize.min.css',
'/src/css/style.css',
'https://fonts.googleapis.com/icon?family=Material+Icons',
'https://code.jquery.com/jquery-2.1.1.min.js',
'https://cdn.jsdelivr.net/npm/chart.js@2.8.0'
]);
}));
});
, -.

, . , . , , - . Fetch , - - , css js xhr. , fetch Service Worker’a , . , , .

self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(response => {
if (response) {
return response;
} else {
return fetch(event.request);
}
})
);
});
event.respondWith () , . Service Worker’ , , fetch. , Service Worker, . , , . cashes.match () , . , . , , , , , , . , , , , fetch (event.request). - .

, - , « » . , , , . , . , . , , , . , .

Object.keys(pureData).forEach(key => tmp[sorter[key.toLowerCase()]] = { key, value: pureData[key] });
tmp.forEach(obj => orderedData[obj.key] = obj.value);
const ctx = document.getElementById('myChart').getContext('2d');
new Chart(ctx, {
type: 'line',
data: {
labels: Object.entries(orderedData).map(([key, _]) => key),
datasets: [{
label: 'Users',
backgroundColor: '#26a69a',
borderColor: '#26a69a',
fill: false,
data: Object.entries(orderedData).map(([_, value]) => value),
}]
}
});
});
};

, , , .

self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(response => {
if (response) {
return response;
} else {
return fetch(event.request)
.then(res => {
return caches.open('dynamic')
.then(cache => {
cache.put(event.request.url, res.clone());
return res;
})
});
}
})
);
});
, , , . , caches, API open (), . cache.put () , . , , - URL- , . - . , , , , . . . . xhr. , css .

. - , . ? SW . , - , , , , indexedDB. , , SW . SW, . , , . , , Service Worker’y . , . - , , API, . Service Worker’y, ready, , . , . , ( ), . , , . , « ». Service Worker’, , , , , .

if ('serviceWorker' in navigator && 'SyncManager' in window) {
navigator.serviceWorker.ready
.then(sw => {
sw.sync.register('sync-request')
});
}
, «POST DATA» , , indexedDB . , indexedDB. , . . - . «sunday», 10 ( :)). writeData utility.js, . - , , - . .

const syncButton = document.getElementById('sync-button');
syncButton.addEventListener('click', _ => {
if ('serviceWorker' in navigator && 'SyncManager' in window) {
navigator.serviceWorker.ready
.then(sw => {
const data = {
id: new Date().getTime(),
sunday: 10
};
writeData('sync-requests', data)
.then(_ => {
sw.sync.register('sync-request')
});
});
}
});
, , - . , , .

self.addEventListener('sync', event => {
console.log('[Service Worker] Syncing');
if (event.tag === 'sync-request') {
event.waitUntil(
readAllData('sync-requests')
.then(async data => {
const requests = [];
for (const d of data) {
requests.push(fetch('https://simple-pwa-8a005.firebaseio.com/data.json', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({
sunday: d.sunday
})
}));
}
const results = await Promise.all(requests);
results.map((response, index) => {
if (response.ok) {
deleteItemFromData('sync-requests', data[index].id);
}
})
})
);
}
});
. event.waitUntil (), , , . , indexedDB ( utility.js), , post , indexedDB, . . . , , «POST DATA» .

Después de pulsar el botón "POST DATA", cuando estamos desconectados no pasa nada, pero cuando se restablece la conexión, vemos que la sincronización se ha completado.

Y para confirmar que los datos realmente se han enviado al servidor, primero debemos eliminar nuestra solicitud de búsqueda de la caché dinámica y hacer clic en el botón "OBTENER DATOS".

Eso es todo por ahora. Nos vemos más tarde. Mi código está disponible en github: https://github.com/Draakan/simplePWA