一、嵌套路由是干啥的?先看实际场景
做Vue项目时,不少同学碰到过这种情况:一个页面里得嵌套另一个页面,比如后台管理系统左边是侧边栏(固定的父页面),右边内容区要根据侧边栏选项切换不同子页面,这时候Vue Router的嵌套路由就是解决这类问题的关键,但嵌套路由到底是啥?咋配置?遇到问题咋处理?今天咱用大白话+实际例子,把这些事儿掰碎了讲清楚。
嵌套路由本质是「路由的层级管理」,让URL结构和页面组件结构一一对应,举个常见例子:比如做一个博客系统,有个/article
路由对应文章列表页(父组件),列表页里点某篇文章,要跳转到/article/123
(子组件,文章详情页),这时候/article
是父路由,/article/:id
是子路由,这种层级关系就需要嵌套路由来实现。
再比如后台管理系统,/admin
对应侧边栏+顶部栏的布局(父组件),点击侧边栏的「用户管理」,右边显示/admin/users
(子组件);点击「订单管理」,右边显示/admin/orders
(另一个子组件),父组件的布局不变,只切换子组件,这就是嵌套路由的典型场景。
嵌套路由核心:父路由、子路由和<router-view>
有啥关系?
要理解嵌套路由,得先搞懂<router-view>
这个东西,它是Vue Router提供的「路由组件挂载点」—— 你配置的路由组件(比如ArticleList
、ArticleDetail
),会被渲染到<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/xxx
(xxx
不是有效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 }) } ] }
父组件ArticleList
的props
接收category
:
export default { props: ['category'], mounted() { console.log('当前分类:', this.category) // 比如frontend } }
子组件ArticleDetail
的props
接收category
和id
:
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>
(渲染users
或orders
):
<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/users
,main
区域渲染UserList
; - 点击「添加用户」按钮:路径变成
/admin/users/add
,UserList
里的<router-view>
渲染UserAdd
组件; - 点击侧边栏「订单管理」:路径变成
/admin/orders
,main
区域渲染OrderList
。
通过这个实战案例,能直观感受到嵌套路由如何让页面结构和URL对应,以及多层嵌套(AdminLayout → UserList → UserAdd
)的配置逻辑。
嵌套路由常见坑有哪些?避坑指南
新手配置嵌套路由时,很容易掉坑里,总结几个高频问题:
子路由path
加了,导致路径错误
错误写法:子路由path
写/child
,父路由path
是/parent
,结果访问/child
才能看到子组件,而不是/parent/child
。
解决:子路由path
不要加,用相对路径,比如父路由/parent
,子路由path
写child
,访问路径就是/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' } } }
子组件(如UserList
或UserAdd
):
export default { inject: ['theme'], mounted() { console.log('当前主题:', this.theme) // dark } }
这种方式适合「父组件到深层子组件」的跨层级通信,比props
逐层传递更方便。
学会嵌套路由有啥用?总结价值
掌握嵌套路由,能解决「页面层级结构和
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。