Lazy Loading Bilder med Vue.js Direktiven och Korsningen Observatör

0
17

När jag tänker på web performance, det första som kommer att tänka på är hur bilder är i allmänhet de sista delarna som visas på en sida. Idag, bilder kan vara en viktig fråga när det gäller prestanda, vilket är olyckligt eftersom hastigheten en webbplats laddas har en direkt påverkan på användare med framgång att göra vad de kom för att sidan ska göra (tror samtal priser).

Mycket nyligen, Rahul Nanwani skrev en utförlig guide på lazy loading-bilder. Jag skulle vilja att täcka samma ämne, men från en annan metod: med hjälp av data attribut, Korsningen Observatör och egna direktiv i Vue.js.

Vad detta kommer att i grunden göra är att tillåta oss att lösa två saker:

  1. Lagra src den bild vi vill läsa utan att ladda den i första hand.
  2. Upptäcka när bilden blir synlig för användaren och utlöser begäran att ladda bilden.

Samma grundläggande lazy loading-konceptet, men ett annat sätt att gå tillväga.

Jag skapade ett exempel, baserad på ett exempel som beskrivs av Benjamin Taylor i hans blogginlägg. Det innehåller en lista av slumpmässiga artiklar som var och en innehåller en kort beskrivning, bild och en länk till källan av artikeln. Vi kommer att gå igenom processen för att skapa en komponent som är ansvarig för att visa listan att göra en artikel, och lazy loading bilden för en specifik artikel.

Låt oss få lata! Eller åtminstone bryta denna komponent ner bit-för-bit.

Steg 1: Skapa ImageItem komponent i Vue

Låt oss börja med att skapa en komponent som kommer att visa en bild (men utan lazy loading inblandade bara ännu). Vi kallar denna fil ImageItem.vue. I den del som mall, vi kommer att använda en figur-tagg som innehåller vår bild — bild-taggen själv kommer att få src attribut som pekar till käll-URL: en för bilden.

<mall>
<figur class=”image__wrapper”>
<img
class=”image__post”
:src=”källan”
alt=”random image”
>
</bild>
</template>

I skriptet avsnitt av komponenten, vi får prop källa som vi kommer att använda för den src webbadressen för bilden vi visar.

export standard {
namn: “ImageItem”,
rekvisita: {
källa: {
typ: String,
krävs: sanna
}
}
};

Allt detta är alldeles utmärkt och kommer att göra bilden normalt vara som det är. Men, om vi lämnar det här, kommer bilden att ladda direkt utan att vänta på att hela komponenten att göra. Det är inte vad vi vill ha, så låt oss gå till nästa steg.

Steg 2: Förhindra att bilden laddas när komponenten är skapat

Det låter kanske lite lustigt att vi vill förhindra att något laddas när vi vill visa det, men detta handlar om att läsa in den vid rätt tidpunkt, snarare än att blockera det på obestämd tid. För att förhindra att bilden laddas, vi behöver bli av med src-attribut i img-taggen. Men, vi har fortfarande behov av att lagra det någonstans så vi kan använda det när vi vill ha det. Ett bra ställe att hålla denna information är i en data – attribut. Detta tillåter oss att lagra information om standard, semantisk HTML-element. I själva verket kan du redan vara van vid att använda dem som en JavaScript-selektorer.

I det här fallet, de är en perfekt passform för våra behov!

<!–ImageItem.vue–>
<mall>
<figur class=”image__wrapper”>
<img
class=”image__post”
:data-url=”källan” // yay för data attribut!
alt=”random image”
>
</bild>
</template>

Med vår bild kommer inte att läsa eftersom det inte finns någon källa URL för att dra ifrån.

Det är en bra start, men fortfarande inte riktigt vad vi vill. Vi vill läsa vår bild under särskilda villkor. Vi kan begära bilden för att ladda genom att ersätta src-attribut med bildkällans URL hålls i våra data-url attribut. Det är den lätta delen. Den verkliga utmaningen är att lista ut när man ska ersätta den med den verkliga källan.

Vårt mål är att fästa lasten till användarens skärm läge. Så, när användaren förflyttar sig till en punkt där bilden kommer i bild, det är där det massor.

Hur kan vi upptäcka om bilden är i visa eller inte? Det är vårt nästa steg.

Steg 3: Identifiera när bilden är synliga för användaren

Du kanske har erfarenhet av att använda JavaScript för att avgöra när ett element är i sikte. Du kan också ha erfarenhet likvidation med några tuffa manus.

Vi skulle till exempel kunna använda händelser och event handlers för att upptäcka bläddra position, offset värde, element höjd, och vyport höjd, sedan beräkna om en bild är i det virtuella eller inte. Men det låter redan knotiga, inte det?

Men det kan bli värre. Detta har en direkt påverkan på prestanda. Dessa beräkningar skulle få sparken på varje bläddra händelse. Ännu värre, tänk dig ett par dussin bilder, var och en har att räkna om den är synlig eller inte på varje bläddra händelse. Galenskap!

Korsningen Observatör till undsättning! Detta ger en mycket effektiv metod för att upptäcka om ett element är synliga i det virtuella. Specifikt, den gör det möjligt för dig att konfigurera en callback som utlöses när ett element som kallas mål — skär med antingen enheten vy eller en viss del.

Så, vad vi behöver göra för att använda det? Ett par saker:

  • skapa en ny korsning observatör
  • titta på den delen som vi vill lata belastning för synlighet förändringar
  • ladda element när elementet är i viewport (genom att ersätta src våra data-url)
  • sluta titta på för att synliggöra förändringar (unobserve) efter det att belastningen är klar

Vue.js har egna direktiv för att linda alla dessa funktioner tillsammans och använda den när vi behöver den, så många gånger som vi behöver det. Sätta att använda är vårt nästa steg.

Steg 4: Skapa en Vue egna direktiv

Vad är en anpassad direktiv? Vue dokumentation beskriver det som ett sätt att få låg nivå DOM tillgång på delar. Till exempel, för att ändra ett attribut i ett särskilt DOM-element, som i vårt fall, kan vara att ändra src-attribut i img-element. Perfekt!

Vi ska bryta ner det i ett ögonblick, men det här är vad vi tittar på så långt som koden:

export standard {
inserted: el => {
funktion loadImage() {
const imageElement = Array.från(el.barn).hitta(
el => el.nodnamn === “IMG”
);
om (imageElement) {
imageElement.addEventListener(“load” () = > {
setTimeout(() => el.classList.lägga till(“lastade”), 100);
});
imageElement.addEventListener(“fel”, () = > – konsolen.logga in(“fel”));
imageElement.src = imageElement.dataset.url;
}
}

funktion handleIntersect(poster, observatör) {
poster.forEach(entry => {
om (posten.isIntersecting) {
loadImage();
observatör.unobserve(el);
}
});
}

funktion createObserver() {
const alt = {
rot: null,
tröskeln: “0”
};
const observatör = new IntersectionObserver(handleIntersect, alternativ).
observatör.observera(el);
}
(om fönstret[“IntersectionObserver”]) {
createObserver();
} else {
loadImage();
}
}
};

OK, vi ta itu med denna steg-för-steg.

Den krok funktion tillåter oss att brand en egen logik i ett visst ögonblick av en bunden del livscykel. Vi använder införas krok eftersom det kallas när den bundna delen har varit införd i dess överordnade noden (detta garanterar den överordnade noden är närvarande). Eftersom vi vill följa synlighet av ett element i förhållande till sin förälder (eller någon förfader), måste vi använda den kroken.

export standard {
inserted: el => {

}
}

Den loadImage funktion är ansvarig för att ersätta den src värdet med data-url. I den har vi tillgång till våra element (el) och det är där vi tillämpa direktivet. Vi kan extrahera img från elementet.

Därefter kommer vi att kontrollera om bilden som finns och, om den gör det, vi lägger till en lyssnare som kommer att skjuta en callback-funktion när lastningen är klar. Anropet kommer att vara ansvariga för att dölja spinner och lägger till animation (fade-in effekt till bilden med hjälp av en CSS-klass. Vi lägger även till en andra lyssnare som kommer att kallas i händelse av att URL: en går inte att ladda.

Slutligen, vi ersätta src våra img element med käll-URL: en för bilden och visa det!

funktion loadImage() {
const imageElement = Array.från(el.barn).hitta(
el => el.nodnamn === “IMG”
);
om (imageElement) {
imageElement.addEventListener(“load” () = > {
setTimeout(() => el.classList.lägga till(“lastade”), 100);
});
imageElement.addEventListener(“fel”, () = > – konsolen.logga in(“fel”));
imageElement.src = imageElement.dataset.url;
}
}

Vi använder Korsningen Observatör handleIntersect funktion som ansvarar för bränning loadImage när vissa villkor är uppfyllda. Det är framför allt sparken när Korsningen Observatör upptäcker att elementet går in i den virtuella eller en förälder komponent element.

Funktionen har tillgång till poster, som är en array med alla element som är bevakad av observatören och observatören själv. Vi iterera genom poster och kontrollera om det finns en enda inlägg blir synliga för våra användare med isIntersecting — och brand loadImage att fungera om det. När bilden är begärt, vi unobserve elementet (ta bort det från observers bevakningslista), som förhindrar att bilden laddas igen. Och igen. Och igen. Och…

funktion handleIntersect(poster, observatör) {
poster.forEach(entry => {
om (posten.isIntersecting) {
loadImage();
observatör.unobserve(el);
}
});
}

Den sista biten är createObserver funktion. Den här killen är ansvariga för att skapa våra Korsningen Observatör och koppla det till våra element. Den IntersectionObserver konstruktor som accepterar en begäran (vår handleIntersect funktion) som avfyras när den observerade del passerar tröskelvärdet och alternativ objekt som bär vår observatör alternativ.

På tal om val objekt, använder rot som vår referens objekt, som vi använder för att basera synlighet av våra tittade element. Det kan vara någon förfader av objektet eller våra webbläsarfönster om vi passerar null. Objekt anger också ett tröskelvärde som kan variera från 0 till 1, och berättar för oss vad som procent av målet synlighet observatören återuppringning bör verkställas, med 0 som betyder så snart som ännu en pixel är synliga och 1 vilket betyder att hela elementet måste vara synlig.

Och sedan, när du har skapat Korsningen Observatör, vi koppla det till våra element med hjälp av observera metod.

funktion createObserver() {
const alt = {
rot: null,
tröskeln: “0”
};
const observatör = new IntersectionObserver(handleIntersect, alternativ).
observatör.observera(el);
}

Steg 5: Registrera direktiv

Att använda vår nyskapade direktiv, vi måste först registrera det. Det finns två sätt att göra det: globalt (som finns överallt i app) eller lokalt (på en viss komponent nivå).

Global registrering

För global registrering, importerar vi våra direktiv och använda de Vue.direktiv metod för att passera det namn vi vill uppmana våra direktiv och direktiv själv. Det tillåter oss att lägga till en v-lazyload attribut till ett element i vår kod.

// main.js
importera Vue från “vue”;
import från App “./Appen”;
importera LazyLoadDirective från “./direktiv/LazyLoadDirective”;

Vue.config.productionTip = false;

Vue.direktiv(“lazyload”, LazyLoadDirective);

nya Vue({
el: “#app”,
komponenter: { App },
mall: “<App/>”
});
Lokal registrering

Om vi vill använda våra direktiv är det endast i en specifik komponent och begränsa tillgång till det vi kan registrera direktiv lokalt. För att göra detta, behöver vi importera direktiv inuti den komponent som kommer att använda den och registrera det i direktiven objekt. Som kommer att ge oss möjligheten att lägga till en v-lazyload attribut till ett element i denna komponent.

importera LazyLoadDirective från “./direktiv/LazyLoadDirective”;

export standard {
direktiv: {
lazyload: LazyLoadDirective
}
}

Steg 6: Använd en direktivet om ImageItem komponent

Nu när våra direktiv har registrerats, kan vi använda den genom att lägga till v-lazyload på det överordnade elementet som bär vår bild (figur tag i vårt fall).

<mall>
<figur v-lazyload class=”image__wrapper”>
<ImageSpinner
class=”image__spinner”
/>
<img
class=”image__post”
:data-url=”källan”
alt=”random image”
>
</bild>
</template>

Webbläsare-Stöd

Vi skulle vara försumlig om vi inte gör en notering om webbläsaren stöd. Även om Korsningen Observera API är det inte stöds av alla webbläsare, det täcker 73% av användarna (i skrivande stund).

Denna webbläsare har stöd för data är från Caniuse, som har mer i detalj. En rad anger att webbläsaren stöder den funktionen på denna version och upp.

Skrivbordet

ChromeOperaFirefoxIEEdgeSafari
58 45 55 Inga 16 Inga

Mobil / Surfplatta

iOS SafariOpera MobileOpera MiniAndroidAndroid ChromeAndroid Firefox
Inga 46 Inga 67 69 62

Inte dåligt. Inte dåligt alls.

Men! Med tanke på att vi vill visa bilder för alla användare (kom ihåg att använda data-url förhindrar att bilden laddas på alla), behöver vi lägga ytterligare en bit till våra direktiv. Speciellt behöver vi för att kontrollera om webbläsaren har stöd för Korsningen Observatör, och det är det inte, brand loadImage istället. Detta kommer att vara vår räddningsplanka.

(om fönstret[“IntersectionObserver”]) {
createObserver();
} else {
loadImage();
}

Sammanfattning

Lazy loading-bilder kan avsevärt förbättra prestanda eftersom det tar sida vikt hogged av bilder och laddar dem i först när du faktiskt behöver dem.

För dem som fortfarande inte övertygad om att det är värt att spela med lazy loading, här är några råa siffror från den enkla exempel som vi har använt. Listan innehåller 11 artiklar med en bild per artikel. Det är en summa av 11 bilder (matte!). Det är inte som att det är massor av bilder men vi kan fortfarande arbeta med det.

Här är vad vi får rivande alla 11 bilder utan lazy loading på en 3G-anslutning:

11 bild förfrågningar om att bidra till en övergripande sida för storlek på 3,2 MB. Glans.

Här är samma sida att sätta lazy loading till uppgift:

Säga vad? Bara en begäran för en bild. Vår sida är nu 1,4 MB. Vi sparat 10 förfrågningar och minskat storleken på sidan med 56%.

Det är ett enkelt och isolerade exempel? Ja, men siffrorna tala för sig själva. Förhoppningsvis hittar du lazy loading-ett effektivt sätt att bekämpa slaget mot sidan svälla och att detta specifika sätt att använda Vue med Korsningen Observatör kommer väl till pass.