Automatisering van de resourceselectie met klanthints

Bouwen voor het web geeft u een ongeëvenaard bereik. Uw webapplicatie is slechts één klik verwijderd en beschikbaar op vrijwel elk aangesloten apparaat: smartphone, tablet, laptop en desktop, tv en meer, ongeacht het merk of het platform. Om de beste ervaring te bieden, heeft u een responsieve site gebouwd die de presentatie en functionaliteit voor elke vormfactor aanpast, en nu bent u uw prestatiechecklist aan het doorlopen om ervoor te zorgen dat de applicatie zo snel mogelijk wordt geladen: u hebt uw critical rendering path , je hebt je tekstbronnen gecomprimeerd en in de cache opgeslagen , en nu kijk je naar je afbeeldingsbronnen, die vaak verantwoordelijk zijn voor het merendeel van de overgedragen bytes. Het probleem is dat beeldoptimalisatie moeilijk is :

  • Bepaal het juiste formaat (vector vs. raster)
  • Bepaal de optimale coderingsformaten (jpeg, webp, enz.)
  • Bepaal de juiste compressie-instellingen (lossy vs. lossless)
  • Bepaal welke metadata behouden of verwijderd moeten worden
  • Maak meerdere varianten van elk voor elk scherm + DPR-resolutie
  • ...
  • Houd rekening met het netwerktype, de snelheid en de voorkeuren van de gebruiker

Individueel gezien zijn dit goed begrepen problemen . Gezamenlijk creëren ze een grote optimalisatieruimte die wij (de ontwikkelaars) vaak over het hoofd zien of verwaarlozen. Mensen zijn slecht in het herhaaldelijk verkennen van dezelfde zoekruimte, vooral als er veel stappen bij betrokken zijn. Computers daarentegen blinken uit in dit soort taken.

Het antwoord op een goede en duurzame optimalisatiestrategie voor afbeeldingen en andere bronnen met vergelijkbare eigenschappen is simpel: automatisering. Als u uw hulpbronnen met de hand afstemt, doet u het verkeerd: u vergeet het, u wordt lui, of iemand anders zal deze fouten voor u maken - gegarandeerd.

Het verhaal van de prestatiebewuste ontwikkelaar

Het zoeken door de beeldoptimalisatieruimte kent twee verschillende fasen: buildtime en runtime.

  • Sommige optimalisaties zijn inherent aan de bron zelf - bijvoorbeeld het selecteren van het juiste formaat en coderingstype, het afstemmen van de compressie-instellingen voor elke encoder, het verwijderen van onnodige metagegevens, enzovoort. Deze stappen kunnen worden uitgevoerd tijdens "build-time".
  • Andere optimalisaties worden bepaald door het type en de eigenschappen van de client die erom vraagt ​​en moeten worden uitgevoerd tijdens "run-time": het selecteren van de juiste bron voor de DPR van de client en de beoogde weergavebreedte, waarbij rekening wordt gehouden met de netwerksnelheid van de client, gebruikers- en applicatievoorkeuren, enzovoort. op.

De build-time tooling bestaat, maar kan beter worden gemaakt. Er zijn bijvoorbeeld veel besparingen te behalen door de "kwaliteit"-instelling voor elke afbeelding en elk afbeeldingsformaat dynamisch af te stemmen , maar ik zie nog niet dat iemand dit daadwerkelijk gebruikt buiten onderzoek. Dit is een gebied dat rijp is voor innovatie, maar voor de doeleinden van dit bericht laat ik het daarbij. Laten we ons concentreren op het runtime-gedeelte van het verhaal.

<img src="/image/thing" sizes="50vw"
        alt="image thing displayed at 50% of viewport width">

De bedoeling van de toepassing is heel eenvoudig: haal de afbeelding op en geef deze weer op 50% van de viewport van de gebruiker. Dit is waar vrijwel elke ontwerper zijn handen en hoofd wast voor de bar. Ondertussen staat de prestatiebewuste ontwikkelaar van het team een ​​lange nacht te wachten:

  1. Om de beste compressie te krijgen, wil ze voor elke client het optimale afbeeldingsformaat gebruiken: WebP voor Chrome, JPEG XR voor Edge en JPEG voor de rest.
  2. Om de beste visuele kwaliteit te krijgen, moet ze meerdere varianten van elke afbeelding genereren met verschillende resoluties: 1x, 1,5x, 2x, 2,5x, 3x en misschien zelfs nog een paar daar tussenin.
  3. Om te voorkomen dat er onnodige pixels worden weergegeven, moet ze begrijpen wat "50% van de viewport van de gebruiker eigenlijk betekent": er zijn veel verschillende viewportbreedtes!
  4. Idealiter wil ze ook een veerkrachtige ervaring bieden waarbij gebruikers op langzamere netwerken automatisch een lagere resolutie halen. Het draait tenslotte allemaal om tijd voor glas.
  5. De applicatie biedt ook een aantal gebruikersopties die bepalen welke afbeeldingsbron moet worden opgehaald, dus daar moet ook rekening mee worden gehouden.

Oh, en dan realiseert de ontwerper zich dat ze een andere afbeelding op 100% breedte moet weergeven als de viewport-grootte klein is om de leesbaarheid te optimaliseren. Dit betekent dat we nu hetzelfde proces voor nog een item moeten herhalen en het ophalen vervolgens afhankelijk moeten maken van de viewportgrootte. Had ik al gezegd dat dit moeilijk is? Nou, oké, laten we eraan beginnen. Met het picture komen we aardig ver :

<picture>
    <!-- serve WebP to Chrome and Opera -->
    <source
    media="(min-width: 50em)"
    sizes="50vw"
    srcset="/image/thing-200.webp 200w, /image/thing-400.webp 400w,
        /image/thing-800.webp 800w, /image/thing-1200.webp 1200w,
        /image/thing-1600.webp 1600w, /image/thing-2000.webp 2000w"
    type="image/webp">
    <source
    sizes="(min-width: 30em) 100vw"
    srcset="/image/thing-crop-200.webp 200w, /image/thing-crop-400.webp 400w,
        /image/thing-crop-800.webp 800w, /image/thing-crop-1200.webp 1200w,
        /image/thing-crop-1600.webp 1600w, /image/thing-crop-2000.webp 2000w"
    type="image/webp">
    <!-- serve JPEGXR to Edge -->
    <source
    media="(min-width: 50em)"
    sizes="50vw"
    srcset="/image/thing-200.jpgxr 200w, /image/thing-400.jpgxr 400w,
        /image/thing-800.jpgxr 800w, /image/thing-1200.jpgxr 1200w,
        /image/thing-1600.jpgxr 1600w, /image/thing-2000.jpgxr 2000w"
    type="image/vnd.ms-photo">
    <source
    sizes="(min-width: 30em) 100vw"
    srcset="/image/thing-crop-200.jpgxr 200w, /image/thing-crop-400.jpgxr 400w,
        /image/thing-crop-800.jpgxr 800w, /image/thing-crop-1200.jpgxr 1200w,
        /image/thing-crop-1600.jpgxr 1600w, /image/thing-crop-2000.jpgxr 2000w"
    type="image/vnd.ms-photo">
    <!-- serve JPEG to others -->
    <source
    media="(min-width: 50em)"
    sizes="50vw"
    srcset="/image/thing-200.jpg 200w, /image/thing-400.jpg 400w,
        /image/thing-800.jpg 800w, /image/thing-1200.jpg 1200w,
        /image/thing-1600.jpg 1600w, /image/thing-2000.jpg 2000w">
    <source
    sizes="(min-width: 30em) 100vw"
    srcset="/image/thing-crop-200.jpg 200w, /image/thing-crop-400.jpg 400w,
        /image/thing-crop-800.jpg 800w, /image/thing-crop-1200.jpg 1200w,
        /image/thing-crop-1600.jpg 1600w, /image/thing-crop-2000.jpg 2000w">
    <!-- fallback for browsers that don't support picture -->
    <img src="/image/thing.jpg" width="50%">
</picture>

We hebben de art direction verzorgd, de indeling geselecteerd en zes varianten van elke afbeelding geleverd om rekening te houden met de variabiliteit in DPR en viewport-breedte van het apparaat van de klant. Indrukwekkend!

Helaas staat het picture ons niet toe om regels te definiëren voor hoe het zich zou moeten gedragen op basis van het verbindingstype of de snelheid van de klant. Dat gezegd hebbende, staat het verwerkingsalgoritme de user-agent toe om in sommige gevallen aan te passen welke bron hij ophaalt (zie stap 5). We moeten maar hopen dat de user-agent slim genoeg is. (Opmerking: geen van de huidige implementaties is dat). Op dezelfde manier zijn er geen haken in het picture om app-specifieke logica mogelijk te maken die rekening houdt met app- of gebruikersvoorkeuren. Om deze laatste twee bits te krijgen, zouden we alle bovenstaande logica naar JavaScript moeten verplaatsen, maar daarmee gaan de vooraf geladen scanneroptimalisaties verloren die door picture worden geboden. Hm.

Afgezien van deze beperkingen, werkt het. Nou ja, tenminste voor dit specifieke bezit. De echte en langetermijnuitdaging hier is dat we niet van de ontwerper of ontwikkelaar kunnen verwachten dat hij voor elk asset een dergelijke code met de hand maakt. Het is bij de eerste poging een leuke hersenpuzzel, maar verliest onmiddellijk daarna zijn aantrekkingskracht. We hebben automatisering nodig. Misschien kunnen de IDE's of andere tools voor het transformeren van inhoud ons redden en automatisch de bovenstaande standaard genereren.

Automatisering van de resourceselectie met klanthints

Haal diep adem, zet je ongeloof opzij en denk eens na over het volgende voorbeeld:

<meta http-equiv="Accept-CH" content="DPR, Viewport-Width, Width">
...
<picture>
    <source media="(min-width: 50em)" sizes="50vw" srcset="/image/thing">
    <img sizes="100vw" src="/image/thing-crop">
</picture>

Geloof het of niet, het bovenstaande voorbeeld is voldoende om dezelfde mogelijkheden te bieden als de veel langere afbeeldingsopmaak hierboven, plus, zoals we zullen zien, het volledige controle van de ontwikkelaar mogelijk maakt over hoe, welke en wanneer de afbeeldingsbronnen worden opgehaald. De "magie" zit in de eerste regel die rapportage van klanthints mogelijk maakt en de browser vertelt om reclame te maken voor de pixelverhouding van het apparaat ( DPR ), de breedte van de layout-viewport ( Viewport-Width ) en de beoogde weergavebreedte ( Width ) van de bronnen die moeten worden weergegeven de server.

Als clienthints zijn ingeschakeld, behoudt de resulterende opmaak aan de clientzijde alleen de presentatievereisten . De ontwerper hoeft zich geen zorgen te maken over afbeeldingstypen, clientresoluties, optimale breekpunten om het aantal geleverde bytes te verminderen of andere selectiecriteria voor bronnen. Laten we eerlijk zijn: dat hebben ze nooit gedaan, en dat zou ook niet moeten. Beter nog, de ontwikkelaar hoeft de bovenstaande markup ook niet te herschrijven en uit te breiden, omdat de daadwerkelijke resourceselectie wordt onderhandeld door de client en de server.

Chrome 46 biedt native ondersteuning voor de hints DPR , Width en Viewport-Width . De hints zijn standaard uitgeschakeld en het bovenstaande <meta http-equiv="Accept-CH" content="..."> dient als een aanmeldingssignaal dat Chrome vertelt de opgegeven headers aan uitgaande verzoeken toe te voegen. Laten we nu de verzoek- en antwoordheaders voor een voorbeeldafbeeldingsverzoek bekijken:

Cliënt hints onderhandelingsdiagram

Chrome maakt reclame voor zijn ondersteuning voor het WebP-formaat via de Accept request header ; de nieuwe Edge-browser adverteert op vergelijkbare wijze ondersteuning voor JPEG XR via de Accept-header.

De volgende drie verzoekheaders zijn de client-hint-headers die reclame maken voor de pixelverhouding van het apparaat van het apparaat van de client (3x), de breedte van de lay-outviewport (460px) en de beoogde weergavebreedte van de bron (230px). Dit levert alle benodigde informatie aan de server om de optimale afbeeldingsvariant te selecteren op basis van zijn eigen set beleid: beschikbaarheid van vooraf gegenereerde bronnen, kosten voor het opnieuw coderen of aanpassen van de grootte van een bron, populariteit van een bron, huidige serverbelasting, enzovoort. . In dit specifieke geval gebruikt de server de DPR en Width -hints en retourneert een WebP-bron, zoals aangegeven door de headers Content-Type , Content-DPR en Vary .

Er is hier geen magie. We hebben de bronselectie verplaatst van HTML-opmaak naar de verzoek-antwoordonderhandeling tussen de client en de server. Als gevolg hiervan houdt HTML zich alleen bezig met presentatievereisten en is het iets dat we door elke ontwerper en ontwikkelaar kunnen laten schrijven, terwijl het zoeken in de ruimte voor beeldoptimalisatie wordt uitgesteld naar computers en nu gemakkelijk op grote schaal kan worden geautomatiseerd. Ken je onze prestatiebewuste ontwikkelaar nog? Haar taak is nu om een ​​beeldservice te schrijven die gebruik kan maken van de gegeven hints en de juiste reactie kan retourneren: ze kan elke gewenste taal of server gebruiken, of een externe service of een CDN dit namens haar laten doen.

<img src="/image/thing" sizes="50vw"
        alt="image thing displayed at 50% of viewport width">

Herinner je je deze man hierboven nog? Met hints van de klant is de eenvoudige afbeeldingstag nu DPR-, viewport- en breedtebewust zonder enige extra opmaak. Als je art-direction moet toevoegen, kun je de picture gebruiken, zoals we hierboven hebben geïllustreerd, en anders zijn al je bestaande afbeeldingstags een stuk slimmer geworden. Cliënttips verbeteren bestaande img en picture .

De controle over de resourceselectie overnemen met een servicemedewerker

ServiceWorker is in feite een proxy aan de clientzijde die in uw browser wordt uitgevoerd. Het onderschept alle uitgaande verzoeken en stelt u in staat antwoorden te inspecteren, herschrijven, in het cachegeheugen op te slaan en zelfs te synthetiseren. Voor afbeeldingen is dit niet anders. Als clienthints zijn ingeschakeld, kan de actieve ServiceWorker de afbeeldingsverzoeken identificeren, de verstrekte clienthints inspecteren en zijn eigen verwerkingslogica definiëren.

self.onfetch = function(event) {
    var req = event.request.clone();
    console.log("SW received request for: " + req.url)
    for (var entry of req.headers.entries()) {
    console.log("\t" + entry[0] +": " + entry[1])
    }
    ...
}
Klanttips serviceWorker.

ServiceWorker geeft u volledige controle aan de clientzijde over de resourceselectie . Dit is van cruciaal belang. Laat dat maar even doordringen, want de mogelijkheden zijn vrijwel oneindig:

  • U kunt de headerwaarden van de clienthints herschrijven die zijn ingesteld door de user-agent.
  • U kunt nieuwe headerwaarden voor clienthints aan de aanvraag toevoegen.
  • U kunt de URL herschrijven en het afbeeldingsverzoek naar een alternatieve server (bijvoorbeeld CDN) verwijzen.
    • U kunt de hintwaarden zelfs uit headers verplaatsen naar de URL zelf, als dat de implementatie in uw infrastructuur eenvoudiger maakt.
  • U kunt antwoorden in de cache opslaan en uw eigen logica definiëren waarvoor bronnen worden aangeboden.
  • U kunt uw reactie aanpassen op basis van de connectiviteit van gebruikers.
  • U kunt rekening houden met overschrijvingen van toepassings- en gebruikersvoorkeuren.
  • Je kunt... eigenlijk alles doen wat je hartje begeert.

Het picture element zorgt voor de nodige art-directionele controle in de HTML-opmaak. Clienthints bieden annotaties bij resulterende afbeeldingsverzoeken die automatisering van resourceselectie mogelijk maken. ServiceWorker biedt mogelijkheden voor aanvraag- en antwoordbeheer op de client. Dit is het uitbreidbare web in actie.

De klant hints FAQ

  1. Waar zijn klanttips beschikbaar? Verzonden in Chroom 46 . Wordt overwogen in Firefox en Edge .

  2. Waarom zijn de klanthints aangemeld? We willen de overhead minimaliseren voor sites die geen clienthints gebruiken. Om clienthints mogelijk te maken, moet de site de Accept-CH header of een gelijkwaardige <meta http-equiv> -richtlijn in de pagina-opmaak plaatsen. Als een van deze aanwezig is, zal de user-agent de juiste hints toevoegen aan alle subresourceverzoeken. In de toekomst kunnen we een aanvullend mechanisme bieden om deze voorkeur voor een bepaalde oorsprong te behouden, waardoor dezelfde hints kunnen worden gegeven op navigatieverzoeken.

  3. Waarom hebben we klanthints nodig als we ServiceWorker hebben? ServiceWorker heeft geen toegang tot informatie over de lay-out, de resource en de viewportbreedte. Tenminste, niet zonder kostbare roundtrips te introduceren en het beeldverzoek aanzienlijk te vertragen, bijvoorbeeld wanneer een beeldverzoek wordt geïnitieerd door de preload-parser. Clienthints kunnen met de browser worden geïntegreerd om deze gegevens beschikbaar te maken als onderdeel van het verzoek.

  4. Zijn klanthints alleen voor afbeeldingsbronnen? Het belangrijkste gebruiksscenario achter DPR-, Viewport-Width- en Breedte-hints is het inschakelen van resourceselectie voor afbeeldingsitems. Dezelfde hints worden echter gegeven voor alle subbronnen, ongeacht het type. CSS- en JavaScript-verzoeken krijgen bijvoorbeeld ook dezelfde informatie en kunnen ook worden gebruikt om die bronnen te optimaliseren.

  5. Bij sommige afbeeldingsverzoeken wordt de breedte niet gerapporteerd. Waarom? De browser weet mogelijk niet wat de beoogde weergavebreedte is, omdat de site afhankelijk is van de intrinsieke grootte van de afbeelding. Als gevolg hiervan wordt de breedte-hint weggelaten voor dergelijke verzoeken, en voor verzoeken die geen "weergavebreedte" hebben, bijvoorbeeld een JavaScript-bron. Als u breedtehints wilt ontvangen, zorg er dan voor dat u een waarde voor het formaat opgeeft bij uw afbeeldingen.

  6. Hoe zit het met <voeg mijn favoriete hint in> ? Met ServiceWorker kunnen ontwikkelaars alle uitgaande verzoeken onderscheppen en wijzigen (bijvoorbeeld nieuwe headers toevoegen). Het is bijvoorbeeld eenvoudig om op NetInfo gebaseerde informatie toe te voegen om het huidige verbindingstype aan te geven - zie " Capaciteitsrapportage met ServiceWorker ". De "native" hints die in Chrome worden geleverd (DPR, Breedte, Resource-Width) worden in de browser geïmplementeerd omdat een pure SW-gebaseerde implementatie alle afbeeldingsverzoeken zou vertragen.

  7. Waar kan ik meer leren, meer demo's bekijken, en hoe zit het? Bekijk het uitlegdocument en open gerust een probleem op GitHub als je feedback of andere vragen hebt.