我們在構(gòu)建復(fù)雜系統(tǒng)的時候,需要構(gòu)建多頁面來完成不同的功能,在使用多個頁面的時候,用層級路徑來標示頁面。vue router 將組件掛載在路由上的方式來模擬傳統(tǒng)的多頁應(yīng)用的效果,從而構(gòu)建復(fù)雜的單頁應(yīng)用。所以路由主要完成的是根據(jù)當前的路徑完成頁面的切換。
如何使用
注冊路由
首先構(gòu)建路由和組件之間的對應(yīng)關(guān)系
// 0. 如果使用模塊化機制編程,導(dǎo)入Vue和VueRouter,要調(diào)用 Vue.use(VueRouter)
// 1. 定義 (路由) 組件。
// 可以從其他文件 import 進來
const Foo = { template: '<div>foo</div>' }
const Bar = { template: '<div>bar</div>' }
// 2. 定義路由
// 每個路由應(yīng)該映射一個組件。 其中"component" 可以是
// 通過 Vue.extend() 創(chuàng)建的組件構(gòu)造器,
// 或者,只是一個組件配置對象。
// 我們晚點再討論嵌套路由。
const routes = [
{path: "/foo", component: Foo},
{path: "/bar", component: Bar},
//命名路由的方式
{
path: '/user/:userId',
name: 'user',
component: User
}
]
// 3. 創(chuàng)建 router 實例,然后傳 `routes` 配置
// 你還可以傳別的配置參數(shù), 不過先這么簡單著吧。
const router = new VueRouter({
routes
})
// 4. 創(chuàng)建和掛載根實例。
// 記得要通過 router 配置參數(shù)注入路由,
// 從而讓整個應(yīng)用都有路由功能
const app = new Vue({
router
}).$mount('#app')
路由導(dǎo)航和顯示對應(yīng)的內(nèi)容
利用<router-link>模擬<a>,進行頁面之間的導(dǎo)航。利用<router-view>來控制當前路由的對應(yīng)的控件顯示的區(qū)域。
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<h1>Hello App!</h1>
<p>
<!-- 使用 router-link 組件來導(dǎo)航. -->
<!-- 通過傳入 `to` 屬性指定鏈接. -->
<!-- <router-link> 默認會被渲染成一個 `<a>` 標簽 -->
<router-link to="/foo">Go to Foo</router-link>
<router-link to="/bar">Go to Bar</router-link>
<!-- 命名的路由方式 -->
<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>
</p>
<!-- 路由出口 -->
<!-- 路由匹配到的組件將渲染在這里 -->
<router-view></router-view>
</div>
路由的代碼操作
this.$route 用于訪問當前的路由,比如this.$route.params.username用于訪問動態(tài)路由里面的參數(shù)
router.push
router.push(location, onComplete?, onAbort?)
// 字符串
router.push('home')
// 對象
router.push({ path: 'home' })
// 命名的路由,給定路由名稱的情況下,用這種方式導(dǎo)航
router.push({ name: 'user', params: { userId: '123' }})
// 帶查詢參數(shù),變成 /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})
注意:如果提供了 path,params 會被忽略,上述例子中的 query 并不屬于這種情況。取而代之的是下面例子的做法,你需要提供路由的 name 或手寫完整的帶有參數(shù)的 path
router.replace
跟 router.push 很像,唯一的不同就是,它不會向 history 添加新記錄,而是跟它的方法名一樣 —— 替換掉當前的 history 記錄。
router.go(n)
這個方法的參數(shù)是一個整數(shù),意思是在 history 記錄中向前或者后退多少步,類似 window.history.go(n)。
// 在瀏覽器記錄中前進一步,等同于 history.forward()
router.go(1)
// 后退一步記錄,等同于 history.back()
router.go(-1)
// 前進 3 步記錄
router.go(3)
// 如果 history 記錄不夠用,那就默默地失敗唄
router.go(-100)
router.go(100)
路由的種類
路由匹配順序
由于動態(tài)路由匹配存在,一個路徑可能匹配多個路由規(guī)則,路由匹配順序按照路由定義的優(yōu)先級來,誰先定義,誰的優(yōu)先級就高。
動態(tài)路由匹配
主要用于將屬于同一類的不同路由映射到同一個組件里面,路由的不同部分會被參數(shù)化匹配,如
routes: [
// 動態(tài)路徑參數(shù) 以冒號開頭
{ path: '/user/:id', component: User }
]
匹配的方式
動態(tài)路徑參數(shù)
一個“路徑參數(shù)”使用冒號 : 標記。當匹配到一個路由時,參數(shù)值會被設(shè)置到 this.$route.params,可以在每個組件內(nèi)使用。于是,我們可以更新 User 的模板,輸出當前用戶的 ID。
在不同的路由之間進行導(dǎo)航的時候,如果對應(yīng)的是同一個組件,那么組件會被復(fù)用,也就是意味著組件的生命周期鉤子不會再被調(diào)用。所以想要檢測路由的變化和頁面的重新刷新,可以了用檢測路由變化,或者利用導(dǎo)航守衛(wèi)。
watch: {
'$route' (to, from) {
// 對路由變化作出響應(yīng)...
}
}
const User = {
template: '...',
beforeRouteUpdate (to, from, next) {
// react to route changes...
// don't forget to call next()
}
}
匹配所有路由
主要用以客戶端 404 錯誤,為剩余的路由設(shè)置一個默認顯示。所以一般放在最后。
{
// 會匹配所有路徑
path: '*'
}
{
// 會匹配以 `/user-` 開頭的任意路徑
path: '/user-*'
}
當使用一個通配符時,$route.params 內(nèi)會自動添加一個名為 pathMatch 參數(shù)。它包含了 URL 通過通配符被匹配的部分:
// 給出一個路由 { path: '/user-*' }
this.$router.push('/user-admin')
this.$route.params.pathMatch // 'admin'
// 給出一個路由 { path: '*' }
this.$router.push('/non-existing')
this.$route.params.pathMatch // '/non-existing'
高級匹配模式
支持以正則表達式的方式來匹配路由,依賴path-to-regexp來實現(xiàn)。
嵌套路由
實際生活中的應(yīng)用界面,通常由多層嵌套的組件組合而成。同樣地,URL 中各段動態(tài)路徑也按某種結(jié)構(gòu)對應(yīng)嵌套的各層組件,例如:
/user/foo/profile /user/foo/posts
+------------------+ +-----------------+
| User | | User |
| +--------------+ | | +-------------+ |
| | Profile | | +------------> | | Posts | |
| | | | | | | |
| +--------------+ | | +-------------+ |
+------------------+ +-----------------+
借助 vue-router,使用嵌套路由配置,就可以很簡單地表達這種關(guān)系。也就是說被路由渲染的組件里面還有包含路由渲染路由<router-view>。這通過子路由的方式來實現(xiàn)
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User,
children: [
// 當 /user/:id 匹配成功,
// UserHome 會被渲染在 User 的 <router-view> 中
{ path: '', component: UserHome },
// ...其他子路由
{
// 當 /user/:id/profile 匹配成功,
// UserProfile 會被渲染在 User 的 <router-view> 中
path: 'profile',
component: UserProfile
},
{
// 當 /user/:id/posts 匹配成功
// UserPosts 會被渲染在 User 的 <router-view> 中
path: 'posts',
component: UserPosts
}
]
}
]
})
顯示多個路由視圖-命名視圖
有時候想同時 (同級) 展示多個視圖,而不是嵌套展示,例如創(chuàng)建一個布局,有 sidebar (側(cè)導(dǎo)航) 和 main (主內(nèi)容) 兩個視圖,這個時候命名視圖就派上用場了。你可以在界面中擁有多個單獨命名的視圖,而不是只有一個單獨的出口。如果 router-view 沒有設(shè)置名字,那么默認為 default。
<router-view class="view one"></router-view>
<router-view class="view two" name="a"></router-view>
<router-view class="view three" name="b"></router-view>
構(gòu)建路由的方式也有變化,變成了多組件
const router = new VueRouter({
routes: [
{
path: '/',
components: {
default: Foo,
a: Bar,
b: Baz
}
}
]
})
嵌套路由和命名路由組合
我們也有可能使用命名視圖創(chuàng)建嵌套視圖的復(fù)雜布局。這時你也需要命名用到的嵌套 router-view 組件。我們以一個設(shè)置面板為例:
/settings/emails /settings/profile
+-----------------------------------+ +------------------------------+
| UserSettings | | UserSettings |
| +-----+-------------------------+ | | +-----+--------------------+ |
| | Nav | UserEmailsSubscriptions | | +------------> | | Nav | UserProfile | |
| | +-------------------------+ | | | +--------------------+ |
| | | | | | | | UserProfilePreview | |
| +-----+-------------------------+ | | +-----+--------------------+ |
+-----------------------------------+ +------------------------------+
UserSettings 組件的 <template> 部分應(yīng)該是類似下面的這段代碼:
<!-- UserSettings.vue -->
<div>
<h1>User Settings</h1>
<NavBar/>
<router-view/>
<router-view name="helper"/>
</div>
然后你可以用這個路由配置完成該布局:
{
path: '/settings',
// 你也可以在頂級路由就配置命名視圖
component: UserSettings,
children: [{
path: 'emails',
component: UserEmailsSubscriptions
}, {
path: 'profile',
components: {
default: UserProfile,
helper: UserProfilePreview
}
}]
}
路由重定向
//重定向到路徑
const router = new VueRouter({
routes: [
{ path: '/a', redirect: '/b' }
]
})
//重定向到命名路由
const router = new VueRouter({
routes: [
{ path: '/a', redirect: { name: 'foo' }}
]
})
//重定向到一個動態(tài)目標
const router = new VueRouter({
routes: [
{ path: '/a', redirect: to => {
// 方法接收 目標路由 作為參數(shù)
// return 重定向的 字符串路徑/路徑對象
}}
]
})
重定向發(fā)生在進入路由之前,所以路由守衛(wèi)并不會被執(zhí)行。
路由別名
/a 的別名是 /b,意味著,當用戶訪問 /b 時,URL 會保持為 /b,但是路由匹配則為 /a,就像用戶訪問 /a 一樣。
上面對應(yīng)的路由配置為
const router = new VueRouter({
routes: [
{ path: '/a', component: A, alias: '/b' }
]
})
給路由組件傳參
通過訪問路由參數(shù)可以獲取當前動態(tài)路由的參數(shù),但是這樣組件跟路由就耦合了,
通過使用 props 將組件和路由解耦:
const User = {
props: ['id'],
template: '<div>User {{ id }}</div>'
}
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User, props: true },
// 對于包含命名視圖的路由,你必須分別為每個命名視圖添加 `props` 選項:
{
path: '/user/:id',
components: { default: User, sidebar: Sidebar },
props: { default: true, sidebar: false }
}
]
})
//靜態(tài)傳參
const router = new VueRouter({
routes: [
{ path: '/promotion/from-newsletter', component: Promotion, props: { newsletterPopup: false } }
]
})
//函數(shù)傳參
//URL /search?q=vue 會將 {query: 'vue'} 作為屬性傳遞給 SearchUser 組件。
const router = new VueRouter({
routes: [
{ path: '/search', component: SearchUser, props: (route) => ({ query: route.query.q }) }
]
})
路由模式
vue-router 默認 hash 模式 —— 使用 URL 的 hash 來模擬一個完整的 URL,于是當 URL 改變時,頁面不會重新加載。
如果不想要很丑的 hash,我們可以用路由的 history 模式,這種模式充分利用 history.pushState API 來完成 URL 跳轉(zhuǎn)而無須重新加載頁面。
const router = new VueRouter({
mode: 'history',
routes: [...]
})
當你使用 history 模式時,URL 就像正常的 url,例如 http://yoursite.com/user/id,也好看!
不過這種模式要玩好,還需要后臺配置支持。因為我們的應(yīng)用是個單頁客戶端應(yīng)用,如果后臺沒有正確的配置,當用戶在瀏覽器直接訪問 http://oursite.com/user/id 就會返回 404,這就不好看了。
所以呢,你要在服務(wù)端增加一個覆蓋所有情況的候選資源:如果 URL 匹配不到任何靜態(tài)資源,則應(yīng)該返回同一個 index.html 頁面,這個頁面就是你 app 依賴的頁面。
導(dǎo)航守衛(wèi)的調(diào)用順序
- 導(dǎo)航被觸發(fā)。
- 在失活的組件里調(diào)用離開守衛(wèi)。
- 調(diào)用全局的 beforeEach 守衛(wèi)。
- 在重用的組件里調(diào)用 beforeRouteUpdate 守衛(wèi) (2.2+)。
- 在路由配置里調(diào)用 beforeEnter。
- 解析異步路由組件。
- 在被激活的組件里調(diào)用 beforeRouteEnter。
- 調(diào)用全局的 beforeResolve 守衛(wèi) (2.5+)。
- 導(dǎo)航被確認。
- 調(diào)用全局的 afterEach 鉤子。
- 觸發(fā) DOM 更新。
- 用創(chuàng)建好的實例調(diào)用 beforeRouteEnter 守衛(wèi)中傳給 next 的回調(diào)函數(shù)。