IntersectionObserver
const observer = new IntersectionObserver(callback, {
root: null, // 根元素(默认视窗)
rootMargin: "0px", // 根元素边界扩展(类似 CSS margin)
threshold: 0.5, // 触发阈值(0-1 或数组 [0, 0.25, 1])
});
图片懒加载
const vLazy = (observer: IntersectionObserver) => {
return {
beforeMount: (el: HTMLImageElement, binding : DirectiveBinding) => {
el.classList.add("op-lazyload");
const { value } = binding;
el.dataset.origin = value;
observer.observe(el);
},
};
};
const lazyPlugin = {
install(app : App) {
const observer = new IntersectionObserver((entries) => {
entries.forEach((item) => {
if (item.isIntersecting) {
const el = item.target as HTMLImageElement;
el.src = el.dataset.origin as string;
el.classList.remove("op-lazyload");
observer.unobserve(el);
}
});
});
},
};
组件懒加载
import {
h,
defineAsyncComponent,
defineComponent,
ref,
onMounted,
AsyncComponentLoader,
Component,
} from 'vue';
type ComponentResolver = (component: Component) => void
export const lazyLoadComponentIfVisible = ({
// 目标组件加载函数
componentLoader,
// 目标组件加载时使用的占位组件
loadingComponent,
errorComponent,
delay,
timeout
}: {
componentLoader: AsyncComponentLoader;
loadingComponent: Component;
errorComponent?: Component;
delay?: number;
timeout?: number;
}) => {
let resolveComponent: ComponentResolver;
return defineAsyncComponent({
// the loader function
loader: () => {
return new Promise((resolve) => {
resolveComponent = resolve as ComponentResolver;
});
},
loadingComponent: defineComponent({
setup() {
const elRef = ref();
async function loadComponent() {
const component = await componentLoader()
resolveComponent(component)
}
onMounted(async() => {
if (!('IntersectionObserver' in window)) {
await loadComponent();
return;
}
const observer = new IntersectionObserver((entries) => {
if (!entries[0].isIntersecting) {
return;
}
observer.unobserve(elRef.value);
await loadComponent();
});
observer.observe(elRef.value);
});
return () => {
return h('div', { ref: elRef }, loadingComponent);
};
},
}),
delay,
errorComponent,
timeout,
});
};
使用
<script setup lang="ts">
import Loading from './components/Loading.vue';
import { lazyLoadComponentIfVisible } from './utils';
const LazyLoaded = lazyLoadComponentIfVisible({
componentLoader: () => import('./components/HelloWorld.vue'),
loadingComponent: Loading,
});
</script>
<template>
<LazyLoaded />
</template>