Nuxt3实现带独立URL的单页项目弹窗式作品集方案问询
Nuxt3实现弹窗式项目详情+独立URL+单页体验方案
结合你提到的两种方案的优缺点,这里提供一个整合式的实现思路,同时满足弹窗展示、独立可分享URL、无刷新单页体验三个核心需求:
核心思路
通过路由参数承载项目标识+状态管理控制弹窗显隐+滚动位置保存恢复的组合方式,既利用Nuxt路由生成独立URL,又通过监听路由变化实现弹窗的无缝切换,同时解决滚动体验问题。
具体实现步骤
1. 路由结构设计
保留嵌套路由作为URL载体,但不需要为项目页面单独渲染完整布局:
pages/ -- index.vue # 主页面:包含着陆区、关于区、项目网格 -- projects/ ---- [slug].vue # 空页面(仅作为路由占位,内容在index.vue的弹窗中加载)
2. 状态管理定义
用Nuxt内置的useState(或Pinia)维护弹窗相关状态:
// 在composables/usePortfolio.js中定义可复用状态 export const usePortfolio = () => { const isModalOpen = useState('isModalOpen', () => false) const currentProject = useState('currentProject', () => null) const scrollPosition = useState('scrollPosition', () => 0) return { isModalOpen, currentProject, scrollPosition } }
3. 项目网格与路由跳转
在index.vue的项目网格中,使用<NuxtLink>跳转至项目路由,同时保存当前滚动位置:
<template> <div class="grid grid-cols-1 md:grid-cols-3 gap-4"> <div v-for="project in projects" :key="project.id" class="project-card"> <NuxtLink :to="`/projects/${project.slug}`" @click="saveScroll" > <h3>{{ project.title.rendered }}</h3> <img :src="project.featured_media_url" alt="" /> </NuxtLink> </div> </div> </template> <script setup> const { scrollPosition } = usePortfolio() const saveScroll = () => { scrollPosition.value = window.scrollY // 存入sessionStorage,解决页面刷新后滚动位置丢失问题 sessionStorage.setItem('portfolioScroll', scrollPosition.value.toString()) } // 从WordPress API加载项目列表 const { data: projects } = await useFetch('https://你的WP站点/wp-json/wp/v2/projects') </script>
4. 路由监听与弹窗控制
在index.vue中监听路由变化,动态控制弹窗显隐并加载项目内容:
<template> <!-- 主页面内容:着陆区、关于区、项目网格 --> <!-- 项目详情弹窗 --> <div v-if="isModalOpen" class="fixed inset-0 bg-black/80 flex items-center justify-center z-50 p-4"> <div class="bg-white rounded-lg p-6 max-w-4xl w-full max-h-[90vh] overflow-y-auto relative"> <button @click="closeModal" class="absolute top-4 right-4 text-gray-500 hover:text-gray-800" > ✕ </button> <h2 class="text-2xl font-bold mb-4">{{ currentProject?.title.rendered }}</h2> <div v-html="currentProject?.content.rendered" class="prose"></div> </div> </div> </template> <script setup> const { isModalOpen, currentProject, scrollPosition } = usePortfolio() const route = useRoute() const router = useRouter() // 监听路由变化,处理弹窗逻辑 watch(() => route.path, async (newPath) => { if (newPath.startsWith('/projects/')) { const slug = newPath.split('/projects/')[1] // 从WordPress API加载单个项目详情 const { data: project } = await useFetch(`https://你的WP站点/wp-json/wp/v2/projects?slug=${slug}`) currentProject.value = project.value[0] isModalOpen.value = true // 恢复跳转前的滚动位置,避免页面跳顶 window.scrollTo(0, scrollPosition.value) } else { isModalOpen.value = false } }) // 关闭弹窗时返回上一页 const closeModal = () => router.back() // 处理页面刷新场景:直接访问项目URL时自动打开弹窗 onMounted(() => { if (route.path.startsWith('/projects/')) { const savedPos = sessionStorage.getItem('portfolioScroll') if (savedPos) scrollPosition.value = parseInt(savedPos) // 触发路由监听逻辑加载项目 watch(() => route.path, () => {}, { immediate: true }) } }) </script>
5. 禁用路由跳转自动滚动
在nuxt.config.ts中配置滚动行为,避免跳转项目路由时页面自动滚顶:
export default defineNuxtConfig({ app: { scrollBehavior(to, from) { // 跳转到项目路由时,保留当前滚动位置 if (to.path.startsWith('/projects/')) { return { top: window.scrollY } } // 从项目路由返回时,恢复之前的滚动位置 if (from.path.startsWith('/projects/')) { const savedPos = sessionStorage.getItem('portfolioScroll') return { top: savedPos ? parseInt(savedPos) : 0 } } return { top: 0 } } }, // 其他配置... })
方案优势
- 每个项目拥有独立URL,支持直接分享和浏览器书签
- 点击项目时无页面刷新,弹窗无缝加载,保持单页体验
- 关闭弹窗或浏览器前进后退时,自动恢复之前的滚动位置
- 兼容页面刷新场景,直接访问项目URL会自动打开弹窗并恢复滚动位置
内容的提问来源于stack exchange,提问作者Geert Roks
相关产品推荐
相关产品推荐

