Les travailleurs des services sont une technologie fantastique. Vous les connaissez peut-être en relation avec le terme Progressive Web Application (PWA), de sorte que quelque chose qui est normalement visible sur le navigateur puisse être "installé" dans le système d'exploitation, et puisse être ouvert comme une application native, et désinstallé comme une application native, et ressemble à une application native tout autour. Mais les travailleurs des services peuvent faire bien plus que cela.
Pour l'accessibilité et les explications, regardez ici.
Les Service Workers sont essentiellement des Web Workers partagés (qui existent d'ailleurs en tant que technologie distincte) avec la capacité spéciale d'intercepter toutes les requêtes http faites par le navigateur à partir d'URL dans la même portée (chemin d'origine) avec lequel le travailleur a été enregistré. Ensuite, il pourrait être demandé soit de répondre avec une réponse construite ou mise en cache - empêchant effectivement le navigateur d'atteindre le réseau avec la requête - soit de transmettre la requête au réseau comme d'habitude ou en modifiant la requête (en utilisant fetch).
Cela dit, il est clair pourquoi les techniciens de service sont souvent associés à la possibilité d'accéder à une page Web hors ligne : la première fois, vous pouvez télécharger et mettre en cache toutes les ressources statiques (ce qui en gros "installe" la page), le service worker peut alors répondre aux mêmes demandes avec les versions mises en cache, servant essentiellement les "ressources de l'application" comme s'il s'agissait d'une application native. dev.to en est un excellent exemple.
C'est déjà une simplification, et parler de contournement de cache, de mises à jour et du reste sort du cadre de cet article, donc je ne m'y livrerai pas. Ce dont je vais parler, c'est de la capacité des travailleurs des services à servir des réponses construites.
Mon équipe a récemment été chargée de créer une application « vitrine », c'est-à-dire une application Web qui ne fait fondamentalement rien, mais sert à montrer comment utiliser notre kit d'interface utilisateur de composants Web, en suivant le système de conception. et les lignes directrices de codage.
L'application était conçue comme une application purement frontend (ce qui signifie que nous n'étions pas censés développer également un backend), mais devrait ressembler à l'une des nombreuses applications B2B que notre client maintient, avec le backend et tout . C'est là que le rôle d'un agent de service s'avère utile.
Maintenant, répondre par une réponse textuelle est assez simple. Même un JSON est essentiellement du texte, donc en fin de compte, notre service worker pourrait ressembler à ceci :
self.addEventListener('fetch', event => { if (event.request.url.includes('/api/hello')) { event.respondWith(new Response( JSON.stringify({ message: 'Hello!' }), { headers: { 'Content-Type': 'application/json' }} ); } else { event.respondWith(fetch(event.request)); } });
Je ne vous ennuierai pas sur la façon dont cet extrait pourrait être amélioré. La correspondance d'URL pourrait utiliser URLPattern. Vous pouvez charger des données statiques avec fetch et les stocker sur IndexedDB. Vous pouvez devenir fou avec ça.
Mais qu’en est-il des autres types de réponses dynamiques ? Vous aimez les images ?
Le moyen le plus simple de générer une image dynamique est de créer un SVG, qui est essentiellement un document XML. Autrement dit, c'est du texte. C'est une tâche tout à fait réalisable, et vous pouvez utiliser des bibliothèques comme D3.js pour générer les éléments et les chemins SVG pour vous : des usines comme line() et d'autres renvoient des fonctions qui renvoient ce que vous devez mettre dans l'attribut d de
self.addEventListener('fetch', event => { if (event.request.url.includes('/api/hello')) { event.respondWith(new Response( JSON.stringify({ message: 'Hello!' }), { headers: { 'Content-Type': 'application/json' }} ); } else { event.respondWith(fetch(event.request)); } });
Générer dynamiquement des SVG pourrait être formidable pour retirer la tâche du thread principal - et le résultat pourrait même être mis en cache. C'est idéal pour les graphiques et les infographies, et assez "facile" à réaliser.
Ce qui est plus délicat, c'est de générer une image raster comme un PNG ou un JPG. « Génération » signifie utiliser des instruments d'édition pour modifier une image ou la créer à partir de zéro. Ce que nous faisons habituellement dans ces cas, c'est d'utiliser un
Le problème est que les techniciens de service n'ont pas accès à l'élément DOM. Alors, on n'a pas de chance ?
Ne vous inquiétez pas, mes amis ! Parce que tous les travailleurs (y compris les travailleurs des services) peuvent créer des objets OffscreenCanvas. Donnez une largeur et une hauteur en pixels au conscructeur et voilà, une toile parfaitement fine (bien qu'invisible) chez un service worker :
import { pie, arc } from 'd3-shape'; const pieData = pie().sort(null)(data); const sectorArc = arc().outerRadius(35).innerRadius(20); const svg = '<svg viewBox="-40 -40 80 80" xmlns="http://www.w3.org/2000/svg">' + pieData.map((pie, index) => `<path d="${sectorArc(pie)}" fill="${colors[index]}"/>` ).join('') + '</svg>'; event.respondWith(new Response( svg, { headers: { 'Content-Type': 'image/svg+xml' }} ));
Pour ceux qui se demandent : oui, vous pouvez obtenir un type de contexte différent, même si tous ne sont pas disponibles dans tous les navigateurs. Vous pouvez essayer d'utiliser une bibliothèque comme three.js pour générer des scènes 3D dans un service worker (je pense que j'essaierai cela plus tard).
Maintenant, nous pouvons faire... n'importe quoi, en gros. Dessinez des lignes, des arcs, des chemins, etc. Même en modifiant la géométrie de notre toile. C'est aussi simple que de dessiner sur un contexte de canevas DOM, je ne me livrerai donc pas à cette partie.
On peut en effet aussi écrire du texte. Ceci est important car dans d'autres environnements - à savoir un worklet Paint, nous ne pouvons pas faire cela :
Remarque : PaintRenderingContext2D implémente un sous-ensemble de l'API CanvasRenderingContext2D. Plus précisément, il n'implémente pas les API CanvasImageData, CanvasUserInterface, CanvasText ou CanvasTextDrawingStyles.
Mais chez un employé de service, tout va bien. Cela signifie que nous disposons d'un environnement plus puissant (bien que moins performant) pour générer nos images d'arrière-plan.
Dessiner du texte est aussi simple que ceci :
const canvas = new OffscreenCanvas(800, 600); const context = canvas.getContext('2d');
Vous pouvez utiliser la police que vous aimez ici, mais j'ai constaté que les valeurs standard habituelles comme sans-serif, monospace ou system-ui ne semblent pas fonctionner, car elles reviennent toutes à la police serif par défaut. Mais vous pouvez utiliser des piles de polices comme d'habitude :
context.fillStyle = '#222'; context.font = '24px serif'; // (x, y) = (50, 90) will be the *bottom left* corner of the text context.fillText('Hello, world!', 50, 90);
De plus, vous pouvez utiliser l'API Font Loading pour charger des polices à partir de ressources externes :
self.addEventListener('fetch', event => { if (event.request.url.includes('/api/hello')) { event.respondWith(new Response( JSON.stringify({ message: 'Hello!' }), { headers: { 'Content-Type': 'application/json' }} ); } else { event.respondWith(fetch(event.request)); } });
Renvoyer la réponse est, encore une fois, aussi simple que d'appeler la méthode convertToBlob qui renvoie la promesse - vous l'aurez deviné - d'un Blob. Et les blobs peuvent être facilement renvoyés à l'expéditeur.
import { pie, arc } from 'd3-shape'; const pieData = pie().sort(null)(data); const sectorArc = arc().outerRadius(35).innerRadius(20); const svg = '<svg viewBox="-40 -40 80 80" xmlns="http://www.w3.org/2000/svg">' + pieData.map((pie, index) => `<path d="${sectorArc(pie)}" fill="${colors[index]}"/>` ).join('') + '</svg>'; event.respondWith(new Response( svg, { headers: { 'Content-Type': 'image/svg+xml' }} ));
La méthode crée une image PNG par défaut, mais peut être invitée à créer un fichier JPG à la place, comme vu ci-dessus. « image/webp » est un autre format courant, mais Safari ne le prend pas en charge. Pour être honnête, le choix ici est un peu décevant, car les décodeurs de format d'image nouvellement disponibles et plus performants ne sont pas reflétés dans leurs encodeurs correspondants. Mais c'est suffisant dans la plupart des cas de toute façon.
Fun fact : la méthode convertToBlob est spécifique à la classe OffscreenCanvas. HTMLCanvasElements doit à la place toBlob, qui prend un rappel comme premier argument, dans le style courant de gestion des tâches asynchrones de l'ère pré-Promise.
Maintenant, tout cela fonctionne si nous voulons créer une image à partir de zéro. Mais que se passe-t-il si nous voulons repartir d’un modèle vierge ?
Si nous devions travailler dans le fil principal, nous pourrions placer une image dans le contexte en utilisant la méthode drawImage de notre contexte 2D, en la source par exemple. à partir d'un site élément.
Le problème est, encore une fois, que nous ne pouvons pas accéder au DOM, nous ne pouvons donc pas référencer éléments. Ce que nous pouvons faire, à la place, c'est récupérer l'image dont nous avons besoin comme arrière-plan, obtenir son Blob, puis le convertir en quelque chose d'autre que drawImage peut digérer. Entrez createImageBitmap, une méthode globale également disponible dans les service Workers. Il renvoie une promesse pour une instance ImageBitmap, l'une des nombreuses classes les moins connues de développement Web frontend. Il est apparemment plus largement utilisé dans les contextes WebGL, mais drawImage semble l'accepter, alors...
const canvas = new OffscreenCanvas(800, 600); const context = canvas.getContext('2d');
À partir de ce moment, nous pouvons procéder à y dessiner nos gribouillis et nos textes, créant ainsi une image synamique satisfaisante à renvoyer à l'utilisateur.
Remarque : cela pourrait être plus facilement résolu avec un SVG, car vous pouvez simplement utiliser une image
élément pour créer une image d’arrière-plan. Mais cela signifierait que le navigateur doit charger l'image après l'image générée a été envoyée, alors qu'avec cette technique cela se fait avant. Quelque chose de similaire s'applique lors du choix d'une police.
Dans tous ces exemples, j'ai utilisé des service Workers module (c'est-à-dire que j'ai utilisé l'importation à partir d'autres modules ES). Hélas, les modules service Workers ne sont pas encore pris en charge par Firefox, mais j'espère qu'ils le seront bientôt. En attendant, vous devrez peut-être ajuster votre code pour utiliser à la place les anciens importScripts.
Lors de l'importation d'autres scripts dans un service Workers, que ce soit via import ou importScripts, rappelez-vous que le navigateur pas déclenchera un événement updatefound lorsqu'un fichier importé est modifié : il est déclenché uniquement lorsque le script d'entrée du service worker change.
Dans un cas comme le nôtre, où le service worker n'est nécessaire que pour se moquer de la présence d'un backend, son cycle de vie pourrait être raccourci en appelant self.skipWaiting() juste au moment où l'événement d'installation est déclenché, puis en appelant self. clients.claim() sur l'événement activate afin de pouvoir répondre immédiatement aux requêtes (sinon, cela ne démarrera qu'au prochain rafraîchissement de la page).
self.addEventListener('fetch', event => { if (event.request.url.includes('/api/hello')) { event.respondWith(new Response( JSON.stringify({ message: 'Hello!' }), { headers: { 'Content-Type': 'application/json' }} ); } else { event.respondWith(fetch(event.request)); } });
Et c'est essentiellement tout, alors... amusez-vous avec les travailleurs des services, les amis !
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!