Code前端首页关于Code前端联系我们

一、嵌套路由是干啥的?先看实际场景

terry 9小时前 阅读数 9 #Vue

做Vue项目时,不少同学碰到过这种情况:一个页面里得嵌套另一个页面,比如后台管理系统左边是侧边栏(固定的父页面),右边内容区要根据侧边栏选项切换不同子页面,这时候Vue Router的嵌套路由就是解决这类问题的关键,但嵌套路由到底是啥?咋配置?遇到问题咋处理?今天咱用大白话+实际例子,把这些事儿掰碎了讲清楚。

嵌套路由本质是「路由的层级管理」,让URL结构和页面组件结构一一对应,举个常见例子:

比如做一个博客系统,有个/article路由对应文章列表页(父组件),列表页里点某篇文章,要跳转到/article/123(子组件,文章详情页),这时候/article是父路由,/article/:id是子路由,这种层级关系就需要嵌套路由来实现。

再比如后台管理系统,/admin对应侧边栏+顶部栏的布局(父组件),点击侧边栏的「用户管理」,右边显示/admin/users(子组件);点击「订单管理」,右边显示/admin/orders(另一个子组件),父组件的布局不变,只切换子组件,这就是嵌套路由的典型场景。

嵌套路由核心:父路由、子路由和<router-view>有啥关系?

要理解嵌套路由,得先搞懂<router-view>这个东西,它是Vue Router提供的「路由组件挂载点」—— 你配置的路由组件(比如ArticleListArticleDetail),会被渲染到<router-view>所在的位置。

嵌套路由的关键是「<router-view>的嵌套」:父路由对应的组件模板里,得放一个<router-view>,用来渲染子路由对应的组件,举个例子:

父组件ArticleList的模板长这样:

<template>
  <div class="article-list">
    <h2>文章列表</h2>
    <ul>
      <li v-for="article in articles" :key="article.id">
        <router-link :to="'/article/' + article.id">{{ article.title }}</router-link>
      </li>
    </ul>
    <router-view></router-view> <!-- 子组件(文章详情)会渲染到这里 -->
  </div>
</template>

当用户点击文章链接,进入/article/123时,子路由对应的ArticleDetail组件,就会被渲染到父组件ArticleList里的<router-view>位置,这就是「嵌套」的核心逻辑:父组件用<router-view>承载子组件

咋配置嵌套路由?分步骤教你写代码

配置嵌套路由分三步:定义路由规则(用children数组)、父组件加<router-view>、设置子路由导航,咱一步一步来。

路由规则里用children数组定义子路由

router/index.js里,给父路由配置children数组,里面放子路由的规则。

import Vue from 'vue'
import VueRouter from 'vue-router'
import ArticleList from '../views/ArticleList.vue'
import ArticleDetail from '../views/ArticleDetail.vue'
Vue.use(VueRouter)
const routes = [
  {
    path: '/article', // 父路由路径
    component: ArticleList, // 父路由组件
    children: [ // 子路由规则放在children里
      {
        path: ':id', // 子路由路径,注意:这里没加/,是相对父路由的路径
        component: ArticleDetail // 子路由组件
      }
    ]
  }
]
const router = new VueRouter({
  mode: 'history',
  routes
})
export default router

这里要注意path: ':id'没加,因为加了会变成「绝对路径」,比如父路由是/article,子路由path/detail,那访问路径是/detail,而不是/article/detail,所以子路由path用「相对路径」(不加),这样访问路径就是/article/123(父路径+子路径)。

父组件模板里加<router-view>

父组件(比如ArticleList)的模板必须包含<router-view>,否则子组件没地方渲染,页面会空白,像这样:

<template>
  <div>
    <h2>文章列表</h2>
    <!-- 这里是文章列表的内容 -->
    <router-view></router-view> <!-- 子组件(ArticleDetail)会渲染到这里 -->
  </div>
</template>

子路由的导航咋写?用<router-link>或编程式导航

导航到子路由有两种方式:<router-link>组件或者编程式导航(this.$router.push)。

<router-link>的话,可以写绝对路径或相对路径:

<!-- 绝对路径:/article/ + 文章id -->
<router-link :to="'/article/' + article.id">查看文章</router-link>
<!-- 相对路径:在父组件内时,to可以直接写子路径 -->
<router-link :to="article.id">查看文章</router-link>

编程式导航同理:

// 绝对路径
this.$router.push('/article/' + article.id)
// 相对路径(在父组件内时)
this.$router.push(article.id)

相对路径更灵活,比如父路由路径变了(比如从/article改成/posts),子路由导航不用挨个改,所以推荐在父组件内用相对路径。

嵌套路由里的 tricky 问题咋解决?(默认路由、404、传参)

配置嵌套路由时,总会遇到一些细节问题:比如进入父路由时自动显示某个子页面、子路由找不到时显示404、父路由参数咋传给子组件,咱一个个解决。

默认子路由:进入父路由时自动渲染子组件

需求:访问/article时,自动显示「文章推荐」子组件,而不是空白,这时候给子路由配path: ''(空路径)。

路由规则改成这样:

children: [
  {
    path: '', // 空路径,匹配/article
    component: ArticleRecommend // 默认子组件
  },
  {
    path: ':id',
    component: ArticleDetail
  }
]

这样访问/article时,会渲染ArticleRecommend;访问/article/123时,渲染ArticleDetail

子路由的404页面:匹配不到子路由时显示错误页

需求:父路由下的子路由如果不存在(比如用户输错路径/article/abc,但abc不是有效文章id),显示404,这时候用「通配符」匹配所有未定义的子路径。

路由规则里,把404路由放在children因为路由匹配是「从上到下」,匹配所有,放最后才不会覆盖其他子路由):

children: [
  { path: '', component: ArticleRecommend },
  { path: ':id', component: ArticleDetail },
  { path: '*', component: ArticleNotFound } // 404组件
]

这样访问/article/xxxxxx不是有效id且没被其他子路由匹配)时,就会渲染ArticleNotFound

嵌套路由的参数传递:父路由参数咋给子组件?

比如父路由是/article/:category(比如/article/frontend),子路由要拿到category参数,有两种方式:

子组件通过$route.params获取

子组件里直接用this.$route.params.category,但这种方式让子组件「紧耦合」$route,不利于组件复用和测试。

props传递参数(推荐)

路由规则里给父路由和子路由开启props,把参数通过props传给组件。

{
  path: '/article/:category',
  component: ArticleList,
  props: true, // 父组件通过props接收category
  children: [
    {
      path: ':id',
      component: ArticleDetail,
      props: (route) => ({ 
        category: route.params.category, // 从父路由参数拿category
        id: route.params.id 
      })
    }
  ]
}

父组件ArticleListprops接收category

export default {
  props: ['category'],
  mounted() {
    console.log('当前分类:', this.category) // 比如frontend
  }
}

子组件ArticleDetailprops接收categoryid

export default {
  props: ['category', 'id'],
  mounted() {
    console.log('当前分类:', this.category, '文章id:', this.id)
  }
}

这样组件不依赖$route,更灵活,也方便写单元测试。

实战:做个后台管理页面的嵌套路由

光说不练假把式,咱做个简单的后台管理页面,包含「用户管理」和「订单管理」两个模块,每个模块下有子页面。

规划路由结构

  • 父路由:/admin,对应侧边栏+顶部栏布局(组件AdminLayout
  • 子路由1:/admin/users,对应用户列表页(组件UserList
  • 子路由2:/admin/users/add,对应添加用户页(组件UserAdd,是users的子路由)
  • 子路由3:/admin/orders,对应订单列表页(组件OrderList

编写路由规则(router/index.js

import Vue from 'vue'
import VueRouter from 'vue-router'
import AdminLayout from '../views/AdminLayout.vue'
import UserList from '../views/UserList.vue'
import UserAdd from '../views/UserAdd.vue'
import OrderList from '../views/OrderList.vue'
Vue.use(VueRouter)
const routes = [
  {
    path: '/admin',
    component: AdminLayout,
    children: [
      {
        path: '', // 默认子路由:访问/admin时重定向到users
        redirect: 'users'
      },
      {
        path: 'users', // 相对路径,完整路径是/admin/users
        component: UserList,
        children: [ // users的子路由
          {
            path: 'add', // 完整路径/admin/users/add
            component: UserAdd
          }
        ]
      },
      {
        path: 'orders',
        component: OrderList
      }
    ]
  }
]
const router = new VueRouter({
  mode: 'history',
  routes
})
export default router

编写父组件模板(AdminLayout.vue

AdminLayout/admin的组件,包含侧边栏和<router-view>(渲染usersorders):

<template>
  <div class="admin-layout">
    <aside>
      <h3>后台管理</h3>
      <nav>
        <router-link to="users">用户管理</router-link>
        <router-link to="orders">订单管理</router-link>
      </nav>
    </aside>
    <main>
      <router-view></router-view> <!-- 渲染users或orders组件 -->
    </main>
  </div>
</template>
<script>
export default {
  name: 'AdminLayout'
}
</script>

编写UserList组件(包含子路由add<router-view>

UserList/admin/users的组件,里面要渲染/admin/users/add的子组件,所以模板里加<router-view>

<template>
  <div class="user-list">
    <h2>用户列表</h2>
    <button @click="goToAdd">添加用户</button>
    <router-view></router-view> <!-- 渲染user-add组件 -->
  </div>
</template>
<script>
export default {
  name: 'UserList',
  methods: {
    goToAdd() {
      // 编程式导航到子路由add
      this.$router.push('add') // 相对路径,完整路径/admin/users/add
    }
  }
}
</script>

测试导航

  • 访问/admin:会自动重定向到/admin/usersmain区域渲染UserList
  • 点击「添加用户」按钮:路径变成/admin/users/addUserList里的<router-view>渲染UserAdd组件;
  • 点击侧边栏「订单管理」:路径变成/admin/ordersmain区域渲染OrderList

通过这个实战案例,能直观感受到嵌套路由如何让页面结构和URL对应,以及多层嵌套(AdminLayout → UserList → UserAdd)的配置逻辑。

嵌套路由常见坑有哪些?避坑指南

新手配置嵌套路由时,很容易掉坑里,总结几个高频问题:

子路由path加了,导致路径错误

错误写法:子路由path/child,父路由path/parent,结果访问/child才能看到子组件,而不是/parent/child

解决:子路由path不要加,用相对路径,比如父路由/parent,子路由pathchild,访问路径就是/parent/child

父组件没加<router-view>,子组件没地方渲染

症状:配置了子路由,但页面空白,控制台也没报错。

解决:检查父组件模板,必须包含<router-view>,子组件才会被渲染到那里。

路由匹配顺序搞反,导致子路由不生效

错误写法:把通配符放在子路由前面,

children: [
  { path: '*', component: NotFound },
  { path: 'child', component: Child }
]

结果访问/parent/child时,匹配到,渲染NotFound,而不是Child

解决:路由匹配是「从上到下」,所以这类通配符路由要放在children数组最后,让具体路由先匹配。

默认子路由的path配置错误

错误写法:默认子路由path写,导致访问/parent时,匹配不到默认子路由。

解决:默认子路由path写(空字符串),这样访问/parent时才会匹配。

子组件获取父路由参数失败

症状:父路由有动态参数(比如/parent/:id),子组件用this.$route.params.id拿不到值。

原因:子路由的path没包含参数,或者参数传递方式不对。

解决:如果子路由需要父路由参数,要么在子路由path里包含参数(比如path: 'child/:id'),要么用props传递(前面讲的「方式二」)。

嵌套路由和其他技术咋结合?(Vuex、组件通信)

实际项目中,嵌套路由的不同层级组件可能需要共享数据,这时候可以结合Vuex或组件通信方式(如provide/inject)。

用Vuex共享状态

比如父组件和子组件都需要用户信息,把用户信息存在Vuex的store里:

// store/index.js
export default new Vuex.Store({
  state: {
    user: { name: '小明', role: 'editor' }
  },
  getters: {
    getUser: state => state.user
  }
})

父组件和子组件都可以通过this.$store.getters.getUser获取用户信息,不用关心组件层级。

provide/inject跨层级传值

如果不想用Vuex,也可以用Vue的provide/inject特性,父组件用provide提供数据,子组件用inject接收。

父组件(如AdminLayout):

export default {
  provide() {
    return {
      theme: this.theme // 假设theme是父组件的数据源
    }
  },
  data() {
    return {
      theme: 'dark'
    }
  }
}

子组件(如UserListUserAdd):

export default {
  inject: ['theme'],
  mounted() {
    console.log('当前主题:', this.theme) // dark
  }
}

这种方式适合「父组件到深层子组件」的跨层级通信,比props逐层传递更方便。

学会嵌套路由有啥用?总结价值

掌握嵌套路由,能解决「页面层级结构和

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门