Vue嵌套路由
在單頁應用裏,“頁面”不再是整屏刷新,而是由路由驅動的組件樹。當業務複雜到「用户中心 → 個人資料 / 收貨地址 / 賬號安全 / 好友列表」這種層級時,嵌套路由(Nested Routes)是唯一能把深度與可維護性同時保留下來的方案。
一、嵌套路由到底在解決什麼問題
想象一個用户中心:
/user 用户中心外殼(Layout)
├── /user/profile 個人資料
├── /user/address 收貨地址
├── /user/security 賬號安全
└── /user/friends 好友列表
如果寫成平級路由,每切換一個子頁面就要重新加載整個外殼(導航、側邊欄、用户信息),浪費、卡頓、狀態丟失。
嵌套路由讓外殼只掛載一次,子頁面作為 <router-view> 的局部插槽渲染,完美複用外殼,並天然支持麪包屑、標籤頁、權限控制。
二、一條代碼看全貌
// router/index.js
const routes = [
{
path: '/user',
component: () => import('@/views/user/Layout.vue'), // 外殼
children: [
{ path: '', component: () => import('@/views/user/Profile.vue') },
{ path: 'address', component: () => import('@/views/user/Address.vue') },
{ path: 'security', component: () => import('@/views/user/Security.vue') },
{ path: 'friends', component: () => import('@/views/user/Friends.vue') }
]
}
]
要點:
- 層級關係 = 文件系統:父路由的
component是文件夾,children是裏面的文件。 - 默認子路由 = 空字符串
'',訪問/user時自動渲染Profile。 - 路徑寫法 = 相對路徑:
address會自動拼接成/user/address,無需手寫全量。
三、Layout 組件
<!-- views/user/Layout.vue -->
<template>
<div class="user-center">
<aside>
<router-link to="/user">個人資料</router-link>
<router-link to="/user/address">收貨地址</router-link>
<router-link to="/user/security">賬號安全</router-link>
<router-link to="/user/friends">好友列表</router-link>
</aside>
<main>
<router-view /> <!-- 子路由插在這裏 -->
</main>
</div>
</template>
子頁面渲染時,Layout 組件不會重新創建,導航高亮、用户信息、WebSocket 連接全部保持。
四、動態路由 + 嵌套:URL 即狀態
把用户 ID 塞進路徑:
{
path: '/user/:id',
component: () => import('@/views/user/Layout.vue'),
props: true, // 把 id 作為 prop 注入 Layout
children: [
{ path: '', component: () => import('@/views/user/Profile.vue'), props: true },
{ path: 'address', component: () => import('@/views/user/Address.vue'), props: true }
]
}
訪問 /user/42/address 時:
Layout通過props.id拿到 42,去拉用户信息;Address通過props.id再去拉地址列表;- 切換子路由只改後半段,外殼複用,接口只增不重複。
五、項目實踐
1.權限與麪包屑
{
path: '/user',
component: Layout,
meta: { title: '用户中心', needAuth: true },
children: [
{ path: '', meta: { title: '個人資料' } },
{ path: 'address', meta: { title: '收貨地址' } }
]
}
全局後置鈎子:
router.afterEach(to => {
document.title = to.matched
.map(r => r.meta.title)
.filter(Boolean)
.join(' - ')
})
matched 數組從根到當前節點依次展開,天然就是麪包屑數據源。
權限同理:在導航守衞裏檢查 to.matched.some(r => r.meta.needAuth),一次遞歸即可拿到所有層級要求。
2.代碼分割
- 父路由同步加載:外殼體積小,保證首屏骨架秒出;
- 子路由全部懶加載:利用魔法註釋給 chunk 命名,方便 CDN 緩存。
component: () =>
import(/* webpackChunkName: "user-security" */ '@/views/user/Security.vue')
六、常見問題
- 空路徑與斜槓:
path: ''與path: '/'都匹配/user,但後者會額外觸發重定向,導致外殼重複渲染。 - 深度監聽失效:在
Layout裏watch $route時,記得加immediate: true,否則首次進入不觸發。 - 滾動位置丟失:給
<router-view>加key="$route.fullPath"可強制重新掛載,但會破壞緩存;更優解是在activated鈎子裏手動恢復 scrollTop。