Carga diferida de imágenes en Svelte
Una forma sencilla de mejorar la velocidad de un sitio web es descargar imágenes únicamente cuando sean necesarias, que sería cuando ingresen a la ventana gráfica. Esta técnica de “carga diferida” existe desde hace algún tiempo y hay muchos tutoriales excelentes sobre cómo implementarla.
Pero incluso con todos los recursos disponibles, la implementación de la carga diferida puede verse diferente según el proyecto en el que esté trabajando o el marco que esté utilizando. En este artículo, usaré la API de Intersection Observer junto con el onLoad
evento para cargar imágenes de forma diferida con el marco Svelte JavaScript.
Consulte la introducción de Tristram Tolliday a Svelte si es nuevo en el marco.
Trabajamos con un ejemplo de la vida real.
Preparé este enfoque mientras probaba la velocidad en una aplicación Svelte and Sapper en la que trabajo, Shop Ireland. Uno de nuestros objetivos es hacerlo lo más rápido posible. Llegamos a un punto en el que la página de inicio estaba sufriendo un impacto en el rendimiento porque el navegador estaba descargando un montón de imágenes que ni siquiera estaban en la pantalla, por lo que, naturalmente, optamos por cargarlas de forma diferida.
Svelte ya es bastante rápido porque todo el código se compila de antemano. Pero una vez que incorporamos la carga diferida para las imágenes, las cosas realmente empezaron a acelerarse.
Esto es en lo que vamos a trabajar juntos. No dudes en obtener el código final de esta demostración de GitHub y seguir leyendo para obtener una explicación de cómo funciona.
Aquí es donde terminaremos al final:
Iniciamos rápidamente Svelte
Es posible que ya tengas una aplicación Svelte que te gustaría usar, pero si no, comencemos un nuevo proyecto Svelte y trabajamos en él localmente. Desde la línea de comando:
npx degit sveltejs/template my-svelte-projectcd my-svelte-projectnpm installnpm run dev
Ahora deberías tener una aplicación para principiantes ejecutándose en http://localhost:5000
.
Agregar la carpeta de componentes
La demostración inicial de Svelte tiene un App.svelte
archivo pero aún no tiene componentes. Configuramos los componentes que necesitamos para esta demostración. No hay una carpeta de componentes, así que creemos una en la src
carpeta. Dentro de esa carpeta, cree una Image
carpeta; Aquí contendrá nuestros componentes para esta demostración.
Vamos a hacer que nuestros componentes hagan dos cosas. Primero, verificará cuándo ingresa una imagen a la ventana gráfica. Luego, cuando ingresa una imagen, los componentes esperarán hasta que el archivo de imagen se haya cargado antes de mostrarlo.
El primer componente será un IntersectionObserver
que envuelve al segundo componente, un ImageLoader
. Lo que me gusta de esta configuración es que permite que cada componente se centre en hacer una cosa en lugar de intentar agrupar un montón de operaciones en un solo componente.
Comenzamos con el IntersectionObserver
componente.
Observando la intersección
Nuestro primer componente será una implementación funcional de la API de Intersection Observer. El Intersection Observer es algo bastante complejo, pero lo esencial es que observa un elemento secundario y nos informa cuando ingresa al cuadro delimitador de su padre. Por lo tanto, las imágenes: pueden ser hijas de algún elemento principal y podemos recibir un aviso cuando se desplazan a la vista.
Si bien definitivamente es una gran idea para familiarizarse con los entresijos de la API de Intersection Observer (y Travis Almand tiene un excelente artículo sobre ella), vamos a utilizar un útil componente Svelte que Rich Harris preparó para esvelte.dev.
Primero configuraremos esto antes de profundizar en qué hace exactamente. Cree un nuevo IntersectionObserver.svelte
archivo y suéltelo en la src/components/Image
carpeta. Aquí es donde definiremos el componente con el siguiente código:
script import { onMount } from 'svelte';
export let once = false; export let top = 0; export let bottom = 0; export let left = 0; export let right = 0;
let intersecting = false; let container;
onMount(() = { if (typeof IntersectionObserver !== 'undefined') { const rootMargin = `${bottom}px ${left}px ${top}px ${right}px`;
const observer = new IntersectionObserver(entries = { intersecting = entries[0].isIntersecting; if (intersecting once) { observer.unobserve(container); } }, { rootMargin });
observer.observe(container); return () = observer.unobserve(container); }
// The following is a fallback for older browsers function handler() { const bcr = container.getBoundingClientRect();
intersecting = ( (bcr.bottom + bottom) 0 (bcr.right + right) 0 (bcr.top - top) window.innerHeight (bcr.left - left) window.innerWidth );
if (intersecting once) { window.removeEventListener('scroll', handler); } }
window.addEventListener('scroll', handler); return () = window.removeEventListener('scroll', handler); });/script
style div { width: 100%; height: 100%; }/style
div bind_this={container} slot {intersecting}/slot/div
Podemos usar este componente como envoltorio alrededor de otros componentes, y determinará por nosotros si el componente envuelto se cruza con la ventana gráfica.
Si está familiarizado con la estructura de los componentes de Svelte, verá que sigue un patrón que comienza con scripts, pasa a estilos y luego termina con marcado. Establece algunas opciones que podemos pasar, incluida una once
propiedad, junto con valores numéricos para las distancias superior, derecha, inferior e izquierda desde el borde de la pantalla que define el punto donde comienza la intersección.
Ignoraremos las distancias y en su lugar haremos uso de la once
propiedad. Esto asegurará que las imágenes solo se cargan una vez, cuando ingresan a la ventana gráfica.
La lógica principal del componente está dentro de la onMount
sección. Esto configura nuestro observador, que se utiliza para nuestro elemento de verificación y determina si se “interseca” con el área visible de la pantalla.
Para navegadores más antiguos, también adjunte un evento de desplazamiento para verificar si el elemento es visible a medida que nos desplazamos, y luego eliminará este oyente si hemos determinado que es viable y eso once
es true
.
Cargando las imágenes
Usemos nuestro IntersectionObserver
componente para cargar imágenes condicionalmente envolviéndolo alrededor de un ImageLoader
componente. Nuevamente, este es el componente que recibe una notificación IntersectionOberserver
para saber que es hora de cargar una imagen.
Eso significa que necesitaremos un nuevo archivo de componente en formato components/Image
. Llamémoslo ImageLoader.svelte
. Aquí está el código que queremos en él:
script export let src export let alt
import IntersectionObserver from './IntersectionObserver.svelte' import Image from './Image.svelte' /script
IntersectionObserver once={true} let_intersecting={intersecting} {#if intersecting} Image {alt} {src} / {/if}/IntersectionObserver
Este componente requiere algunos accesorios relacionados con la imagen ( src
y alt
) que usaremos para crear el marcado real de una imagen. Observe que estamos importando dos componentes en la sección de scripts, incluido el que IntersectionObserver
acabamos de crear y otro llamado Image
que aún no hemos creado, pero al que llegaremos en un momento.
Se IntersectionObserver
pone a trabajar actuando como un voltorio alrededor del componente que pronto se creará Image
. Mira esas propiedades en él. Estamos configurando once
para true
que la imagen solo se cargue la primera vez que la veamos.
Luego hacemos uso de los accesorios de tragamonedas de Svelte. ¿Que son esos? Cubramos eso a continuación.
Valores de propiedad de ranura
Los componentes de envoltura, como el nuestro, IntersectionObserver
son útiles para pasar accesorios a los niños que contiene. Svelte nos ofrece algo llamado accesorios de tragamonedas para que eso suceda.
En nuestro IntersectionObserver
componente es posible que hayas anotado esta línea:
slot {intersecting}/slot
Esto es pasar el accesorio de intersección a cualquier componente que le demos. En este caso, nuestro componente ImageLoader recibe el accesorio cuando usa el contenedor. Accedemos al accesorio usando let_intersecting={intersecting} así:
IntersectionObserver once={true} let_intersecting={intersecting}
Luego podemos usar el valor de intersección para determinar cuándo es el momento de cargar un Image
componente. En este caso, usamos una condición si para verificar cuándo es el momento de comenzar:
IntersectionObserver once={true} let_intersecting={intersecting} {#if intersecting} Image {alt} {src} / {/if}/IntersectionObserver
Si se produce la intersección, Image
se carga y recibe los accesorios alt
y src
. Puedes aprender un poco más sobre los accesorios de las tragamonedas en este tutorial de Svelte.
Ahora tenemos el código implementado para mostrar un Image
componente cuando se desplaza por la pantalla. Finalmente comenzamos a construir el componente.
Mostrando imágenes al cargar
Sí, lo has adivinado: agregamos un Image.svelte
archivo a la components/Image
carpeta de nuestro Image
componente. Este es el componente que recibe nuestros alt
accesorios src
y los coloca en un img
elemento.
Aquí está el código del componente:
script export let src export let alt
import { onMount } from 'svelte'
let loaded = false let thisImage
onMount(() = { thisImage.onload = () = { loaded = true } })
/script
style img { height: 200px; opacity: 0; transition: opacity 1200ms ease-out; } img.loaded { opacity: 1; }/style
img {src} {alt} class:loaded bind_this={thisImage} /
De buenas a primeras, estamos recibiendo los alt
accesorios y src
antes de definir dos nuevas variables: loaded
para almacenar si la imagen se ha cargado o no, y thisImage
para almacenar una referencia al propio elemento DOM img.
También estamos usando un método útil llamado Svelte onMount
. Esto nos brinda una forma de llamar funciones una vez que un componente se ha representado en el DOM. En este caso, configuramos una devolución de llamada para thisImage.onload
. En términos sencillos, eso significa que se ejecuta cuando la imagen ha terminado de cargarse y establecerá la loaded
variable en un true
valor.
Usaremos CSS para revelar la imagen y desvanecerla a la vista. Activemos opacity: 0
las imágenes para que inicialmente sean invisibles, aunque técnicamente están en la página. Luego, cuando se cruzan con la ventana gráfica y ImageLoader
otorgan permiso para cargar la imagen, configuraremos la imagen en opacidad total. Podemos hacer que sea una transición suave estableciendo la transition
propiedad en la imagen. La demostración establece el tiempo de transición en 1200 ms, pero puede acelerarlo o ralentizarlo según sea necesario.
Eso nos lleva a la última línea del archivo, que es el marcado de un img
elemento.
img {src} {alt} class:loaded bind_this={thisImage} /
Esto se utiliza class:loaded
para aplicar condicionalmente una .loaded
clase si la variable cargada es true
. También utiliza el bind:this
método para asociar este elemento DOM con la thisImage
variable.
Carga diferida nativa
Si bien la compatibilidad con la carga diferida nativa en los navegadores casi está aquí, aún no es compatible con todas las versiones estables actuales. Aún podemos agregarle soporte mediante una simple verificación de capacidad.
En nuestro ImageLoader.svelte
archivo podemos incorporar la onMount
función y, dentro de ella, verificar si nuestro navegador admite la carga diferida.
import { onMount } from 'svelte'let nativeLoading = false// Determine whether to bypass our intersecting checkonMount(() = { if ('loading' in HTMLImageElement.prototype) { nativeLoading = true }})
Luego ajustamos nuestra if
condición para incluir este nativeLoading
booleano.
{#if intersecting || nativeLoading} Image {alt} {src} /{/if}
Por último, en Image.svelte
, le decimos a nuestro navegador que utilice la carga diferida agregando elementos loading="lazy"
al img
elemento.
img {src} {alt} class:loaded bind_this={thisImage} /
Esto permitirá que los navegadores modernos y futuros omitan nuestro código y se encarguen de la carga diferida de forma nativa.
¡Conectémoslo todo!
Muy bien, es hora de utilizar nuestro componente. Abra el App.svelte
archivo y coloque el siguiente código para importar nuestro componente y usarlo:
script import ImageLoader from './components/Image/ImageLoader.svelte';/script
ImageLoader src="OUR_IMAGE_URL" alt="Our image"/ImageLoader
Aquí está la demostración una vez más:
Y recuerde que puede descargar el código completo de esta demostración en GitHub. Si desea ver esto funcionando en un sitio de producción, consulte mi proyecto Shop Ireland. La carga diferida se utiliza en la página de inicio, las páginas de categorías y las páginas de búsqueda para ayudar a acelerar las cosas. ¡Espero que te resulte útil para tus propios proyectos Svelte!
Deja un comentario