Lat Legge Bilder med Vue.js Direktiver og Kryss Observatør

0
14

Når jeg tenker på web ytelse, er den første tingen som kommer til hjernen min er hvordan bildene er generelt de siste elementene som vises på en side. I dag, kan bildene bli et stort problem når det kommer til ytelse, noe som er uheldig siden den hastigheten en nettside lastes har en direkte innvirkning på brukerne klarer å gjøre det de kom til siden for å gjøre det på (tror samtale priser).

Veldig nylig, Rahul Nanwani skrev opp en omfattende guide på late lasting av bilder. Jeg vil dekke det samme tema, men på en annen måte: ved hjelp av data attributter, Kryss Observatør og tilpasset direktiver i Vue.js.

Hva dette vil i utgangspunktet gjøre er å tillate oss å løse to ting:

  1. Store src av bildet vi ønsker å bruke uten å legge det i første omgang.
  2. Oppdage når bildet blir synlig for brukeren og utløse forespørsel om å laste bildet.

Samme grunnleggende lat legger i begrepet, men en annen måte å gå om det.

Jeg opprettet et eksempel, basert på et eksempel beskrevet av Benjamin Taylor i sin blogg. Den inneholder en liste av tilfeldige artikler hver og en som inneholder en kort beskrivelse, bilde og en link til kilden av artikkelen. Vi vil gå gjennom prosessen med å lage en komponent som er ansvarlig for å vise at listen, rendering en artikkel, og lat legge til bildet for en bestemt artikkel.

La oss få lat! Eller i det minste bryte denne komponenten ned bit-for-bit.

Trinn 1: Opprette ImageItem komponent i Vue

La oss begynne med å lage en komponent som vil vise et bilde (men ikke lat legge involvert ennå). Vi vil kalle denne filen ImageItem.vue. I komponent-malen, vil vi bruke en figur-brikke som inneholder vår bilde — bilde-koden i seg selv vil motta src attributt som peker til kilden URL for bildefilen.

<mal>
<figur class=”image – __ – wrapper”>
<img
class=”image__elementet”
:src=”kilden”
alt=”random bilde”
>
</figure>
</template>

I skriptet delen av komponenten, vi får prop kilde som vi vil bruke for src-url-adressen til bildet vi viser.

eksport standard {
navn: “ImageItem”,
rekvisittar: {
kilde: {
type: String,
nødvendig: true
}
}
};

Alt dette er helt greit og vil gjengi bildet som normalt er. Men, hvis vi forlater det her, bildet lastes rett bort uten å vente for hele komponent for å være gjengi. Det er ikke det vi vil, så la oss gå til neste trinn.

Trinn 2: Hindre at bildet blir lastet inn når komponenten er opprettet

Det kan høres litt morsomt at vi ønsker å hindre noe fra å lastes når vi ønsker å vise det, men dette er i ferd med å legge det til rett tid heller enn å blokkere det, på ubestemt tid. For å hindre at bildet blir lastet inn, vi må bli kvitt src attributt fra i img-koden. Men, vi har fortsatt behov for å lagre den et sted, slik at vi kan gjøre bruk av det når vi ønsker det. Et godt sted å oppbevare denne informasjonen i en data – attributtet. Disse gir oss mulighet til å lagre informasjon på standard, semantisk HTML-elementer. Faktisk, kan du allerede være vant til å bruke dem som JavaScript-velgere.

I dette tilfellet, de er perfekt for våre behov!

<!–ImageItem.vue–>
<mal>
<figur class=”image – __ – wrapper”>
<img
class=”image__elementet”
:data-url=”kilde” – / / yay for data egenskaper!
alt=”random bilde”
>
</figure>
</template>

Med det, vår bildet lastes ikke inn fordi det er ingen kilde URL for å trekke fra.

Det er en god start, men fortsatt ikke helt hva vi ønsker. Vi ønsker å bruke vår bilde under spesifikke forhold. Vi kan be om å få bildet til å laste inn ved å erstatte src-attributt med bilde kilde URL holdt i vår data-url-attributtet. Det er den enkle delen. Den virkelige utfordringen er å finne ut når du kan erstatte den med den faktiske kilden.

Vårt mål er å feste lasten til brukerens sted på skjermen. Så, når brukeren blar til et punkt hvor bildet kommer til syne, det er der den er lastet inn.

Hvordan kan vi oppdage hvis bildet er i vise eller ikke? Det er vårt neste skritt.

Trinn 3: Oppdage når bildet er synlig for brukeren

Du har kanskje erfaring med bruk av JavaScript for å avgjøre når et element er i sikte. Du kan også ha erfaring svingete opp med litt vrient script.

For eksempel, vi kan bruke hendelser og event handlers å oppdage bla posisjon, offset-verdi, element høyde, og viewport høyde, og deretter beregne om et bilde er i viewport eller ikke. Men som allerede høres vrient ut, ikke sant?

Men det kan bli verre. Dette har direkte konsekvenser på ytelse. De beregninger ville bli sparket på hver bla event. Og enda verre: tenk deg et par dusin bilder, som hver har for å beregne om det er synlig eller ikke på hver bla event. Galskap!

Skjæringspunktet Observatør til unnsetning! Dette gir en veldig effektiv måte å oppdage hvis et element er synlig i vinduet. Spesielt, det tillater deg å konfigurere en tilbakeringing som utløses når ett element kalt target — krysser med enten enheten vindu eller en bestemt element.

Så, hva vi trenger å gjøre for å bruke det? Et par ting:

  • opprette et nytt kryss, observatør
  • se elementet vi ønsker å lat belastning for synlighet endringer
  • legg til elementet, når elementet er i viewport (ved å erstatte src med våre data-url)
  • stoppe å se for synlighet endringer (unobserve) etter lasten er ferdig

Vue.js har tilpasset direktiver for å bryte all denne funksjonaliteten sammen og bruke det når vi trenger det, så mange ganger som vi har behov for det. Å sette som å bruke, er vårt neste skritt.

Trinn 4: Lag en Vue tilpasset direktiv

Hva er en tilpasset direktiv? Vue dokumentasjonen beskriver det som en måte å få lavt nivå DOM tilgang på elementer. Hvis du For eksempel endrer en egenskap av en bestemt DOM-element som i vårt tilfelle, kan være å endre kilde-attributtet for en img-elementet. Perfekt!

Vi vil bryte dette ned på et øyeblikk, men her er hva vi ser på som langt som til koden:

eksport standard {
det er satt inn: el => {
funksjonen loadImage() {
const imageElement = Array.fra(el.barn).finn(
el => el.nodeName === “IMG”
);
hvis (imageElement) {
imageElement.addEventListener(“load”, () => {
setTimeout(() => el.classList.legge til(“loaded”), 100);
});
imageElement.addEventListener(“feil”, () => konsollen.logg(“feil”));
imageElement.src = imageElement.dataset.url adressen;
}
}

funksjonen handleIntersect(oppføringene, observatør) {
oppføringer.forEach(entry => {
hvis (oppføring.isIntersecting) {
loadImage();
observatør.unobserve(el);
}
});
}

funksjonen createObserver() {
const valg = {
root: null,
terskel: “0”
};
const observatør = new IntersectionObserver(handleIntersect, valg);
observatør.observere(el);
}
hvis (vinduet[“IntersectionObserver”]) {
createObserver();
} else {
loadImage();
}
}
};

OK, la oss takle denne trinn-for-trinn.

Det kroken-funksjonen gjør det mulig for oss å skyte en tilpasset logikk på et bestemt øyeblikk av en bundet element livssyklus. Vi bruker den satt kroken fordi det er kalt når bundet element har blitt satt inn i den overordnede node (dette garanterer den overordnede node er til stede). Siden vi ønsker å observere synlighet av et element i relasjon til sine foreldre (eller noen stamfar), vi trenger å bruke kroken.

eksport standard {
det er satt inn: el => {

}
}

Den loadImage funksjon er man ansvarlig for å erstatte src verdi med data-url. I det, har vi tilgang til våre element (el), som er hvor vi anvende direktivet. Vi kan hente ut img fra dette elementet.

Neste, vi sjekke om bildet eksisterer og, hvis den gjør det, vil vi legge til en lytter som vil brann en callback-funksjon når innlastingen er ferdig. Som innb. vil være ansvarlig for å skjule den spinner og legge til animasjon (fade-effekt) til bildet ved hjelp av en CSS-klasse. Vi har også legge til en andre lytteren som vil bli kalt i tilfelle at URL-en ikke klarer å laste inn.

Til slutt, vi erstatte src av våre img-element med kilden URL-en til bilde og vise det!

funksjonen loadImage() {
const imageElement = Array.fra(el.barn).finn(
el => el.nodeName === “IMG”
);
hvis (imageElement) {
imageElement.addEventListener(“load”, () => {
setTimeout(() => el.classList.legge til(“loaded”), 100);
});
imageElement.addEventListener(“feil”, () => konsollen.logg(“feil”));
imageElement.src = imageElement.dataset.url adressen;
}
}

Vi bruker Skjæringspunktet Observer handleIntersect funksjon, som er ansvarlig for å skyte loadImage når visse betingelser er oppfylt. Spesielt, det er avfyrt når Skjæringspunktet Observatør oppdager at elementet kommer inn i vindu eller en forelder komponent element.

Funksjonen har tilgang til oppføringer, som er en samling av alle elementer som er sett av observatøren og observatør i seg selv. Vi iterere gjennom registreringer og kontroller om en enkelt oppføring blir synlig for våre bruker med isIntersecting — og brann loadImage funksjonen hvis det er. Når bildet er forespurt, vil vi unobserve elementet (fjerne det fra the observer ‘ s watch-listen), noe som hindrer at bildet blir lastet inn på nytt. Og igjen. Og igjen. Og…

funksjonen handleIntersect(oppføringene, observatør) {
oppføringer.forEach(entry => {
hvis (oppføring.isIntersecting) {
loadImage();
observatør.unobserve(el);
}
});
}

Den siste biten er createObserver funksjon. Denne fyren er ansvarlig for å lage vår Skjæringspunktet Observatør og feste det til vårt rette element. Den IntersectionObserver constructor aksepterer en tilbakeringing (vår handleIntersect funksjon) som utløses når den observerte element passerer den angitte grensen og valg objekt som bærer vår observatør valg.

Sett av alternativer objekt, bruker root som vår referanse objekt, som vi bruker til å base synligheten av våre sett element. Det kan være hvilken som helst stamfar til objektet eller vår nettleser vindu hvis vi passerer null. Objektet også angir en grenseverdi som kan variere fra 0 til 1, og forteller oss hva som prosent av målet er synlighet observatør innb. skal utføres, med 0 som betyr så snart som selv en piksel er synlig, og 1 betyr hele element må være synlig.

Og så, etter å opprette Skjæringspunktet Observatør, legger vi det til vår elementet ved hjelp av den observere metode.

funksjonen createObserver() {
const valg = {
root: null,
terskel: “0”
};
const observatør = new IntersectionObserver(handleIntersect, valg);
observatør.observere(el);
}

Trinn 5: Registrering av direktiv

Å bruke vår nyopprettede direktiv, vi må først registrere det. Det er to måter å gjøre det: globalt (tilgjengelig overalt i app) eller lokalt (på en bestemt komponent nivå).

Global registrering

For global registrering, vi importerer våre direktivet og bruk Vue.direktivet metode for å passere navn ønsker vi å ringe våre direktivet og direktivet i seg selv. Det tillater oss å legge en v-lazyload attributt til et element i vår kode.

// main.js
import Vue fra “vue”;
import-Appen fra “./App”;
import LazyLoadDirective fra “./direktiver/LazyLoadDirective”;

Vue.config.productionTip = false;

Vue.direktiv(“lazyload”, LazyLoadDirective);

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

Hvis vi ønsker å bruke vår direktivet bare i en bestemt komponent og begrense tilgang til det, kan vi registrere direktivet lokalt. For å gjøre det, trenger vi å importere direktivet inne i den komponenten som skal bruke det og registrere den i direktivene objekt. Det vil gi oss muligheten til å legge til en v-lazyload attributt til et element i denne komponenten.

import LazyLoadDirective fra “./direktiver/LazyLoadDirective”;

eksport standard {
direktiver: {
lazyload: LazyLoadDirective
}
}

Trinn 6: Bruk et direktiv på ImageItem komponent

Nå som vår direktivet har blitt registrert, kan vi bruke det ved å legge til v-lazyload på den overordnede element som bærer vårt bilde (figur tag i vårt tilfelle).

<mal>
<figur v-lazyload class=”image – __ – wrapper”>
<ImageSpinner
class=”image__spinner”
/>
<img
class=”image__elementet”
:data-url=”kilden”
alt=”random bilde”
>
</figure>
</template>

Nettleserstøtte

Vi ville være remiss hvis vi ikke lage et notat om nettleseren støtter. Selv om Skjæringspunktet Observere API-det er ikke støttes av alle nettlesere, betyr det dekker 73% av brukerne (i skrivende stund).

Denne nettleseren støtter data er fra Caniuse, som har mer detalj. Angir et nummer som nettleseren støtter funksjonen på denne versjonen, og opp.

Skrivebordet

ChromeOperaFirefoxIEEdgeSafari
58 45 55 Ingen 16 Ingen

Mobil / Nettbrett

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

Ikke dårlig. Ikke dårlig i det hele tatt.

Men! Å ha i tankene at vi ønsker å vise bilder til alle brukere (husk at bruk av data-url hindrer at bildet blir lagt i det hele tatt), må vi legge en mer stykket til vår direktivet. Spesielt trenger vi for å sjekke om nettleseren støtter Skjæringspunktet Observatør, og det er det ikke, brann loadImage i stedet. Dette vil være vår tilbakefall.

hvis (vinduet[“IntersectionObserver”]) {
createObserver();
} else {
loadImage();
}

Oppsummering

Lat legge bildene kan forbedre side ytelse fordi det tar side vekt hogged av bilder og laster dem bare når brukeren faktisk trenger dem.

For de som fortsatt ikke overbevist om det er verdt å spille med lazy lasting, her er noen rå tall fra den enkle eksempel vi har brukt. Listen inneholder 11 artikler med ett bilde per artikkel. Det er et total av 11 bilder (matematikk!). Det er ikke sånn at det er massevis av bilder, men vi kan fortsatt jobbe med det.

Her er hva vi får rending alle 11 bilder uten lat legge på en 3G-tilkobling:

11 bilde forespørsler bidra til en samlet side størrelse på 3,2 MB. Oomph.

Her er det samme side å sette lat legge til oppgaven:

Si hva? Bare en forespørsel til ett bilde. Vår side er nå 1.4 MB. Vi har spart 10 forespørsler og redusert siden størrelse med 56%.

Er det en enkel og isolert eksempel? Ja, men tallene fortsatt taler for seg selv. Forhåpentligvis vil du finne lat legge en effektiv måte å kjempe kampen mot siden bloat og at denne bestemte tilnærming ved hjelp av Vue med Kryss Observatør kommer i hendig.