Vue 3 嵌套路由:构建多层页面布局

更新日期: 2025-12-03 阅读: 20 标签: 路由

嵌套路由是 vue Router 的重要功能。它允许在路由组件里嵌套其他路由组件,形成父子关系。这种设计特别适合构建有多层布局的应用。


一、什么是嵌套路由?

简单说,嵌套路由就是路由里面还有路由。比如一个管理系统:

  • 外层有整体布局(头部、侧边栏)

  • 内层是不同的功能页面

  • 功能页面里可能还有子页面

嵌套路由就是为了处理这种分层结构的需求。


二、为什么需要嵌套路由?

想象一个后台管理系统:

  1. 外层框架:顶部导航栏、左侧菜单栏、底部信息栏

  2. 中间内容区:不同的功能模块(用户管理、订单管理)

  3. 内层详情:每个功能的子页面(用户列表、用户详情)

如果没有嵌套路由,每个页面都要重复写外层布局代码。用嵌套路由,外层布局写一次,内层内容动态切换。


三、实际案例:服务项目页面

我们来看一个具体的例子。假设要做一个服务展示页面,左侧是服务列表,右侧显示选中服务的详情。

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

重要注意事项:

  1. 子路由路径不要以 / 开头:写 'web-design' 而不是 '/web-design'

  2. 父组件必须有 <router-view>:用于显示子组件

  3. 可以使用默认重定向:当访问父路由时自动跳转到某个子路由


五、路由跳转的几种方式

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:子路由不显示

可能原因:

  1. 父组件没有 <router-view>

  2. 子路由路径写错了(多写了 /)

  3. 组件没有正确导入

检查步骤:

  1. 确认父组件有 <router-view>

  2. 检查路由配置,子路径不要以 / 开头

  3. 检查浏览器控制台有无错误

问题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 的强大功能,特别适合构建多层布局的应用。关键记住几点:

  1. 配置子路由:在父路由的 children 数组中配置

  2. 显示子组件:父组件中需要 <router-view>

  3. 路径写法:子路由路径不要以 / 开头

  4. 路由跳转:可以用编程式或声明式导航

掌握了嵌套路由,你就能构建出结构清晰、易于维护的复杂应用。从简单的服务展示页面开始练习,慢慢应用到更复杂的项目中。记住,好的路由设计能让你的项目更加清晰和易于维护。

本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!

链接: https://fly63.com/article/detial/13280

vue路由history模式_如何去除vue项目中的#

在使用vue-cli搭建的环境中,浏览器上URL地址中是存在#的,这是由于vue-router 默认 hash 模式,不难发现#的出现真的很丑陋。官网给出了如何使用history模式mode: history

vue路由传参主要的3种方式

vue中路由传参主要的3种方式:query方式(push时使用path来匹配)、params模式(push时使用name来匹配)、location预声明参数模式(push使用path来匹配,但是它跟params模式不同)

vue动态加载路由_实现vue动态加载路由器设置

我们的通用的后台管理系统中,我们会根据权限的粗细不同,会对每个角色每个权限每个资源进行控制。同样的我们也需要实现一个这样的功能。 这篇文章我将主要讲vue端的实现,关于后台接口我就不会涉及,当我接触的时候我们的后台接口是springcloud实现。

两种前端路由的实现方式

前后端分离开发模式,后端会把路由控制丢在前端,这几天再开发单页面小的项目,手动撸了个路由。前端路由实现有两种方法。HTML5 History API包括2个方法:history.pushState()和history.replaceState(),和1个事件:window.onpopstate。hash + location.onhashchange

vue动态路由_vue-router通过接口请求动态生成路由的实现

在后台管理系统中,一般都会采用权限管理。路由菜单数据都会保存到数据库中,在vue-router 2.2版本新增了一个router.addRoutes(routes)方法,即可用它来实现动态路由了

HTML5 History 模式

vue-router 默认 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。如果不想要很丑的 hash,我们可以用路由的 history 模式,这种模式充分利用 history.pushState API 来完成 URL 跳转而无须重新加载页面。

vue router 路由鉴权(非动态路由)

原本想用动态路由的思路去做,按权限加载对应路由表,但是由于权限可以交叉(比如一个人可以同时是主题管理员和数据服务管理员),导致权限路由表还是得去做判断组合。于是放弃了这个思路,索性就在beforeEach里直接判断了。

vue中路由按需加载的几种方式

使用vue-cli构建项目后,我们会在Router文件夹下面的index.js里面引入相关的路由组件,webpack在打包的时候会把整个路由打包成一个js文件,如果页面一多,会导致这个文件非常大,加载缓慢

vue-router 中参数传递(params,query)

query和params的区别,query相当于get请求,在页面跳转的时候,可以在地址栏看到请求参数,然而params则相当于post请求,参数不会在地址栏中显示。

Node.js的路由

当服务端接收到HTTP请求时,可以通过onRequest() 获取到url, pathname,query,及paramParams参数;为了解析这些数据需要使用url和querystring模块

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!