useLazyLoad Composable
Reactive lazy loading for images and components with IntersectionObserver
Code
import { ref, onMounted, onUnmounted, type Ref } from 'vue';
export function useLazyLoad(
target: Ref<HTMLElement | null>,
options: IntersectionObserverInit = { rootMargin: '200px 0px' }
) {
const isLoaded = ref(false);
const isIntersecting = ref(false);
let observer: IntersectionObserver | null = null;
const loadContent = () => {
__TOKEN_18__ (isLoaded.value) return;
isLoaded.value = true;
// For images: set src from data-src
__TOKEN_20__ (target.value?.tagName === 'IMG') {
const img = target.value as HTMLImageElement;
__TOKEN_22__ (img.dataset.src) {
img.src = img.dataset.src;
img.removeAttribute('data-src');
}
}
};
const handleIntersect = ([entry]: IntersectionObserverEntry[]) => {
isIntersecting.value = entry.isIntersecting;
__TOKEN_24__ (entry.isIntersecting) {
loadContent();
observer?.unobserve(entry.target);
}
};
onMounted(() => {
__TOKEN_25__ (!target.value || !IntersectionObserver) {
loadContent();
return;
}
observer = new IntersectionObserver(handleIntersect, options);
observer.observe(target.value);
});
onUnmounted(() => {
__TOKEN_28__ (observer && target.value) {
observer.unobserve(target.value);
observer.disconnect();
}
});
return { isLoaded, isIntersecting, loadContent };
}
// Usage example
// const imgRef = ref<HTMLImageElement | null>(null);
// const { isLoaded } = useLazyLoad(imgRef);
// Template: <img ref="imgRef" data-src="/large-image.jpg" v-if="isLoaded">