使用unplugin-vue-router实现带路由参数的模态对话框
用unplugin-vue-router实现带独立URL的模态弹窗
完全可以用unplugin-vue-router实现这个需求,核心是利用嵌套路由+路由参数,把弹窗作为子路由挂载在列表页面的父路由下,既支持点击列表项触发弹窗并更新URL,也支持直接访问详情URL时自动弹出弹窗并保留后方的列表页面。以下是具体实现步骤:
1. 路由结构设计
不管用unplugin-vue-router的文件系统路由还是手动配置路由,都要遵循嵌套结构:
- 父路由:
/servers,负责渲染服务器列表 - 子路由:
/servers/:id,负责渲染对应ID的服务器详情弹窗
方式一:文件系统路由(推荐,unplugin-vue-router原生支持)
按照如下目录结构创建文件,unplugin-vue-router会自动生成路由:
src/pages/ ├── servers/ │ ├── index.vue # 父路由:服务器列表页面 │ └── [id].vue # 子路由:服务器详情弹窗 └── ...
方式二:手动配置路由
如果需要自定义路由名称或元信息,可以在router.ts中手动配置:
import { createRouter, createWebHistory } from 'vue-router' import { routes } from 'vue-router/auto-routes' const router = createRouter({ history: createWebHistory(), routes: [ ...routes, { path: '/servers', name: 'servers', component: () => import('./pages/servers/index.vue'), children: [ { path: ':id', name: 'server-detail', component: () => import('./pages/servers/[id].vue'), meta: { isModal: true } // 标记为弹窗路由,方便后续处理 } ] } ] }) export default router
2. 实现服务器列表组件(servers/index.vue)
这个组件负责渲染列表,点击项时跳转到子路由触发弹窗:
<template> <div class="server-list"> <h1>服务器列表</h1> <div v-for="server in servers" :key="server.id" class="server-item" @click="openDetail(server.id)"> {{ server.name }} - {{ server.status }} </div> <!-- 子路由出口:弹窗会渲染在这里 --> <router-view /> </div> </template> <script setup> import { useRouter } from 'vue-router' const router = useRouter() // 模拟服务器数据 const servers = [ { id: '1', name: '服务器A', status: '运行中' }, { id: '2', name: '服务器B', status: '维护中' } ] const openDetail = (id) => { router.push({ name: 'servers-[id]', params: { id } }) // 文件系统路由自动生成的名称,手动配置的话用'server-detail' } </script> <style scoped> .server-list { padding: 20px; } .server-item { padding: 10px; border: 1px solid #eee; margin: 10px 0; cursor: pointer; } .server-item:hover { background: #f5f5f5; } </style>
3. 实现弹窗详情组件(servers/[id].vue)
这个组件作为子路由,负责渲染弹窗、加载详情,以及关闭弹窗时的路由处理:
<template> <!-- 弹窗遮罩层 --> <div class="modal-overlay" @click="closeModal"> <!-- 弹窗内容区,点击内部不触发关闭 --> <div class="modal-content" @click.stop> <button class="close-btn" @click="closeModal">×</button> <h2>服务器详情</h2> <p>ID: {{ serverId }}</p> <p>名称: {{ server?.name }}</p> <p>状态: {{ server?.status }}</p> </div> </div> </template> <script setup> import { useRouter, useRoute } from 'vue-router' import { ref, onMounted, watch } from 'vue' const router = useRouter() const route = useRoute() const serverId = ref(route.params.id) const server = ref(null) // 模拟根据ID获取服务器详情 const fetchServerDetail = async (id) => { // 这里替换成真实的API请求 await new Promise(resolve => setTimeout(resolve, 300)) const mockData = [ { id: '1', name: '服务器A', status: '运行中', ip: '192.168.1.100' }, { id: '2', name: '服务器B', status: '维护中', ip: '192.168.1.101' } ] server.value = mockData.find(item => item.id === id) } onMounted(() => { fetchServerDetail(serverId.value) }) // 监听路由参数变化,更新详情 watch(() => route.params.id, (newId) => { serverId.value = newId fetchServerDetail(newId) }) const closeModal = () => { router.back() // 回退到父路由,或者用router.push('/servers') } </script> <style scoped> .modal-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.5); display: flex; justify-content: center; align-items: center; z-index: 1000; } .modal-content { background: white; padding: 20px; border-radius: 8px; width: 400px; position: relative; } .close-btn { position: absolute; top: 10px; right: 15px; border: none; background: transparent; font-size: 20px; cursor: pointer; } </style>
4. 关键逻辑说明
- 路由嵌套:父路由
/servers始终渲染列表,子路由/servers/:id渲染弹窗,直接访问子路由时,父路由会先加载,再渲染弹窗,满足“弹窗后方显示列表”的需求。 - 路由参数监听:弹窗组件监听
route.params.id的变化,切换不同服务器时自动更新详情。 - 弹窗关闭处理:点击关闭按钮或遮罩层时,通过
router.back()回退到父路由,URL也会同步回到/servers。 - 无依赖弹窗:完全自定义CSS实现弹窗,不需要Vuetify等UI库。
内容的提问来源于stack exchange,提问作者Simon Klimek
相关产品推荐
相关产品推荐

