Vue 3 嵌套路由:构建多层页面布局
嵌套路由是 vue Router 的重要功能。它允许在路由组件里嵌套其他路由组件,形成父子关系。这种设计特别适合构建有多层布局的应用。
一、什么是嵌套路由?
简单说,嵌套路由就是路由里面还有路由。比如一个管理系统:
外层有整体布局(头部、侧边栏)
内层是不同的功能页面
功能页面里可能还有子页面
嵌套路由就是为了处理这种分层结构的需求。
二、为什么需要嵌套路由?
想象一个后台管理系统:
如果没有嵌套路由,每个页面都要重复写外层布局代码。用嵌套路由,外层布局写一次,内层内容动态切换。
三、实际案例:服务项目页面
我们来看一个具体的例子。假设要做一个服务展示页面,左侧是服务列表,右侧显示选中服务的详情。
1. 父组件布局(Services.vue)
<template>
<div class="services-container">
<h2 class="content-title">服务项目</h2>
<div class="services-layout">
<!-- 左侧服务列表 -->
<div class="services-sidebar">
<button
v-for="service in services"
:key="service.id"
:class="['service-btn', { active: isActive(service.id) }]"
@click="selectService(service.id)"
>
{{ service.name }}
</button>
</div>
<!-- 右侧嵌套内容区域 -->
<div class="services-content">
<router-view></router-view>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { useRouter, useRoute } from 'vue-router'
const router = useRouter()
const route = useRoute() // 获取当前路由信息
const services = ref([
{ id: 'web-design', name: '网站设计与开发' },
{ id: 'mobile-app', name: '移动应用开发' },
{ id: 'ecommerce', name: '电子商务解决方案' },
{ id: 'api', name: 'API 开发与集成' },
{ id: 'consulting', name: '技术咨询与培训' }
])
// 判断当前激活的服务
const isActive = computed(() => (serviceId: string) => {
return route.params.id === serviceId ||
route.path.endsWith(serviceId)
})
// 选择服务
const selectService = (serviceId: string) => {
router.push(`/services/${serviceId}`)
}
</script>
<style scoped>
.services-container {
padding: 20px;
max-width: 1200px;
margin: 0 auto;
}
.content-title {
font-size: 24px;
margin-bottom: 20px;
color: #2c3e50;
border-bottom: 2px solid #eee;
padding-bottom: 10px;
}
.services-layout {
display: flex;
gap: 20px;
min-height: 400px;
}
.services-sidebar {
width: 250px;
display: flex;
flex-direction: column;
gap: 10px;
}
.service-btn {
padding: 12px 16px;
background: #f8f9fa;
border: 1px solid #dee2e6;
border-radius: 6px;
text-align: left;
cursor: pointer;
transition: all 0.3s ease;
font-size: 14px;
}
.service-btn:hover {
background: #e9ecef;
border-color: #adb5bd;
}
.service-btn.active {
background: #007bff;
color: white;
border-color: #007bff;
}
.services-content {
flex: 1;
border: 1px solid #dee2e6;
border-radius: 6px;
padding: 20px;
background: white;
min-height: 300px;
}
</style>关键点:
<router-view></router-view> 是子路由显示的位置
用 useRoute() 获取当前路由信息
用 useRouter() 进行路由跳转
2. 子组件示例(WebDesign.vue)
<template>
<div class="service-detail">
<h3>网站设计与开发</h3>
<p>我们提供全方位的网站设计与开发服务,从概念设计到最终部署,确保您的网站在各种设备上都能完美展示。</p>
<div class="service-features">
<h4>服务特点:</h4>
<ul>
<li>响应式设计,适配各种屏幕尺寸</li>
<li>现代化UI/UX设计</li>
<li>seo优化,提高搜索引擎排名</li>
<li>快速加载速度优化</li>
<li>内容管理系统集成</li>
</ul>
</div>
<div class="price-section">
<h4>服务价格:</h4>
<p>基础版:¥10,000起</p>
<p>高级版:¥20,000起</p>
<p>定制版:根据需求报价</p>
</div>
</div>
</template>
<script setup lang="ts">
// 可以在这里获取服务详情数据
import { onMounted } from 'vue'
import { useRoute } from 'vue-router'
const route = useRoute()
onMounted(() => {
console.log('当前服务ID:', route.params.id)
// 可以根据ID加载不同的数据
})
</script>
<style scoped>
.service-detail h3 {
color: #2c3e50;
margin-bottom: 15px;
font-size: 20px;
}
.service-detail p {
margin-bottom: 15px;
line-height: 1.6;
color: #495057;
}
.service-features {
margin-top: 20px;
margin-bottom: 20px;
}
.service-features h4 {
margin-bottom: 10px;
color: #495057;
font-size: 16px;
}
.service-features ul {
padding-left: 20px;
}
.service-features li {
margin-bottom: 8px;
color: #666;
}
.price-section {
margin-top: 25px;
padding-top: 20px;
border-top: 1px solid #eee;
}
.price-section h4 {
margin-bottom: 10px;
color: #495057;
}
.price-section p {
margin-bottom: 5px;
color: #333;
}
</style>3. 其他子组件
可以创建更多服务详情组件:
<!-- MobileApp.vue -->
<template>
<div class="service-detail">
<h3>移动应用开发</h3>
<p>专业开发iOS和Android应用,提供从设计到上架的全流程服务。</p>
<!-- 更多内容 -->
</div>
</template>
<!-- Ecommerce.vue -->
<template>
<div class="service-detail">
<h3>电子商务解决方案</h3>
<p>搭建完整的电商平台,支持商品管理、订单处理、支付集成等功能。</p>
<!-- 更多内容 -->
</div>
</template>四、路由配置
这是最关键的一步,需要在路由文件中配置嵌套关系:
// router/index.ts
import { createRouter, createWebHistory } from 'vue-router'
import Services from '../views/Services.vue'
import WebDesign from '../views/services/WebDesign.vue'
import MobileApp from '../views/services/MobileApp.vue'
import Ecommerce from '../views/services/Ecommerce.vue'
import ApiDevelopment from '../views/services/ApiDevelopment.vue'
import Consulting from '../views/services/Consulting.vue'
const router = createRouter({
history: createWebHistory(),
routes: [
// 其他路由...
{
path: '/services',
component: Services,
name: 'services',
children: [
{
path: '', // 默认子路由
redirect: 'web-design'
},
{
path: 'web-design',
component: WebDesign,
name: 'web-design'
},
{
path: 'mobile-app',
component: MobileApp,
name: 'mobile-app'
},
{
path: 'ecommerce',
component: Ecommerce,
name: 'ecommerce'
},
{
path: 'api',
component: ApiDevelopment,
name: 'api-development'
},
{
path: 'consulting',
component: Consulting,
name: 'consulting'
}
]
}
]
})
export default router重要注意事项:
子路由路径不要以 / 开头:写 'web-design' 而不是 '/web-design'
父组件必须有 <router-view>:用于显示子组件
可以使用默认重定向:当访问父路由时自动跳转到某个子路由
五、路由跳转的几种方式
1. 编程式导航(在脚本中)
// 使用 useRouter
import { useRouter } from 'vue-router'
const router = useRouter()
// 基本跳转
router.push('/services/web-design')
// 使用命名路由
router.push({ name: 'web-design' })
// 带参数(如果需要)
router.push({
name: 'service-detail',
params: { id: 'web-design' }
})2. 声明式导航(在模板中)
<template>
<!-- 使用 router-link 组件 -->
<router-link to="/services/web-design">
网站设计服务
</router-link>
<!-- 使用命名路由 -->
<router-link :to="{ name: 'web-design' }">
网站设计服务
</router-link>
<!-- 在按钮中跳转 -->
<button @click="$router.push('/services/web-design')">
查看详情
</button>
</template>3. 在父组件中监听路由变化
import { watch } from 'vue'
import { useRoute } from 'vue-router'
const route = useRoute()
// 监听路由参数变化
watch(
() => route.params.id,
(newId) => {
console.log('服务ID变化:', newId)
// 根据ID加载对应数据
loadServiceData(newId)
}
)
// 监听完整路由变化
watch(
() => route.path,
(newPath) => {
console.log('路由变化:', newPath)
}
)六、高级用法
1. 动态路由匹配
如果有很多相似的服务页面,可以用动态路由:
// 路由配置
{
path: '/services',
component: Services,
children: [
{
path: ':id', // 动态参数
component: ServiceDetail,
name: 'service-detail'
}
]
}
// 在 ServiceDetail 组件中获取参数
import { useRoute } from 'vue-router'
const route = useRoute()
console.log('当前服务ID:', route.params.id) // 比如 'web-design'2. 嵌套多层路由
路由可以嵌套多层:
{
path: '/admin',
component: AdminLayout,
children: [
{
path: 'users',
component: UserManagement,
children: [
{
path: 'list',
component: UserList
},
{
path: 'detail/:id',
component: UserDetail
},
{
path: 'create',
component: CreateUser
}
]
}
]
}3. 路由守卫
可以在嵌套路由中使用路由守卫:
// 全局守卫
router.beforeEach((to, from, next) => {
console.log('从', from.path, '到', to.path)
next()
})
// 路由独享守卫
{
path: '/services',
component: Services,
beforeEnter: (to, from, next) => {
// 检查权限等
if (hasPermission()) {
next()
} else {
next('/login')
}
},
children: [...]
}
// 组件内守卫(在组件中使用)
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
onBeforeRouteLeave((to, from) => {
// 离开当前组件时
const answer = window.confirm('确定要离开吗?')
return answer
})
onBeforeRouteUpdate((to, from) => {
// 路由更新时(比如参数变化)
console.log('路由参数更新')
})七、常见问题解决
问题1:子路由不显示
可能原因:
父组件没有 <router-view>
子路由路径写错了(多写了 /)
组件没有正确导入
检查步骤:
确认父组件有 <router-view>
检查路由配置,子路径不要以 / 开头
检查浏览器控制台有无错误
问题2:路由跳转但URL不变
可能原因: 使用了错误的跳转方法
解决方案:
// 错误
router.push('web-design') // 相对路径,可能不对
// 正确
router.push('/services/web-design') // 绝对路径
router.push({ name: 'web-design' }) // 使用命名路由问题3:样式冲突
现象: 父组件和子组件样式互相影响
解决方案:
<!-- 父组件 -->
<style scoped> /* scoped 限制样式作用域 */
.services-content {
/* 只影响当前组件 */
}
</style>
<!-- 或者使用css模块 -->
<style module>
.content {
/* 生成唯一类名 */
}
</style>八、最佳实践建议
1. 项目结构
src/
├── views/ # 页面级组件
│ ├── Services.vue
│ └── ...
├── components/ # 可复用组件
│ └── services/ # 服务相关组件
│ ├── ServiceSidebar.vue
│ ├── ServiceDetail.vue
│ └── ...
└── router/
└── index.ts # 路由配置2. 路由命名规范
使用 kebab-case(短横线分隔):web-design
命名路由要清晰:'service-web-design'
避免重名
3. 性能优化
使用路由懒加载
组件按需加载
合理使用 keep-alive
// 懒加载示例
{
path: 'web-design',
component: () => import('../views/services/WebDesign.vue'),
name: 'web-design'
}九、实际应用场景
1. 后台管理系统
外层:整体布局
中层:各个模块
内层:具体功能页面
2. 电商网站
外层:网站框架
中层:商品分类
内层:商品列表、商品详情
3. 文档网站
外层:导航栏、侧边栏
中层:文档分类
内层:具体文档页面
总结
嵌套路由是 Vue Router 的强大功能,特别适合构建多层布局的应用。关键记住几点:
配置子路由:在父路由的 children 数组中配置
显示子组件:父组件中需要 <router-view>
路径写法:子路由路径不要以 / 开头
路由跳转:可以用编程式或声明式导航
掌握了嵌套路由,你就能构建出结构清晰、易于维护的复杂应用。从简单的服务展示页面开始练习,慢慢应用到更复杂的项目中。记住,好的路由设计能让你的项目更加清晰和易于维护。
本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!