Vue3实战:构建现代化图片画廊应用 | 前端开发教程

2025-08-14 0 736

发布日期:2023年10月15日

一、项目概述

本教程将带领大家使用Vue3的Composition API开发一个功能完整的图片画廊应用。该应用包含以下核心功能:

  • 响应式图片网格布局
  • 图片分类筛选
  • 大图预览模式
  • 平滑过渡动画
  • 图片懒加载

通过这个项目,您将掌握Vue3的核心开发模式,并学习到实际项目中的最佳实践。

二、环境准备

确保已安装以下环境:

Node.js ≥ 16.0
npm ≥ 8.0
Vue CLI 5.x

创建Vue3项目:

vue create vue3-gallery
cd vue3-gallery

三、核心功能实现

1. 项目结构设计

采用模块化组件结构:

src/
├── components/
│   ├── GalleryGrid.vue    # 图片网格组件
│   ├── GalleryItem.vue    # 单个图片项
│   ├── Lightbox.vue       # 大图预览组件
│   └── CategoryFilter.vue # 分类筛选组件
├── composables/           # 组合式函数
│   └── useGallery.js      # 画廊业务逻辑
├── assets/                # 静态资源
└── App.vue                # 根组件

2. 组合式API实现业务逻辑

创建useGallery.js组合式函数:

import { ref, computed } from 'vue'

export default function useGallery(initialImages) {
    const images = ref(initialImages)
    const activeCategory = ref('all')
    const lightboxOpen = ref(false)
    const currentImageIndex = ref(0)

    // 计算属性:过滤后的图片
    const filteredImages = computed(() => {
        return activeCategory.value === 'all' 
            ? images.value 
            : images.value.filter(img => img.category === activeCategory.value)
    })

    // 打开大图预览
    const openLightbox = (index) => {
        currentImageIndex.value = index
        lightboxOpen.value = true
    }

    // 切换分类
    const setCategory = (category) => {
        activeCategory.value = category
    }

    return {
        images,
        filteredImages,
        activeCategory,
        lightboxOpen,
        currentImageIndex,
        openLightbox,
        setCategory
    }
}

3. 图片网格组件实现

GalleryGrid.vue组件:

<template>
    <div class="gallery-grid">
        <CategoryFilter 
            :categories="categories"
            :activeCategory="activeCategory"
            @select-category="setCategory"
        />
        
        <TransitionGroup name="fade" tag="div" class="grid-container">
            <GalleryItem
                v-for="(image, index) in filteredImages"
                :key="image.id"
                :image="image"
                @click="openLightbox(index)"
            />
        </TransitionGroup>
    </div>
</template>

<script setup>
import { computed } from 'vue'
import GalleryItem from './GalleryItem.vue'
import CategoryFilter from './CategoryFilter.vue'
import useGallery from '../composables/useGallery'

const props = defineProps({
    initialImages: {
        type: Array,
        required: true
    }
})

const { 
    filteredImages, 
    activeCategory, 
    openLightbox, 
    setCategory 
} = useGallery(props.initialImages)

const categories = computed(() => {
    const allCategories = props.initialImages.map(img => img.category)
    return ['all', ...new Set(allCategories)]
})
</script>

4. 大图预览组件实现

Lightbox.vue组件:

<template>
    <Transition name="lightbox">
        <div v-if="isOpen" class="lightbox-overlay" @click.self="close">
            <button class="close-btn" @click="close">×</button>
            <img 
                :src="currentImage.src" 
                :alt="currentImage.alt"
                class="lightbox-image"
            />
            <div class="lightbox-nav">
                <button @click="prevImage" :disabled="isFirst">❮</button>
                <span>{{ currentIndex + 1 }} / {{ totalImages }}</span>
                <button @click="nextImage" :disabled="isLast">❯</button>
            </div>
        </div>
    </Transition>
</template>

<script setup>
import { computed } from 'vue'

const props = defineProps({
    isOpen: Boolean,
    images: Array,
    currentIndex: Number
})

const emit = defineEmits(['close', 'change-image'])

const currentImage = computed(() => props.images[props.currentIndex])
const totalImages = computed(() => props.images.length)
const isFirst = computed(() => props.currentIndex === 0)
const isLast = computed(() => props.currentIndex === totalImages.value - 1)

const close = () => emit('close')
const prevImage = () => emit('change-image', props.currentIndex - 1)
const nextImage = () => emit('change-image', props.currentIndex + 1)
</script>

四、高级功能扩展

1. 图片懒加载实现

修改GalleryItem.vue实现懒加载:

<template>
    <div class="gallery-item" ref="itemRef">
        <img 
            v-if="isVisible"
            :src="image.thumbnail" 
            :alt="image.alt"
            @click="$emit('click')"
        />
        <div v-else class="placeholder"></div>
    </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue'

const props = defineProps({
    image: {
        type: Object,
        required: true
    }
})

const itemRef = ref(null)
const isVisible = ref(false)

const observer = new IntersectionObserver((entries) => {
    if (entries[0].isIntersecting) {
        isVisible.value = true
        observer.unobserve(itemRef.value)
    }
})

onMounted(() => {
    observer.observe(itemRef.value)
})

onUnmounted(() => {
    observer.disconnect()
})
</script>

2. 添加过渡动画

App.vue中添加全局过渡样式:

<style>
.fade-move,
.fade-enter-active,
.fade-leave-active {
    transition: all 0.5s ease;
}

.fade-enter-from,
.fade-leave-to {
    opacity: 0;
    transform: scale(0.9);
}

.fade-leave-active {
    position: absolute;
}

.lightbox-enter-active,
.lightbox-leave-active {
    transition: opacity 0.3s ease;
}

.lightbox-enter-from,
.lightbox-leave-to {
    opacity: 0;
}
</style>

五、项目优化与部署

1. 性能优化建议:

  • 使用v-lazy指令实现图片懒加载
  • 对大图进行压缩和WebP格式转换
  • 实现虚拟滚动处理大量图片

2. 部署到Vercel:

npm install -g vercel
vercel

六、总结与扩展

通过本项目,我们学习了:

  1. Vue3组合式API的实际应用
  2. 组件化开发的最佳实践
  3. 过渡动画的实现技巧
  4. 性能优化的多种手段

扩展方向建议:

  • 集成Firebase实现用户上传功能
  • 添加图片收藏功能
  • 实现SSR版本提升SEO效果
Vue3实战:构建现代化图片画廊应用 | 前端开发教程
收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

淘吗网 vue3 Vue3实战:构建现代化图片画廊应用 | 前端开发教程 https://www.taomawang.com/web/vue3/832.html

下一篇:

已经没有下一篇了!

常见问题

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务