实际应用中,界面通常由多层嵌套的组件组成。Vue Router 支持嵌套路由,让组件结构和路由结构保持一致。
App
└── User
├── UserHeader
├── UserSidebar
└── UserContent
├── UserProfile
├── UserPosts
└── UserSettings
/user → User 组件
/user/profile → User + UserProfile 组件
/user/posts → User + UserPosts 组件
/user/settings → User + UserSettings 组件
const routes = [
{
path: '/user',
component: User,
children: [
{
path: '',
component: UserHome
},
{
path: 'profile',
component: UserProfile
},
{
path: 'posts',
component: UserPosts
}
]
}
]
<template>
<div class="user">
<h2>用户中心</h2>
<nav>
<router-link to="/user">首页</router-link>
<router-link to="/user/profile">个人资料</router-link>
<router-link to="/user/posts">文章</router-link>
</nav>
<router-view></router-view>
</div>
</template>
/ 开头children: [
{ path: '/profile', component: Profile }
]
会被当作根路径,嵌套路径不会被拼接。
/ 开头children: [
{ path: 'profile', component: Profile }
]
路径会被拼接:/user/profile
children: [
{ path: '', component: UserHome }
]
匹配 /user,渲染默认子组件。
const routes = [
{
path: '/user',
component: User,
children: [
{
path: 'settings',
component: UserSettings,
children: [
{
path: '',
component: SettingsGeneral
},
{
path: 'security',
component: SettingsSecurity
},
{
path: 'notification',
component: SettingsNotification
}
]
}
]
}
]
/user/settings → User + UserSettings + SettingsGeneral
/user/settings/security → User + UserSettings + SettingsSecurity
/user/settings/notification → User + UserSettings + SettingsNotification
const routes = [
{
path: '/user',
name: 'user',
component: User,
children: [
{
path: 'profile',
name: 'user-profile',
component: UserProfile
},
{
path: 'posts',
name: 'user-posts',
component: UserPosts
}
]
}
]
<router-link :to="{ name: 'user-profile' }">个人资料</router-link>
<router-link :to="{ name: 'user-posts' }">文章</router-link>
const routes = [
{
path: '/user',
component: User,
children: [
{
path: 'profile',
components: {
default: UserProfile,
sidebar: ProfileSidebar,
header: ProfileHeader
}
}
]
}
]
<template>
<div class="user">
<router-view name="header"></router-view>
<div class="content">
<router-view name="sidebar"></router-view>
<router-view></router-view>
</div>
</div>
</template>
const routes = [
{
path: '/user',
component: User,
redirect: '/user/profile',
children: [
{ path: 'profile', component: UserProfile },
{ path: 'posts', component: UserPosts }
]
}
]
children: [
{ path: '', redirect: 'profile' },
{ path: 'profile', component: UserProfile }
]
export default {
beforeRouteEnter(to, from, next) {
next(vm => {
console.log('进入子路由')
})
},
beforeRouteUpdate(to, from, next) {
console.log('子路由参数变化')
next()
},
beforeRouteLeave(to, from, next) {
if (this.hasUnsavedChanges) {
const answer = window.confirm('确定离开?')
if (answer) next()
else next(false)
} else {
next()
}
}
}
const routes = [
{
path: '/',
component: Layout,
redirect: '/dashboard',
children: [
{
path: 'dashboard',
name: 'dashboard',
component: Dashboard,
meta: { title: '仪表盘', icon: 'dashboard' }
},
{
path: 'user',
name: 'user',
component: UserList,
meta: { title: '用户管理', icon: 'user' }
},
{
path: 'product',
name: 'product',
component: ProductLayout,
meta: { title: '商品管理', icon: 'product' },
children: [
{
path: 'list',
name: 'product-list',
component: ProductList,
meta: { title: '商品列表' }
},
{
path: 'category',
name: 'product-category',
component: ProductCategory,
meta: { title: '分类管理' }
}
]
},
{
path: 'order',
name: 'order',
component: OrderLayout,
meta: { title: '订单管理', icon: 'order' },
children: [
{
path: 'list',
name: 'order-list',
component: OrderList
},
{
path: 'detail/:id',
name: 'order-detail',
component: OrderDetail,
props: true
}
]
}
]
}
]
<template>
<div class="layout">
<aside class="sidebar">
<el-menu :default-active="activeMenu" router>
<el-menu-item index="/dashboard">
<i class="icon-dashboard"></i>
<span>仪表盘</span>
</el-menu-item>
<el-submenu index="product">
<template #title>
<i class="icon-product"></i>
<span>商品管理</span>
</template>
<el-menu-item index="/product/list">商品列表</el-menu-item>
<el-menu-item index="/product/category">分类管理</el-menu-item>
</el-submenu>
</el-menu>
</aside>
<main class="main">
<header class="header">
<breadcrumb></breadcrumb>
</header>
<div class="content">
<router-view></router-view>
</div>
</main>
</div>
</template>
<script>
export default {
computed: {
activeMenu() {
return this.$route.path
}
}
}
</script>
export default {
computed: {
breadcrumbs() {
return this.$route.matched
.filter(route => route.meta && route.meta.title)
.map(route => ({
title: route.meta.title,
path: route.path
}))
}
}
}
export default {
computed: {
menuRoutes() {
return this.$router.options.routes
.find(route => route.path === '/')
.children
.filter(route => !route.hidden)
.map(route => ({
path: route.path,
title: route.meta?.title,
icon: route.meta?.icon,
children: route.children?.map(child => ({
path: `${route.path}/${child.path}`,
title: child.meta?.title
}))
}))
}
}
}
嵌套路由要点:
/ 开头会自动拼接