Vue3 实现高效图片懒加载与预览功能详解
一、图片懒加载的实现
1.1 原理简介
懒加载的核心原理是:
- 监听滚动事件:检测用户滚动页面时,哪些图片进入了可视区域。
- 动态加载图片:当图片进入可视区域时,动态地将图片的src属性从占位图替换为实际的图片URL。
1.2 实现步骤
- 创建Vue组件:
首先,创建一个名为LazyImage.vue的Vue组件。
   <template>
     <img :src="loaded ? realSrc : placeholder" @load="handleLoad" />
   </template>
   <script setup>
   import { ref, onMounted } from 'vue';
   const props = defineProps({
     src: String,
     placeholder: {
       type: String,
       default: 'path/to/placeholder.png'
     }
   });
   const loaded = ref(false);
   const realSrc = ref('');
   const handleLoad = () => {
     loaded.value = true;
   };
   const loadImage = () => {
     const img = new Image();
     img.onload = () => {
       realSrc.value = props.src;
     };
     img.src = props.src;
   };
   onMounted(() => {
     const observer = new IntersectionObserver((entries) => {
       entries.forEach((entry) => {
         if (entry.isIntersecting) {
           loadImage();
           observer.unobserve(entry.target);
         }
       });
     });
     observer.observe(document.querySelector('img'));
   });
   </script>
- 使用组件:
在其他Vue组件中引入并使用LazyImage组件。
   <template>
     <div>
       <LazyImage src="path/to/your/image.jpg" />
     </div>
   </template>
   <script setup>
   import LazyImage from './components/LazyImage.vue';
   </script>
二、图片预览功能的实现
2.1 原理简介
- 模态框展示:点击图片时,弹出一个模态框展示大图。
- 附加功能:在模态框中添加旋转、缩放等按钮,通过CSS和JavaScript实现相应的功能。
2.2 实现步骤
- 创建预览组件:
创建一个名为ImagePreview.vue的Vue组件。
   <template>
     <div v-if="visible" class="preview-modal">
       <div class="preview-content">
         <img :src="currentImage" :style="imageStyle" />
         <button @click="rotateImage">旋转</button>
         <button @click="closePreview">关闭</button>
       </div>
     </div>
   </template>
   <script setup>
   import { ref } from 'vue';
   const props = defineProps({
     images: Array,
     initialIndex: {
       type: Number,
       default: 0
     }
   });
   const visible = ref(false);
   const currentImage = ref('');
   const rotationAngle = ref(0);
   const openPreview = (index) => {
     visible.value = true;
     currentImage.value = props.images[index];
   };
   const closePreview = () => {
     visible.value = false;
     rotationAngle.value = 0;
   };
   const rotateImage = () => {
     rotationAngle.value = (rotationAngle.value + 90) % 360;
   };
   const imageStyle = computed(() => ({
     transform: `rotate(${rotationAngle.value}deg)`
   }));
   defineExpose({
     openPreview
   });
   </script>
   <style>
   .preview-modal {
     position: fixed;
     top: 0;
     left: 0;
     width: 100%;
     height: 100%;
     background-color: rgba(0, 0, 0, 0.5);
     display: flex;
     justify-content: center;
     align-items: center;
   }
   .preview-content img {
     max-width: 90%;
     max-height: 90%;
   }
   </style>
- 使用预览组件:
在其他Vue组件中引入并使用ImagePreview组件。
   <template>
     <div>
       <img v-for="(img, index) in images" :key="img" :src="img" @click="preview.openPreview(index)" />
       <ImagePreview ref="preview" :images="images" />
     </div>
   </template>
   <script setup>
   import { ref } from 'vue';
   import ImagePreview from './components/ImagePreview.vue';
   const images = ref(['path/to/image1.jpg', 'path/to/image2.jpg']);
   const preview = ref(null);
   </script>
三、结合懒加载与预览功能
- 修改LazyImage组件: 在LazyImage组件中添加点击事件,触发预览。
   <template>
     <img :src="loaded ? realSrc : placeholder" @load="handleLoad" @click="handleClick" />
   </template>
   <script setup>
   import { ref, onMounted } from 'vue';
   import { usePreview } from './usePreview'; // 假设这是预览功能的组合式API
   const props = defineProps({
     src: String,
     placeholder: {
       type: String,
       default: 'path/to/placeholder.png'
     }
   });
   const { loaded, realSrc, handleLoad, loadImage } = useLazyLoad(props);
   const { openPreview } = usePreview();
   const handleClick = () => {
     openPreview(props.src);
   };
   onMounted(() => {
     const observer = new IntersectionObserver((entries) => {
       entries.forEach((entry) => {
         if (entry.isIntersecting) {
           loadImage();
           observer.unobserve(entry.target);
         }
       });
     });
     observer.observe(document.querySelector('img'));
   });
   </script>
- 创建组合式API:
创建useLazyLoad和usePreview两个组合式API,分别用于懒加载和预览功能。
   // useLazyLoad.js
   import { ref } from 'vue';
   export function useLazyLoad(props) {
     const loaded = ref(false);
     const realSrc = ref('');
     const handleLoad = () => {
       loaded.value = true;
     };
     const loadImage = () => {
       const img = new Image();
       img.onload = () => {
         realSrc.value = props.src;
       };
       img.src = props.src;
     };
     return { loaded, realSrc, handleLoad, loadImage };
   }
   // usePreview.js
   import { ref } from 'vue';
   export function usePreview() {
     const visible = ref(false);
     const currentImage = ref('');
     const rotationAngle = ref(0);
     const openPreview = (image) => {
       visible.value = true;
       currentImage.value = image;
     };
     const closePreview = () => {
       visible.value = false;
       rotationAngle.value = 0;
     };
     const rotateImage = () => {
       rotationAngle.value = (rotationAngle.value + 90) % 360;
     };
     return { visible, currentImage, rotationAngle, openPreview, closePreview, rotateImage };
   }