TORNAR

Servir mitjans adaptatius utilitzant Service Workers

5 min de lectura

Fent pair amb @Ester Martí

Tothom ha experimentat com visitar un lloc web a través d'una connexió de xarxa lenta sol trigar una eternitat a carregar-se, fent que l'experiència sigui molt dolorosa o completament impossible.

Quan es tracta de desenvolupament web, solem oblidar el rendiment de càrrega centrant-nos més a afegir noves característiques cridaneres. Però probablement els nostres usuaris no estan utilitzant l'últim MacBook Pro connectat a una xarxa d'1Gps. És més probable que estiguin utilitzant un dispositiu mòbil de gamma mitjana o baixa amb una connexió de xarxa que en el millor dels casos és una connexió 3G.

El 2018, el 52.2% de totes les pàgines web globals es van servir a telèfons mòbils.

Per tant, tenir cura del rendiment és important i un dels que més recursos consumeix és l'entrega de mitjans. Mostrarem com adaptar l'entrega de mitjans basada en la connexió de xarxa utilitzant l' API d'Informació de Xarxa. Aquesta és una versió millorada d'un experiment que vaig fer amb el meu company @Eduardo Aquiles com un component de React, similar al que Max Böck explica en el seu article sobre components conscients de la connexió però en aquest cas, utilitzant service workers.

L'API d'Informació de Xarxa

L'API d'Informació de Xarxa és un esborrany d'especificació que exposa una interfície a JavaScript amb informació sobre la connexió del dispositiu.

La interfície consta d'un conjunt diferent d'atributs que ens dóna múltiple informació sobre la xarxa. Els més rellevants per a nosaltres en aquest article són:

  • type: El tipus de connexió que l'agent d'usuari està utilitzant. (p.ex. ‘wifi’, ‘cellular’, ‘ethernet’, etc.)
  • effectiveType El tipus de connexió efectiva que es determina utilitzant una combinació de rtt i downlink observats recentment. (veure taula)
  • saveData Indica quan l'usuari va sol·licitar un ús reduït de dades.

valors d'effectiveType

ECTRTT Mínim (ms)Downlink Màxim (Kbps)Explicació
slow‑2g200050La xarxa és adequada només per a transferències petites com pàgines de només text.
2g140070La xarxa és adequada per a transferències d'imatges petites.
3g270700La xarxa és adequada per a transferències d'actius grans com imatges d'alta resolució, àudio i vídeo SD.
4g0La xarxa és adequada per a vídeo HD, vídeo en temps real, etc.
Taula de tipus de connexió efectiva (ECT)

Suport del navegador

L'API encara no té un suport complet del navegador però és suportada pels navegadors mòbils més populars que són on aquesta tècnica tindrà més impacte.

Suport del navegador per a l'API d'Informació de Xarxa

De fet, el 70% dels usuaris mòbils tenen aquesta API habilitada al seu dispositiu.

Servir Mitjans Adaptatius

El nostre propòsit serà servir diferents recursos multimèdia basats en la informació que obtenim de l'atribut effectiveType. Quan parlem de diferents recursos multimèdia podria ser un mitjà completament diferent, com canviar entre vídeo HD, imatge HD o imatge de baixa qualitat, l'enfocament suggerit per Addy Osmani.

En aquest exemple, utilitzarem diferents nivells de compressió per a la mateixa imatge.

Primer, necessitem obtenir la qualitat adequada basada en les condicions de la xarxa. Això és fàcilment assolible utilitzant el següent fragment:

function getMediaQuality() {
const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
if (!connection) {
return 'medium';
}
switch (connection.effectiveType) {
case 'slow-2g':
case '2g':
return 'low';
case '3g':
return 'medium';
case '4g':
return 'high';
default:
return 'low';
}
}

Imagina que tenim un servidor d'imatges on podem especificar la qualitat de la imatge que volem amb un paràmetre de consulta quality com low, medium o high. Per tant podem establir la qualitat a l'atribut src de les etiquetes d'imatges com segueix:

<img src="http://images.magarcia.io/cute_cat?quality=low" alt="Gat bonic" />
const images = document.querySelectorAll('img');
images.forEach(img => {
img.src = img.src.replace('low', getMediaQuality());
});

És important notar que la qualitat per defecte establerta a la imatge és low, el que significa que els dispositius carregaran primer la imatge de baixa qualitat i després si té una connexió d'alta velocitat carregarà la de millor qualitat.

Després el fragment de JavaScript anterior obtindrà totes les imatges al document i reemplaçarà el paràmetre de qualitat a l'apropiat basat en el que la funció getMediaQuality retorna. Si la qualitat és low no farà més peticions, però si canvia farà dues peticions: una amb la imatge de qualitat low quan els navegadors analitzen l'etiqueta img i una altra amb qualitat medium o high quan s'executa el codi JavaScript.

Això no és ideal però millorarà els temps de càrrega en xarxes lentes. Però per a xarxes de connexió mitjana/alta, com hem esmentat abans, farà dues peticions per a cada imatge consumint més dades de les necessàries.

Utilitzant Service Workers

El problema esmentat respecte a les dues peticions pot ser arreglat utilitzant service workers, interceptant la petició feta pel navegador i reemplaçant-la amb la qualitat apropiada per a la imatge.

Primer, necessitem registrar el nostre service worker:

if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/sw.js').then(
function(registration) {
console.log('Registre de ServiceWorker exitós amb abast: ', registration.scope);
},
function(err) {
console.log('Registre de ServiceWorker fallit: ', err);
}
);
});
}

A continuació, afegim un listener per a l'esdeveniment fetch, que per a totes les imatges sol·licitades des del lloc afegirà el paràmetre de qualitat correcte utilitzant la funció getMediaQuality creada a la secció anterior.

self.addEventListener('fetch', function(event) {
if (/\.jpg$|.png$|.webp$/.test(event.request.url)) {
const url = event.request.url + `?quality=${getMediaQuality()}`;
event.respondWith(fetch(url));
}
});

I ja no necessitem especificar el paràmetre de qualitat a l'etiqueta img ja que el service worker s'encarregarà d'això.

<img src=“http://images.magarcia.io/cute_cat” alt=“Gat bonic”/>

El codi

Pots trobar el codi d'aquest post (més complet, net i amb menys errors) en aquest repositori de GitHub.

Lectura Addicional