发布日期: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
六、总结与扩展
通过本项目,我们学习了:
- Vue3组合式API的实际应用
- 组件化开发的最佳实践
- 过渡动画的实现技巧
- 性能优化的多种手段
扩展方向建议:
- 集成Firebase实现用户上传功能
- 添加图片收藏功能
- 实现SSR版本提升SEO效果