基本使用
html:
<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 元件來導航. -->
<!-- 通過傳入 `to` 屬性指定連結. -->
<!-- <router-link> 預設會被渲染成一個 `<a>` 標籤 -->
<router-link to="/foo">Go to Foo</router-link>
<router-link to="/bar">Go to Bar</router-link>
</p>
<!-- 路由出口 -->
<!-- 路由匹配到的元件將渲染在這裡 -->
<router-view></router-view>
</div>
JavaScript:
// 0. 如果使用模組化機制程式設計,匯入Vue和VueRouter,要呼叫 Vue.use(VueRouter)
// 1. 定義(路由)元件。
// 可以從其他檔案 import 進來
const Foo = { template: `<div>foo</div>` }
const Bar = { template: `<div>bar</div>` }
// 2. 定義路由
// 每個路由應該對映一個元件。 其中"component" 可以是
// 通過 Vue.extend() 建立的元件構造器,
// 或者,只是一個元件配置物件。
// 我們晚點再討論巢狀路由。
const routes = [
{ path: `/foo`, component: Foo },
{ path: `/bar`, component: Bar }
]
// 3. 建立 router 例項,然後傳 `routes` 配置
// 你還可以傳別的配置引數, 不過先這麼簡單著吧。
const router = new VueRouter({
routes // (縮寫)相當於 routes: routes
})
// 4. 建立和掛載根例項。
// 記得要通過 router 配置引數注入路由,
// 從而讓整個應用都有路由功能
const app = new Vue({
router
}).$mount(`#app`)
// 現在,應用已經啟動了!
通過注入路由,我們可以用 this.$router
來訪問它,就像在任何元件裡用 this.$router
訪問當前路有一樣。
export default {
computed: {
username () {
// 我們很快就會看到 `params` 是什麼
return this.$route.params.username
}
},
methods: {
goBack () {
window.history.length > 1
? this.$router.go(-1)
: this.$router.push(`/`)
}
}
}
路由的命名
const router = new VueRouter({
routes: [
{
path: `/user/:userId`,
name: `user`,
component: User
}
]
})
要連結到一個命名路由,可以給 router-link
的 to
屬性傳一個物件:
<router-link :to="{ name: `user`, params: { userId: 123 }}">User</router-link>
這跟程式碼呼叫 router.push()
是一回事:
router.push({ name: `user`, params: { userId: 123 }})
這兩種方式都會把路由導航到 /user/123
路徑。
重定向和別名
const router = new VueRouter({
routes: [
{ path: `/a`, redirect: `/b` }
]
})
重定向的目標也可以是一個命名的路由:
const router = new VueRouter({
routes: [
{ path: `/a`, redirect: { name: `foo` }}
]
})
甚至是一個方法,動態返回重定向目標:
const router = new VueRouter({
routes: [
{ path: `/a`, redirect: to => {
// 方法接收 目標路由 作為引數
// return 重定向的 字串路徑/路徑物件
}}
]
})
別名
:/a
的別名是 /b
,意味著,當使用者訪問 /b
時,URL 會保持為 /b
,但是路由匹配則為 /a
,就像使用者訪問 /a
一樣。
const router = new VueRouter({
routes: [
{ path: `/a`, component: A, alias: `/b` }
]
})
『別名』的功能讓你可以自由地將 UI 結構對映到任意的 URL,而不是受限於配置的巢狀路由結構。
路由元件傳參props
方式一:
const User = {
template: `<div>User {{ $route.params.id }}</div>`
}
const router = new VueRouter({
routes: [
{ path: `/user/:id`, component: User }
]
})
方式二:
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 }
}
]
})
如果
props
被設定為true
,route.params
將會被設定為元件屬性。
HTML5 History 模式
const router = new VueRouter({
mode: `history`,
routes: [...]
})
404 錯誤頁面配置:
const router = new VueRouter({
mode: `history`,
routes: [
{ path: `*`, component: NotFoundComponent }
]
})
路由元資訊meta
定義路由的時候可以配置 meta
欄位:
const router = new VueRouter({
routes: [
{
path: `/foo`,
component: Foo,
children: [
{
path: `bar`,
component: Bar,
// a meta field
meta: { requiresAuth: true }
}
]
}
]
})
路由過渡動效
<transition>
<router-view></router-view>
</transition>
上面的用法會給所有路由設定一樣的過渡效果,如果你想讓每個路由元件有各自的過渡效果,可以在各路由元件內使用 <transition>
並設定不同的 name
。
const Foo = {
template: `
<transition name="slide">
<div class="foo">...</div>
</transition>
`
}
const Bar = {
template: `
<transition name="fade">
<div class="bar">...</div>
</transition>
`
}
active-class
設定 連結啟用時使用的 CSS 類名。預設值可以通過路由的構造選項 linkActiveClass
來全域性配置。
<router-link class="uilink" :to="{name:`orgList`}" active-class="active">成員</router-link>
巢狀路由
const router = new VueRouter({
routes: [
{ path: `/user/:id`, component: User,
children: [
{
// 當 /user/:id/profile 匹配成功,
// UserProfile 會被渲染在 User 的 <router-view> 中
path: `profile`,
component: UserProfile
},
{
// 當 /user/:id/posts 匹配成功
// UserPosts 會被渲染在 User 的 <router-view> 中
path: `posts`,
component: UserPosts
}
]
}
]
})
要注意,以
/
開頭的巢狀路徑會被當作根路徑
。 這讓你充分的使用巢狀元件而無須設定巢狀的路徑。
程式設計式導航
router.push(…)
// 字串
router.push(`home`)
// 物件
router.push({ path: `home` })
// 命名的路由
router.push({ name: `user`, params: { userId: 123 }})
// 帶查詢引數,變成 /register?plan=private
router.push({ path: `register`, query: { plan: `private` }})
注意:如果提供了
path
,params
會被忽略,上述例子中的query
並不屬於這種情況。取而代之的是下面例子的做法,你需要提供路由的name
或手寫完整的帶有引數的path
:
const userId = 123
router.push({ name: `user`, params: { userId }}) // -> /user/123
router.push({ path: `/user/${userId}` }) // -> /user/123
// 這裡的 params 不生效
router.push({ path: `/user`, params: { userId }}) // -> /user
router.go(n)
// 在瀏覽器記錄中前進一步,等同於 history.forward()
router.go(1)
// 後退一步記錄,等同於 history.back()
router.go(-1)
// 前進 3 步記錄
router.go(3)
// 如果 history 記錄不夠用,那就默默地失敗唄
router.go(-100)
router.go(100)
命名檢視
有時候想同時(同級)展示多個檢視,而不是巢狀展示,例如建立一個佈局,有 sidebar
(側導航) 和 main
(主內容) 兩個檢視,這個時候命名檢視就派上用場了。你可以在介面中擁有多個單獨命名的檢視,而不是隻有一個單獨的出口。如果 router-view
沒有設定名字,那麼預設為 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>
一個檢視使用一個元件渲染,因此對於同個路由,多個檢視就需要多個元件。確保正確使用 components
配置(帶上 s):
const router = new VueRouter({
routes: [
{
path: `/`,
components: {
default: Foo,
a: Bar,
b: Baz
}
}
]
})
導航守衛
全域性守衛
你可以使用 router.beforeEach
註冊一個全域性前置守衛:
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
// ...
})
每個守衛方法接收三個引數:
-
to: Route
: 即將要進入的目標 路由物件 -
from: Route
: 當前導航正要離開的路由 -
next: Function
: 一定要呼叫該方法來 resolve 這個鉤子。執行效果依賴 next 方法的呼叫引數。-
next()
: 進行管道中的下一個鉤子。如果全部鉤子執行完了,則導航的狀態就是 confirmed (確認的)。 -
next(false)
: 中斷當前的導航。如果瀏覽器的 URL 改變了(可能是使用者手動或者瀏覽器後退按鈕),那麼 URL 地址會重置到 from 路由對應的地址。 -
next(`/`)
或者next({ path: `/` })
: 跳轉到一個不同的地址。當前的導航被中斷,然後進行一個新的導航。你可以向 next 傳遞任意位置物件,且允許設定諸如 replace: true、name: `home` 之類的選項以及任何用在router-link
的 to prop 或 router.push 中的選項。 -
next(error)
: (2.4.0+) 如果傳入 next 的引數是一個 Error 例項,則導航會被終止且該錯誤會被傳遞給router.onError()
註冊過的回撥。
-
確保要呼叫 next
方法,否則鉤子就不會被 resolved
。
全域性後置鉤子
你也可以註冊全域性後置鉤子,然而和守衛不同的是,這些鉤子不會接受 next
函式也不會改變導航本身:
router.afterEach((to, from) => {
// ...
})
路由獨享的守衛
你可以在路由配置上直接定義 beforeEnter
守衛:
const router = new VueRouter({
routes: [
{
path: `/foo`,
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})
這些守衛與全域性前置守衛的方法引數是一樣的。
元件內的守衛
const Foo = {
template: `...`,
beforeRouteEnter (to, from, next) {
// 在渲染該元件的對應路由被 confirm 前呼叫
// 不!能!獲取元件例項 `this`
// 因為當守衛執行前,元件例項還沒被建立
},
beforeRouteUpdate (to, from, next) {
// 在當前路由改變,但是該元件被複用時呼叫
// 舉例來說,對於一個帶有動態引數的路徑 /foo/:id,在 /foo/1 和 /foo/2 之間跳轉的時候,
// 由於會渲染同樣的 Foo 元件,因此元件例項會被複用。而這個鉤子就會在這個情況下被呼叫。
// 可以訪問元件例項 `this`
},
beforeRouteLeave (to, from, next) {
// 導航離開該元件的對應路由時呼叫
// 可以訪問元件例項 `this`
}
}
資料獲取
有時候,進入某個路由後,需要從伺服器獲取資料。例如,在渲染使用者資訊時,你需要從伺服器獲取使用者的資料。我們可以通過兩種方式來實現:
- 導航完成之後獲取:先完成導航,然後在接下來的元件生命週期鉤子中獲取資料。在資料獲取期間顯示『載入中』之類的指示。
- 導航完成之前獲取:導航完成前,在路由進入的守衛中獲取資料,在資料獲取成功後執行導航。
導航完成後獲取資料
<template>
<div class="post">
<div class="loading" v-if="loading">
Loading...
</div>
<div v-if="error" class="error">
{{ error }}
</div>
<div v-if="post" class="content">
<h2>{{ post.title }}</h2>
<p>{{ post.body }}</p>
</div>
</div>
</template>
export default {
data () {
return {
loading: false,
post: null,
error: null
}
},
created () {
// 元件建立完後獲取資料,
// 此時 data 已經被 observed 了
this.fetchData()
},
watch: {
// 如果路由有變化,會再次執行該方法
`$route`: `fetchData`
},
methods: {
fetchData () {
this.error = this.post = null
this.loading = true
// replace getPost with your data fetching util / API wrapper
getPost(this.$route.params.id, (err, post) => {
this.loading = false
if (err) {
this.error = err.toString()
} else {
this.post = post
}
})
}
}
}
在導航完成前獲取資料
通過這種方式,我們在導航轉入新的路由前獲取資料。我們可以在接下來的元件的 beforeRouteEnter
守衛中獲取資料,當資料獲取成功後只呼叫 next
方法。
export default {
data () {
return {
post: null,
error: null
}
},
beforeRouteEnter (to, from, next) {
getPost(to.params.id, (err, post) => {
next(vm => vm.setData(err, post))
})
},
// 路由改變前,元件就已經渲染完了
// 邏輯稍稍不同
beforeRouteUpdate (to, from, next) {
this.post = null
getPost(to.params.id, (err, post) => {
this.setData(err, post)
next()
})
},
methods: {
setData (err, post) {
if (err) {
this.error = err.toString()
} else {
this.post = post
}
}
}
}
滾動行為
使用前端路由,當切換到新路由時,想要頁面滾到頂部,或者是保持原先的滾動位置,就像重新載入頁面那樣。 vue-router
能做到,而且更好,它讓你可以自定義路由切換時頁面如何滾動。
注意: 這個功能只在支援 history.pushState
的瀏覽器中可用。
當建立一個 Router 例項,你可以提供一個 scrollBehavior 方法:
const router = new VueRouter({
routes: [...],
scrollBehavior (to, from, savedPosition) {
// return 期望滾動到哪個的位置
}
})
scrollBehavior
方法接收 to 和 from 路由物件。第三個引數 savedPosition
當且僅當 popstate
導航 (通過瀏覽器的 前進/後退 按鈕觸發) 時才可用。
這個方法返回滾動位置的物件資訊,長這樣:
- { x: number, y: number }
- { selector: string, offset? : { x: number, y: number }} (offset 只在 2.6.0+ 支援)
如果返回一個 falsy
(譯者注:falsy 不是 false,參考這裡)的值,或者是一個空物件,那麼不會發生滾動。
舉例:
scrollBehavior (to, from, savedPosition) {
return { x: 0, y: 0 }
}
對於所有路由導航,簡單地讓頁面滾動到頂部。
返回 savedPosition
,在按下 後退/前進 按鈕時,就會像瀏覽器的原生表現那樣:
scrollBehavior (to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
return { x: 0, y: 0 }
}
}
如果你要模擬『滾動到錨點』的行為:
scrollBehavior (to, from, savedPosition) {
if (to.hash) {
return {
selector: to.hash
}
}
}